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