xref: /openbmc/openbmc/poky/bitbake/lib/bb/tests/data.py (revision da295319)
1#
2# BitBake Tests for the Data Store (data.py/data_smart.py)
3#
4# Copyright (C) 2010 Chris Larson
5# Copyright (C) 2012 Richard Purdie
6#
7# SPDX-License-Identifier: GPL-2.0-only
8#
9
10import unittest
11import bb
12import bb.data
13import bb.parse
14import logging
15import os
16
17class LogRecord():
18    def __enter__(self):
19        logs = []
20        class LogHandler(logging.Handler):
21            def emit(self, record):
22                logs.append(record)
23        logger = logging.getLogger("BitBake")
24        handler = LogHandler()
25        self.handler = handler
26        logger.addHandler(handler)
27        return logs
28    def __exit__(self, type, value, traceback):
29        logger = logging.getLogger("BitBake")
30        logger.removeHandler(self.handler)
31        return
32
33def logContains(item, logs):
34    for l in logs:
35        m = l.getMessage()
36        if item in m:
37            return True
38    return False
39
40class DataExpansions(unittest.TestCase):
41    def setUp(self):
42        self.d = bb.data.init()
43        self.d["foo"] = "value_of_foo"
44        self.d["bar"] = "value_of_bar"
45        self.d["value_of_foo"] = "value_of_'value_of_foo'"
46
47    def test_one_var(self):
48        val = self.d.expand("${foo}")
49        self.assertEqual(str(val), "value_of_foo")
50
51    def test_indirect_one_var(self):
52        val = self.d.expand("${${foo}}")
53        self.assertEqual(str(val), "value_of_'value_of_foo'")
54
55    def test_indirect_and_another(self):
56        val = self.d.expand("${${foo}} ${bar}")
57        self.assertEqual(str(val), "value_of_'value_of_foo' value_of_bar")
58
59    def test_python_snippet(self):
60        val = self.d.expand("${@5*12}")
61        self.assertEqual(str(val), "60")
62
63    def test_python_snippet_w_dict(self):
64        val = self.d.expand("${@{ 'green': 1, 'blue': 2 }['green']}")
65        self.assertEqual(str(val), "1")
66
67    def test_python_unexpanded_multi(self):
68        self.d.setVar("bar", "${unsetvar}")
69        val = self.d.expand("${@2*2},${foo},${@d.getVar('foo') + ' ${bar}'},${foo}")
70        self.assertEqual(str(val), "4,value_of_foo,${@d.getVar('foo') + ' ${unsetvar}'},value_of_foo")
71
72    def test_expand_in_python_snippet(self):
73        val = self.d.expand("${@'boo ' + '${foo}'}")
74        self.assertEqual(str(val), "boo value_of_foo")
75
76    def test_python_snippet_getvar(self):
77        val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
78        self.assertEqual(str(val), "value_of_foo value_of_bar")
79
80    def test_python_snippet_function_reference(self):
81        self.d.setVar("TESTVAL", "testvalue")
82        self.d.setVar("testfunc", 'd.getVar("TESTVAL")')
83        context = bb.utils.get_context()
84        context["testfunc"] = lambda d: d.getVar("TESTVAL")
85        val = self.d.expand("${@testfunc(d)}")
86        self.assertEqual(str(val), "testvalue")
87
88    def test_python_snippet_builtin_metadata(self):
89        self.d.setVar("eval", "INVALID")
90        self.d.expand("${@eval('3')}")
91
92    def test_python_unexpanded(self):
93        self.d.setVar("bar", "${unsetvar}")
94        val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
95        self.assertEqual(str(val), "${@d.getVar('foo') + ' ${unsetvar}'}")
96
97    def test_python_snippet_syntax_error(self):
98        self.d.setVar("FOO", "${@foo = 5}")
99        self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
100
101    def test_python_snippet_runtime_error(self):
102        self.d.setVar("FOO", "${@int('test')}")
103        self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
104
105    def test_python_snippet_error_path(self):
106        self.d.setVar("FOO", "foo value ${BAR}")
107        self.d.setVar("BAR", "bar value ${@int('test')}")
108        self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
109
110    def test_value_containing_value(self):
111        val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
112        self.assertEqual(str(val), "value_of_foo value_of_bar")
113
114    def test_reference_undefined_var(self):
115        val = self.d.expand("${undefinedvar} meh")
116        self.assertEqual(str(val), "${undefinedvar} meh")
117
118    def test_double_reference(self):
119        self.d.setVar("BAR", "bar value")
120        self.d.setVar("FOO", "${BAR} foo ${BAR}")
121        val = self.d.getVar("FOO")
122        self.assertEqual(str(val), "bar value foo bar value")
123
124    def test_direct_recursion(self):
125        self.d.setVar("FOO", "${FOO}")
126        self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
127
128    def test_indirect_recursion(self):
129        self.d.setVar("FOO", "${BAR}")
130        self.d.setVar("BAR", "${BAZ}")
131        self.d.setVar("BAZ", "${FOO}")
132        self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
133
134    def test_recursion_exception(self):
135        self.d.setVar("FOO", "${BAR}")
136        self.d.setVar("BAR", "${${@'FOO'}}")
137        self.assertRaises(bb.data_smart.ExpansionError, self.d.getVar, "FOO", True)
138
139    def test_incomplete_varexp_single_quotes(self):
140        self.d.setVar("FOO", "sed -i -e 's:IP{:I${:g' $pc")
141        val = self.d.getVar("FOO")
142        self.assertEqual(str(val), "sed -i -e 's:IP{:I${:g' $pc")
143
144    def test_nonstring(self):
145        self.d.setVar("TEST", 5)
146        val = self.d.getVar("TEST")
147        self.assertEqual(str(val), "5")
148
149    def test_rename(self):
150        self.d.renameVar("foo", "newfoo")
151        self.assertEqual(self.d.getVar("newfoo", False), "value_of_foo")
152        self.assertEqual(self.d.getVar("foo", False), None)
153
154    def test_deletion(self):
155        self.d.delVar("foo")
156        self.assertEqual(self.d.getVar("foo", False), None)
157
158    def test_keys(self):
159        keys = list(self.d.keys())
160        self.assertCountEqual(keys, ['value_of_foo', 'foo', 'bar'])
161
162    def test_keys_deletion(self):
163        newd = bb.data.createCopy(self.d)
164        newd.delVar("bar")
165        keys = list(newd.keys())
166        self.assertCountEqual(keys, ['value_of_foo', 'foo'])
167
168class TestNestedExpansions(unittest.TestCase):
169    def setUp(self):
170        self.d = bb.data.init()
171        self.d["foo"] = "foo"
172        self.d["bar"] = "bar"
173        self.d["value_of_foobar"] = "187"
174
175    def test_refs(self):
176        val = self.d.expand("${value_of_${foo}${bar}}")
177        self.assertEqual(str(val), "187")
178
179    #def test_python_refs(self):
180    #    val = self.d.expand("${@${@3}**2 + ${@4}**2}")
181    #    self.assertEqual(str(val), "25")
182
183    def test_ref_in_python_ref(self):
184        val = self.d.expand("${@'${foo}' + 'bar'}")
185        self.assertEqual(str(val), "foobar")
186
187    def test_python_ref_in_ref(self):
188        val = self.d.expand("${${@'f'+'o'+'o'}}")
189        self.assertEqual(str(val), "foo")
190
191    def test_deep_nesting(self):
192        depth = 100
193        val = self.d.expand("${" * depth + "foo" + "}" * depth)
194        self.assertEqual(str(val), "foo")
195
196    #def test_deep_python_nesting(self):
197    #    depth = 50
198    #    val = self.d.expand("${@" * depth + "1" + "+1}" * depth)
199    #    self.assertEqual(str(val), str(depth + 1))
200
201    def test_mixed(self):
202        val = self.d.expand("${value_of_${@('${foo}'+'bar')[0:3]}${${@'BAR'.lower()}}}")
203        self.assertEqual(str(val), "187")
204
205    def test_runtime(self):
206        val = self.d.expand("${${@'value_of' + '_f'+'o'+'o'+'b'+'a'+'r'}}")
207        self.assertEqual(str(val), "187")
208
209class TestMemoize(unittest.TestCase):
210    def test_memoized(self):
211        d = bb.data.init()
212        d.setVar("FOO", "bar")
213        self.assertTrue(d.getVar("FOO", False) is d.getVar("FOO", False))
214
215    def test_not_memoized(self):
216        d1 = bb.data.init()
217        d2 = bb.data.init()
218        d1.setVar("FOO", "bar")
219        d2.setVar("FOO", "bar2")
220        self.assertTrue(d1.getVar("FOO", False) is not d2.getVar("FOO", False))
221
222    def test_changed_after_memoized(self):
223        d = bb.data.init()
224        d.setVar("foo", "value of foo")
225        self.assertEqual(str(d.getVar("foo", False)), "value of foo")
226        d.setVar("foo", "second value of foo")
227        self.assertEqual(str(d.getVar("foo", False)), "second value of foo")
228
229    def test_same_value(self):
230        d = bb.data.init()
231        d.setVar("foo", "value of")
232        d.setVar("bar", "value of")
233        self.assertEqual(d.getVar("foo", False),
234                         d.getVar("bar", False))
235
236class TestConcat(unittest.TestCase):
237    def setUp(self):
238        self.d = bb.data.init()
239        self.d.setVar("FOO", "foo")
240        self.d.setVar("VAL", "val")
241        self.d.setVar("BAR", "bar")
242
243    def test_prepend(self):
244        self.d.setVar("TEST", "${VAL}")
245        self.d.prependVar("TEST", "${FOO}:")
246        self.assertEqual(self.d.getVar("TEST"), "foo:val")
247
248    def test_append(self):
249        self.d.setVar("TEST", "${VAL}")
250        self.d.appendVar("TEST", ":${BAR}")
251        self.assertEqual(self.d.getVar("TEST"), "val:bar")
252
253    def test_multiple_append(self):
254        self.d.setVar("TEST", "${VAL}")
255        self.d.prependVar("TEST", "${FOO}:")
256        self.d.appendVar("TEST", ":val2")
257        self.d.appendVar("TEST", ":${BAR}")
258        self.assertEqual(self.d.getVar("TEST"), "foo:val:val2:bar")
259
260class TestConcatOverride(unittest.TestCase):
261    def setUp(self):
262        self.d = bb.data.init()
263        self.d.setVar("FOO", "foo")
264        self.d.setVar("VAL", "val")
265        self.d.setVar("BAR", "bar")
266
267    def test_prepend(self):
268        self.d.setVar("TEST", "${VAL}")
269        self.d.setVar("TEST:prepend", "${FOO}:")
270        self.assertEqual(self.d.getVar("TEST"), "foo:val")
271
272    def test_append(self):
273        self.d.setVar("TEST", "${VAL}")
274        self.d.setVar("TEST:append", ":${BAR}")
275        self.assertEqual(self.d.getVar("TEST"), "val:bar")
276
277    def test_multiple_append(self):
278        self.d.setVar("TEST", "${VAL}")
279        self.d.setVar("TEST:prepend", "${FOO}:")
280        self.d.setVar("TEST:append", ":val2")
281        self.d.setVar("TEST:append", ":${BAR}")
282        self.assertEqual(self.d.getVar("TEST"), "foo:val:val2:bar")
283
284    def test_append_unset(self):
285        self.d.setVar("TEST:prepend", "${FOO}:")
286        self.d.setVar("TEST:append", ":val2")
287        self.d.setVar("TEST:append", ":${BAR}")
288        self.assertEqual(self.d.getVar("TEST"), "foo::val2:bar")
289
290    def test_remove(self):
291        self.d.setVar("TEST", "${VAL} ${BAR}")
292        self.d.setVar("TEST:remove", "val")
293        self.assertEqual(self.d.getVar("TEST"), " bar")
294
295    def test_remove_cleared(self):
296        self.d.setVar("TEST", "${VAL} ${BAR}")
297        self.d.setVar("TEST:remove", "val")
298        self.d.setVar("TEST", "${VAL} ${BAR}")
299        self.assertEqual(self.d.getVar("TEST"), "val bar")
300
301    # Ensure the value is unchanged if we have an inactive remove override
302    # (including that whitespace is preserved)
303    def test_remove_inactive_override(self):
304        self.d.setVar("TEST", "${VAL} ${BAR}    123")
305        self.d.setVar("TEST:remove:inactiveoverride", "val")
306        self.assertEqual(self.d.getVar("TEST"), "val bar    123")
307
308    def test_doubleref_remove(self):
309        self.d.setVar("TEST", "${VAL} ${BAR}")
310        self.d.setVar("TEST:remove", "val")
311        self.d.setVar("TEST_TEST", "${TEST} ${TEST}")
312        self.assertEqual(self.d.getVar("TEST_TEST"), " bar  bar")
313
314    def test_empty_remove(self):
315        self.d.setVar("TEST", "")
316        self.d.setVar("TEST:remove", "val")
317        self.assertEqual(self.d.getVar("TEST"), "")
318
319    def test_remove_expansion(self):
320        self.d.setVar("BAR", "Z")
321        self.d.setVar("TEST", "${BAR}/X Y")
322        self.d.setVar("TEST:remove", "${BAR}/X")
323        self.assertEqual(self.d.getVar("TEST"), " Y")
324
325    def test_remove_expansion_items(self):
326        self.d.setVar("TEST", "A B C D")
327        self.d.setVar("BAR", "B D")
328        self.d.setVar("TEST:remove", "${BAR}")
329        self.assertEqual(self.d.getVar("TEST"), "A  C ")
330
331    def test_remove_preserve_whitespace(self):
332        # When the removal isn't active, the original value should be preserved
333        self.d.setVar("TEST", " A B")
334        self.d.setVar("TEST:remove", "C")
335        self.assertEqual(self.d.getVar("TEST"), " A B")
336
337    def test_remove_preserve_whitespace2(self):
338        # When the removal is active preserve the whitespace
339        self.d.setVar("TEST", " A B")
340        self.d.setVar("TEST:remove", "B")
341        self.assertEqual(self.d.getVar("TEST"), " A ")
342
343class TestOverrides(unittest.TestCase):
344    def setUp(self):
345        self.d = bb.data.init()
346        self.d.setVar("OVERRIDES", "foo:bar:local")
347        self.d.setVar("TEST", "testvalue")
348
349    def test_no_override(self):
350        self.assertEqual(self.d.getVar("TEST"), "testvalue")
351
352    def test_one_override(self):
353        self.d.setVar("TEST:bar", "testvalue2")
354        self.assertEqual(self.d.getVar("TEST"), "testvalue2")
355
356    def test_one_override_unset(self):
357        self.d.setVar("TEST2:bar", "testvalue2")
358
359        self.assertEqual(self.d.getVar("TEST2"), "testvalue2")
360        self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2:bar'])
361
362    def test_multiple_override(self):
363        self.d.setVar("TEST:bar", "testvalue2")
364        self.d.setVar("TEST:local", "testvalue3")
365        self.d.setVar("TEST:foo", "testvalue4")
366        self.assertEqual(self.d.getVar("TEST"), "testvalue3")
367        self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST:foo', 'OVERRIDES', 'TEST:bar', 'TEST:local'])
368
369    def test_multiple_combined_overrides(self):
370        self.d.setVar("TEST:local:foo:bar", "testvalue3")
371        self.assertEqual(self.d.getVar("TEST"), "testvalue3")
372
373    def test_multiple_overrides_unset(self):
374        self.d.setVar("TEST2:local:foo:bar", "testvalue3")
375        self.assertEqual(self.d.getVar("TEST2"), "testvalue3")
376
377    def test_keyexpansion_override(self):
378        self.d.setVar("LOCAL", "local")
379        self.d.setVar("TEST:bar", "testvalue2")
380        self.d.setVar("TEST:${LOCAL}", "testvalue3")
381        self.d.setVar("TEST:foo", "testvalue4")
382        bb.data.expandKeys(self.d)
383        self.assertEqual(self.d.getVar("TEST"), "testvalue3")
384
385    def test_rename_override(self):
386        self.d.setVar("ALTERNATIVE:ncurses-tools:class-target", "a")
387        self.d.setVar("OVERRIDES", "class-target")
388        self.d.renameVar("ALTERNATIVE:ncurses-tools", "ALTERNATIVE:lib32-ncurses-tools")
389        self.assertEqual(self.d.getVar("ALTERNATIVE:lib32-ncurses-tools"), "a")
390
391    def test_underscore_override(self):
392        self.d.setVar("TEST:bar", "testvalue2")
393        self.d.setVar("TEST:some_val", "testvalue3")
394        self.d.setVar("TEST:foo", "testvalue4")
395        self.d.setVar("OVERRIDES", "foo:bar:some_val")
396        self.assertEqual(self.d.getVar("TEST"), "testvalue3")
397
398    # Test an override with _<numeric> in it based on a real world OE issue
399    def test_underscore_override_2(self):
400        self.d.setVar("TARGET_ARCH", "x86_64")
401        self.d.setVar("PN", "test-${TARGET_ARCH}")
402        self.d.setVar("VERSION", "1")
403        self.d.setVar("VERSION:pn-test-${TARGET_ARCH}", "2")
404        self.d.setVar("OVERRIDES", "pn-${PN}")
405        bb.data.expandKeys(self.d)
406        self.assertEqual(self.d.getVar("VERSION"), "2")
407
408    def test_remove_with_override(self):
409        self.d.setVar("TEST:bar", "testvalue2")
410        self.d.setVar("TEST:some_val", "testvalue3 testvalue5")
411        self.d.setVar("TEST:some_val:remove", "testvalue3")
412        self.d.setVar("TEST:foo", "testvalue4")
413        self.d.setVar("OVERRIDES", "foo:bar:some_val")
414        self.assertEqual(self.d.getVar("TEST"), " testvalue5")
415
416    def test_append_and_override_1(self):
417        self.d.setVar("TEST:append", "testvalue2")
418        self.d.setVar("TEST:bar", "testvalue3")
419        self.assertEqual(self.d.getVar("TEST"), "testvalue3testvalue2")
420
421    def test_append_and_override_2(self):
422        self.d.setVar("TEST:append:bar", "testvalue2")
423        self.assertEqual(self.d.getVar("TEST"), "testvaluetestvalue2")
424
425    def test_append_and_override_3(self):
426        self.d.setVar("TEST:bar:append", "testvalue2")
427        self.assertEqual(self.d.getVar("TEST"), "testvalue2")
428
429    def test_append_and_unused_override(self):
430        # Had a bug where an unused override append could return "" instead of None
431        self.d.setVar("BAR:append:unusedoverride", "testvalue2")
432        self.assertEqual(self.d.getVar("BAR"), None)
433
434class TestKeyExpansion(unittest.TestCase):
435    def setUp(self):
436        self.d = bb.data.init()
437        self.d.setVar("FOO", "foo")
438        self.d.setVar("BAR", "foo")
439
440    def test_keyexpand(self):
441        self.d.setVar("VAL_${FOO}", "A")
442        self.d.setVar("VAL_${BAR}", "B")
443        with LogRecord() as logs:
444            bb.data.expandKeys(self.d)
445            self.assertTrue(logContains("Variable key VAL_${FOO} (A) replaces original key VAL_foo (B)", logs))
446        self.assertEqual(self.d.getVar("VAL_foo"), "A")
447
448class TestFlags(unittest.TestCase):
449    def setUp(self):
450        self.d = bb.data.init()
451        self.d.setVar("foo", "value of foo")
452        self.d.setVarFlag("foo", "flag1", "value of flag1")
453        self.d.setVarFlag("foo", "flag2", "value of flag2")
454
455    def test_setflag(self):
456        self.assertEqual(self.d.getVarFlag("foo", "flag1", False), "value of flag1")
457        self.assertEqual(self.d.getVarFlag("foo", "flag2", False), "value of flag2")
458
459    def test_delflag(self):
460        self.d.delVarFlag("foo", "flag2")
461        self.assertEqual(self.d.getVarFlag("foo", "flag1", False), "value of flag1")
462        self.assertEqual(self.d.getVarFlag("foo", "flag2", False), None)
463
464
465class Contains(unittest.TestCase):
466    def setUp(self):
467        self.d = bb.data.init()
468        self.d.setVar("SOMEFLAG", "a b c")
469
470    def test_contains(self):
471        self.assertTrue(bb.utils.contains("SOMEFLAG", "a", True, False, self.d))
472        self.assertTrue(bb.utils.contains("SOMEFLAG", "b", True, False, self.d))
473        self.assertTrue(bb.utils.contains("SOMEFLAG", "c", True, False, self.d))
474
475        self.assertTrue(bb.utils.contains("SOMEFLAG", "a b", True, False, self.d))
476        self.assertTrue(bb.utils.contains("SOMEFLAG", "b c", True, False, self.d))
477        self.assertTrue(bb.utils.contains("SOMEFLAG", "c a", True, False, self.d))
478
479        self.assertTrue(bb.utils.contains("SOMEFLAG", "a b c", True, False, self.d))
480        self.assertTrue(bb.utils.contains("SOMEFLAG", "c b a", True, False, self.d))
481
482        self.assertFalse(bb.utils.contains("SOMEFLAG", "x", True, False, self.d))
483        self.assertFalse(bb.utils.contains("SOMEFLAG", "a x", True, False, self.d))
484        self.assertFalse(bb.utils.contains("SOMEFLAG", "x c b", True, False, self.d))
485        self.assertFalse(bb.utils.contains("SOMEFLAG", "x c b a", True, False, self.d))
486
487    def test_contains_any(self):
488        self.assertTrue(bb.utils.contains_any("SOMEFLAG", "a", True, False, self.d))
489        self.assertTrue(bb.utils.contains_any("SOMEFLAG", "b", True, False, self.d))
490        self.assertTrue(bb.utils.contains_any("SOMEFLAG", "c", True, False, self.d))
491
492        self.assertTrue(bb.utils.contains_any("SOMEFLAG", "a b", True, False, self.d))
493        self.assertTrue(bb.utils.contains_any("SOMEFLAG", "b c", True, False, self.d))
494        self.assertTrue(bb.utils.contains_any("SOMEFLAG", "c a", True, False, self.d))
495
496        self.assertTrue(bb.utils.contains_any("SOMEFLAG", "a x", True, False, self.d))
497        self.assertTrue(bb.utils.contains_any("SOMEFLAG", "x c", True, False, self.d))
498
499        self.assertFalse(bb.utils.contains_any("SOMEFLAG", "x", True, False, self.d))
500        self.assertFalse(bb.utils.contains_any("SOMEFLAG", "x y z", True, False, self.d))
501
502
503class TaskHash(unittest.TestCase):
504    def test_taskhashes(self):
505        def gettask_bashhash(taskname, d):
506            tasklist, gendeps, lookupcache = bb.data.generate_dependencies(d, set())
507            taskdeps, basehash = bb.data.generate_dependency_hash(tasklist, gendeps, lookupcache, set(), "somefile")
508            bb.warn(str(lookupcache))
509            return basehash["somefile:" + taskname]
510
511        d = bb.data.init()
512        d.setVar("__BBTASKS", ["mytask"])
513        d.setVar("__exportlist", [])
514        d.setVar("mytask", "${MYCOMMAND}")
515        d.setVar("MYCOMMAND", "${VAR}; foo; bar; exit 0")
516        d.setVar("VAR", "val")
517        orighash = gettask_bashhash("mytask", d)
518
519        # Changing a variable should change the hash
520        d.setVar("VAR", "val2")
521        nexthash = gettask_bashhash("mytask", d)
522        self.assertNotEqual(orighash, nexthash)
523
524        d.setVar("VAR", "val")
525        # Adding an inactive removal shouldn't change the hash
526        d.setVar("BAR", "notbar")
527        d.setVar("MYCOMMAND:remove", "${BAR}")
528        nexthash = gettask_bashhash("mytask", d)
529        self.assertEqual(orighash, nexthash)
530
531        # Adding an active removal should change the hash
532        d.setVar("BAR", "bar;")
533        nexthash = gettask_bashhash("mytask", d)
534        self.assertNotEqual(orighash, nexthash)
535
536        # Setup an inactive contains()
537        d.setVar("VAR", "${@bb.utils.contains('VAR2', 'A', 'val', '', d)}")
538        orighash = gettask_bashhash("mytask", d)
539
540        # Activate the contains() and the hash should change
541        d.setVar("VAR2", "A")
542        nexthash = gettask_bashhash("mytask", d)
543        self.assertNotEqual(orighash, nexthash)
544
545        # The contains should be inactive but even though VAR2 has a
546        # different value the hash should match the original
547        d.setVar("VAR2", "B")
548        nexthash = gettask_bashhash("mytask", d)
549        self.assertEqual(orighash, nexthash)
550
551class Serialize(unittest.TestCase):
552
553    def test_serialize(self):
554        import tempfile
555        import pickle
556        d = bb.data.init()
557        d.enableTracking()
558        d.setVar('HELLO', 'world')
559        d.setVarFlag('HELLO', 'other', 'planet')
560        with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
561            tmpfilename = tmpfile.name
562            pickle.dump(d, tmpfile)
563
564        with open(tmpfilename, 'rb') as f:
565            newd = pickle.load(f)
566
567        os.remove(tmpfilename)
568
569        self.assertEqual(d, newd)
570        self.assertEqual(newd.getVar('HELLO'), 'world')
571        self.assertEqual(newd.getVarFlag('HELLO', 'other'), 'planet')
572
573
574