diff options
| -rw-r--r-- | bitbake/lib/bb/COW.py | 122 | ||||
| -rw-r--r-- | bitbake/lib/bb/tests/cow.py | 218 |
2 files changed, 206 insertions, 134 deletions
diff --git a/bitbake/lib/bb/COW.py b/bitbake/lib/bb/COW.py index 3785a8b03e..23c22b65ef 100644 --- a/bitbake/lib/bb/COW.py +++ b/bitbake/lib/bb/COW.py | |||
| @@ -193,125 +193,3 @@ class COWDictBase(metaclass=COWDictMeta): | |||
| 193 | 193 | ||
| 194 | class COWSetBase(metaclass=COWSetMeta): | 194 | class COWSetBase(metaclass=COWSetMeta): |
| 195 | __count__ = 0 | 195 | __count__ = 0 |
| 196 | |||
| 197 | if __name__ == "__main__": | ||
| 198 | import sys | ||
| 199 | COWDictBase.__warn__ = sys.stderr | ||
| 200 | a = COWDictBase() | ||
| 201 | print("a", a) | ||
| 202 | |||
| 203 | a['a'] = 'a' | ||
| 204 | a['b'] = 'b' | ||
| 205 | a['dict'] = {} | ||
| 206 | |||
| 207 | b = a.copy() | ||
| 208 | print("b", b) | ||
| 209 | b['c'] = 'b' | ||
| 210 | |||
| 211 | print() | ||
| 212 | |||
| 213 | print("a", a) | ||
| 214 | for x in a.iteritems(): | ||
| 215 | print(x) | ||
| 216 | print("--") | ||
| 217 | print("b", b) | ||
| 218 | for x in b.iteritems(): | ||
| 219 | print(x) | ||
| 220 | print() | ||
| 221 | |||
| 222 | b['dict']['a'] = 'b' | ||
| 223 | b['a'] = 'c' | ||
| 224 | |||
| 225 | print("a", a) | ||
| 226 | for x in a.iteritems(): | ||
| 227 | print(x) | ||
| 228 | print("--") | ||
| 229 | print("b", b) | ||
| 230 | for x in b.iteritems(): | ||
| 231 | print(x) | ||
| 232 | print() | ||
| 233 | |||
| 234 | try: | ||
| 235 | b['dict2'] | ||
| 236 | except KeyError as e: | ||
| 237 | print("Okay!") | ||
| 238 | |||
| 239 | a['set'] = COWSetBase() | ||
| 240 | a['set'].add("o1") | ||
| 241 | a['set'].add("o1") | ||
| 242 | a['set'].add("o2") | ||
| 243 | |||
| 244 | print("a", a) | ||
| 245 | for x in a['set'].itervalues(): | ||
| 246 | print(x) | ||
| 247 | print("--") | ||
| 248 | print("b", b) | ||
| 249 | for x in b['set'].itervalues(): | ||
| 250 | print(x) | ||
| 251 | print() | ||
| 252 | |||
| 253 | b['set'].add('o3') | ||
| 254 | |||
| 255 | print("a", a) | ||
| 256 | for x in a['set'].itervalues(): | ||
| 257 | print(x) | ||
| 258 | print("--") | ||
| 259 | print("b", b) | ||
| 260 | for x in b['set'].itervalues(): | ||
| 261 | print(x) | ||
| 262 | print() | ||
| 263 | |||
| 264 | a['set2'] = set() | ||
| 265 | a['set2'].add("o1") | ||
| 266 | a['set2'].add("o1") | ||
| 267 | a['set2'].add("o2") | ||
| 268 | |||
| 269 | print("a", a) | ||
| 270 | for x in a.iteritems(): | ||
| 271 | print(x) | ||
| 272 | print("--") | ||
| 273 | print("b", b) | ||
| 274 | for x in b.iteritems(readonly=True): | ||
| 275 | print(x) | ||
| 276 | print() | ||
| 277 | |||
| 278 | del b['b'] | ||
| 279 | try: | ||
| 280 | print(b['b']) | ||
| 281 | except KeyError: | ||
| 282 | print("Yay! deleted key raises error") | ||
| 283 | |||
| 284 | if 'b' in b: | ||
| 285 | print("Boo!") | ||
| 286 | else: | ||
| 287 | print("Yay - has_key with delete works!") | ||
| 288 | |||
| 289 | print("a", a) | ||
| 290 | for x in a.iteritems(): | ||
| 291 | print(x) | ||
| 292 | print("--") | ||
| 293 | print("b", b) | ||
| 294 | for x in b.iteritems(readonly=True): | ||
| 295 | print(x) | ||
| 296 | print() | ||
| 297 | |||
| 298 | b.__revertitem__('b') | ||
| 299 | |||
| 300 | print("a", a) | ||
| 301 | for x in a.iteritems(): | ||
| 302 | print(x) | ||
| 303 | print("--") | ||
| 304 | print("b", b) | ||
| 305 | for x in b.iteritems(readonly=True): | ||
| 306 | print(x) | ||
| 307 | print() | ||
| 308 | |||
| 309 | b.__revertitem__('dict') | ||
| 310 | print("a", a) | ||
| 311 | for x in a.iteritems(): | ||
| 312 | print(x) | ||
| 313 | print("--") | ||
| 314 | print("b", b) | ||
| 315 | for x in b.iteritems(readonly=True): | ||
| 316 | print(x) | ||
| 317 | print() | ||
diff --git a/bitbake/lib/bb/tests/cow.py b/bitbake/lib/bb/tests/cow.py index bf6e79fcee..75142649c4 100644 --- a/bitbake/lib/bb/tests/cow.py +++ b/bitbake/lib/bb/tests/cow.py | |||
| @@ -4,9 +4,17 @@ | |||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | 4 | # SPDX-License-Identifier: GPL-2.0-only |
| 5 | # | 5 | # |
| 6 | # Copyright 2006 Holger Freyther <freyther@handhelds.org> | 6 | # Copyright 2006 Holger Freyther <freyther@handhelds.org> |
| 7 | # Copyright (C) 2020 Agilent Technologies, Inc. | ||
| 7 | # | 8 | # |
| 8 | 9 | ||
| 10 | import io | ||
| 11 | import re | ||
| 12 | import sys | ||
| 9 | import unittest | 13 | import unittest |
| 14 | import contextlib | ||
| 15 | import collections | ||
| 16 | |||
| 17 | from bb.COW import COWDictBase, COWSetBase, COWDictMeta, COWSetMeta | ||
| 10 | 18 | ||
| 11 | 19 | ||
| 12 | class COWTestCase(unittest.TestCase): | 20 | class COWTestCase(unittest.TestCase): |
| @@ -14,11 +22,61 @@ class COWTestCase(unittest.TestCase): | |||
| 14 | Test case for the COW module from mithro | 22 | Test case for the COW module from mithro |
| 15 | """ | 23 | """ |
| 16 | 24 | ||
| 25 | def setUp(self): | ||
| 26 | self._track_warnings = False | ||
| 27 | self._warning_file = io.StringIO() | ||
| 28 | self._unhandled_warnings = collections.deque() | ||
| 29 | COWDictBase.__warn__ = self._warning_file | ||
| 30 | |||
| 31 | def tearDown(self): | ||
| 32 | COWDictBase.__warn__ = sys.stderr | ||
| 33 | if self._track_warnings: | ||
| 34 | self._checkAllWarningsRead() | ||
| 35 | |||
| 36 | def trackWarnings(self): | ||
| 37 | self._track_warnings = True | ||
| 38 | |||
| 39 | def _collectWarnings(self): | ||
| 40 | self._warning_file.seek(0) | ||
| 41 | for warning in self._warning_file: | ||
| 42 | self._unhandled_warnings.append(warning.rstrip("\n")) | ||
| 43 | self._warning_file.truncate(0) | ||
| 44 | self._warning_file.seek(0) | ||
| 45 | |||
| 46 | def _checkAllWarningsRead(self): | ||
| 47 | self._collectWarnings() | ||
| 48 | self.assertSequenceEqual(self._unhandled_warnings, []) | ||
| 49 | |||
| 50 | @contextlib.contextmanager | ||
| 51 | def checkReportsWarning(self, expected_warning): | ||
| 52 | self._checkAllWarningsRead() | ||
| 53 | yield | ||
| 54 | self._collectWarnings() | ||
| 55 | warning = self._unhandled_warnings.popleft() | ||
| 56 | self.assertEqual(warning, expected_warning) | ||
| 57 | |||
| 58 | def checkStrOutput(self, obj, expected_levels, expected_keys): | ||
| 59 | if obj.__class__ is COWDictMeta: | ||
| 60 | expected_class_name = "COWDict" | ||
| 61 | elif obj.__class__ is COWSetMeta: | ||
| 62 | expected_class_name = "COWSet" | ||
| 63 | else: | ||
| 64 | self.fail("obj is of unknown type {0}".format(type(obj))) | ||
| 65 | s = str(obj) | ||
| 66 | regex = re.compile(r"<(\w+) Level: (\d+) Current Keys: (\d+)>") | ||
| 67 | match = regex.match(s) | ||
| 68 | self.assertIsNotNone(match, "bad str output: '{0}'".format(s)) | ||
| 69 | class_name = match.group(1) | ||
| 70 | self.assertEqual(class_name, expected_class_name) | ||
| 71 | levels = int(match.group(2)) | ||
| 72 | self.assertEqual(levels, expected_levels, "wrong # levels in str: '{0}'".format(s)) | ||
| 73 | keys = int(match.group(3)) | ||
| 74 | self.assertEqual(keys, expected_keys, "wrong # keys in str: '{0}'".format(s)) | ||
| 75 | |||
| 17 | def testGetSet(self): | 76 | def testGetSet(self): |
| 18 | """ | 77 | """ |
| 19 | Test and set | 78 | Test and set |
| 20 | """ | 79 | """ |
| 21 | from bb.COW import COWDictBase | ||
| 22 | a = COWDictBase.copy() | 80 | a = COWDictBase.copy() |
| 23 | 81 | ||
| 24 | self.assertEqual(False, 'a' in a) | 82 | self.assertEqual(False, 'a' in a) |
| @@ -27,16 +85,14 @@ class COWTestCase(unittest.TestCase): | |||
| 27 | a['b'] = 'b' | 85 | a['b'] = 'b' |
| 28 | self.assertEqual(True, 'a' in a) | 86 | self.assertEqual(True, 'a' in a) |
| 29 | self.assertEqual(True, 'b' in a) | 87 | self.assertEqual(True, 'b' in a) |
| 30 | self.assertEqual('a', a['a'] ) | 88 | self.assertEqual('a', a['a']) |
| 31 | self.assertEqual('b', a['b'] ) | 89 | self.assertEqual('b', a['b']) |
| 32 | 90 | ||
| 33 | def testCopyCopy(self): | 91 | def testCopyCopy(self): |
| 34 | """ | 92 | """ |
| 35 | Test the copy of copies | 93 | Test the copy of copies |
| 36 | """ | 94 | """ |
| 37 | 95 | ||
| 38 | from bb.COW import COWDictBase | ||
| 39 | |||
| 40 | # create two COW dict 'instances' | 96 | # create two COW dict 'instances' |
| 41 | b = COWDictBase.copy() | 97 | b = COWDictBase.copy() |
| 42 | c = COWDictBase.copy() | 98 | c = COWDictBase.copy() |
| @@ -94,30 +150,168 @@ class COWTestCase(unittest.TestCase): | |||
| 94 | self.assertEqual(False, 'e' in b_2) | 150 | self.assertEqual(False, 'e' in b_2) |
| 95 | 151 | ||
| 96 | def testCow(self): | 152 | def testCow(self): |
| 97 | from bb.COW import COWDictBase | 153 | self.trackWarnings() |
| 154 | |||
| 98 | c = COWDictBase.copy() | 155 | c = COWDictBase.copy() |
| 99 | c['123'] = 1027 | 156 | c['123'] = 1027 |
| 100 | c['other'] = 4711 | 157 | c['other'] = 4711 |
| 101 | c['d'] = { 'abc' : 10, 'bcd' : 20 } | 158 | c['d'] = {'abc': 10, 'bcd': 20} |
| 102 | 159 | ||
| 103 | copy = c.copy() | 160 | copy = c.copy() |
| 104 | 161 | ||
| 105 | self.assertEqual(1027, c['123']) | 162 | self.assertEqual(1027, c['123']) |
| 106 | self.assertEqual(4711, c['other']) | 163 | self.assertEqual(4711, c['other']) |
| 107 | self.assertEqual({'abc':10, 'bcd':20}, c['d']) | 164 | self.assertEqual({'abc': 10, 'bcd': 20}, c['d']) |
| 108 | self.assertEqual(1027, copy['123']) | 165 | self.assertEqual(1027, copy['123']) |
| 109 | self.assertEqual(4711, copy['other']) | 166 | self.assertEqual(4711, copy['other']) |
| 110 | self.assertEqual({'abc':10, 'bcd':20}, copy['d']) | 167 | with self.checkReportsWarning("Warning: Doing a copy because d is a mutable type."): |
| 168 | self.assertEqual({'abc': 10, 'bcd': 20}, copy['d']) | ||
| 111 | 169 | ||
| 112 | # cow it now | 170 | # cow it now |
| 113 | copy['123'] = 1028 | 171 | copy['123'] = 1028 |
| 114 | copy['other'] = 4712 | 172 | copy['other'] = 4712 |
| 115 | copy['d']['abc'] = 20 | 173 | copy['d']['abc'] = 20 |
| 116 | 174 | ||
| 117 | |||
| 118 | self.assertEqual(1027, c['123']) | 175 | self.assertEqual(1027, c['123']) |
| 119 | self.assertEqual(4711, c['other']) | 176 | self.assertEqual(4711, c['other']) |
| 120 | self.assertEqual({'abc':10, 'bcd':20}, c['d']) | 177 | self.assertEqual({'abc': 10, 'bcd': 20}, c['d']) |
| 121 | self.assertEqual(1028, copy['123']) | 178 | self.assertEqual(1028, copy['123']) |
| 122 | self.assertEqual(4712, copy['other']) | 179 | self.assertEqual(4712, copy['other']) |
| 123 | self.assertEqual({'abc':20, 'bcd':20}, copy['d']) | 180 | self.assertEqual({'abc': 20, 'bcd': 20}, copy['d']) |
| 181 | |||
| 182 | def testOriginalTestSuite(self): | ||
| 183 | # This test suite is a port of the original one from COW.py | ||
| 184 | self.trackWarnings() | ||
| 185 | |||
| 186 | a = COWDictBase.copy() | ||
| 187 | self.checkStrOutput(a, 1, 0) | ||
| 188 | |||
| 189 | a['a'] = 'a' | ||
| 190 | a['b'] = 'b' | ||
| 191 | a['dict'] = {} | ||
| 192 | self.checkStrOutput(a, 1, 4) # 4th member is dict__mutable__ | ||
| 193 | |||
| 194 | b = a.copy() | ||
| 195 | self.checkStrOutput(b, 2, 0) | ||
| 196 | b['c'] = 'b' | ||
| 197 | self.checkStrOutput(b, 2, 1) | ||
| 198 | |||
| 199 | with self.checkReportsWarning("Warning: If you aren't going to change any of the values call with True."): | ||
| 200 | self.assertListEqual(list(a.iteritems()), | ||
| 201 | [('a', 'a'), | ||
| 202 | ('b', 'b'), | ||
| 203 | ('dict', {}) | ||
| 204 | ]) | ||
| 205 | |||
| 206 | with self.checkReportsWarning("Warning: If you aren't going to change any of the values call with True."): | ||
| 207 | b_gen = b.iteritems() | ||
| 208 | self.assertTupleEqual(next(b_gen), ('a', 'a')) | ||
| 209 | self.assertTupleEqual(next(b_gen), ('b', 'b')) | ||
| 210 | self.assertTupleEqual(next(b_gen), ('c', 'b')) | ||
| 211 | with self.checkReportsWarning("Warning: Doing a copy because dict is a mutable type."): | ||
| 212 | self.assertTupleEqual(next(b_gen), ('dict', {})) | ||
| 213 | with self.assertRaises(StopIteration): | ||
| 214 | next(b_gen) | ||
| 215 | |||
| 216 | b['dict']['a'] = 'b' | ||
| 217 | b['a'] = 'c' | ||
| 218 | |||
| 219 | self.checkStrOutput(a, 1, 4) | ||
| 220 | self.checkStrOutput(b, 2, 3) | ||
| 221 | |||
| 222 | with self.checkReportsWarning("Warning: If you aren't going to change any of the values call with True."): | ||
| 223 | self.assertListEqual(list(a.iteritems()), | ||
| 224 | [('a', 'a'), | ||
| 225 | ('b', 'b'), | ||
| 226 | ('dict', {}) | ||
| 227 | ]) | ||
| 228 | |||
| 229 | with self.checkReportsWarning("Warning: If you aren't going to change any of the values call with True."): | ||
| 230 | b_gen = b.iteritems() | ||
| 231 | self.assertTupleEqual(next(b_gen), ('a', 'c')) | ||
| 232 | self.assertTupleEqual(next(b_gen), ('b', 'b')) | ||
| 233 | self.assertTupleEqual(next(b_gen), ('c', 'b')) | ||
| 234 | self.assertTupleEqual(next(b_gen), ('dict', {'a': 'b'})) | ||
| 235 | with self.assertRaises(StopIteration): | ||
| 236 | next(b_gen) | ||
| 237 | |||
| 238 | with self.assertRaises(KeyError): | ||
| 239 | print(b["dict2"]) | ||
| 240 | |||
| 241 | a['set'] = COWSetBase() | ||
| 242 | a['set'].add("o1") | ||
| 243 | a['set'].add("o1") | ||
| 244 | a['set'].add("o2") | ||
| 245 | self.assertSetEqual(set(a['set'].itervalues()), {"o1", "o2"}) | ||
| 246 | self.assertSetEqual(set(b['set'].itervalues()), {"o1", "o2"}) | ||
| 247 | |||
| 248 | b['set'].add('o3') | ||
| 249 | self.assertSetEqual(set(a['set'].itervalues()), {"o1", "o2"}) | ||
| 250 | self.assertSetEqual(set(b['set'].itervalues()), {"o1", "o2", "o3"}) | ||
| 251 | |||
| 252 | a['set2'] = set() | ||
| 253 | a['set2'].add("o1") | ||
| 254 | a['set2'].add("o1") | ||
| 255 | a['set2'].add("o2") | ||
| 256 | |||
| 257 | # We don't expect 'a' to change anymore | ||
| 258 | def check_a(): | ||
| 259 | with self.checkReportsWarning("Warning: If you aren't going to change any of the values call with True."): | ||
| 260 | a_gen = a.iteritems() | ||
| 261 | self.assertTupleEqual(next(a_gen), ('a', 'a')) | ||
| 262 | self.assertTupleEqual(next(a_gen), ('b', 'b')) | ||
| 263 | self.assertTupleEqual(next(a_gen), ('dict', {})) | ||
| 264 | self.assertTupleEqual(next(a_gen), ('set2', {'o1', 'o2'})) | ||
| 265 | a_sub_set = next(a_gen) | ||
| 266 | self.assertEqual(a_sub_set[0], 'set') | ||
| 267 | self.checkStrOutput(a_sub_set[1], 1, 2) | ||
| 268 | self.assertSetEqual(set(a_sub_set[1].itervalues()), {'o1', 'o2'}) | ||
| 269 | |||
| 270 | check_a() | ||
| 271 | |||
| 272 | b_gen = b.iteritems(readonly=True) | ||
| 273 | self.assertTupleEqual(next(b_gen), ('a', 'c')) | ||
| 274 | self.assertTupleEqual(next(b_gen), ('b', 'b')) | ||
| 275 | self.assertTupleEqual(next(b_gen), ('c', 'b')) | ||
| 276 | self.assertTupleEqual(next(b_gen), ('dict', {'a': 'b'})) | ||
| 277 | self.assertTupleEqual(next(b_gen), ('set2', {'o1', 'o2'})) | ||
| 278 | b_sub_set = next(b_gen) | ||
| 279 | self.assertEqual(b_sub_set[0], 'set') | ||
| 280 | self.checkStrOutput(b_sub_set[1], 2, 1) | ||
| 281 | self.assertSetEqual(set(b_sub_set[1].itervalues()), {'o1', 'o2', 'o3'}) | ||
| 282 | |||
| 283 | del b['b'] | ||
| 284 | with self.assertRaises(KeyError): | ||
| 285 | print(b['b']) | ||
| 286 | self.assertFalse('b' in b) | ||
| 287 | |||
| 288 | check_a() | ||
| 289 | |||
| 290 | b.__revertitem__('b') | ||
| 291 | check_a() | ||
| 292 | self.assertEqual(b['b'], 'b') | ||
| 293 | self.assertTrue('b' in b) | ||
| 294 | |||
| 295 | b.__revertitem__('dict') | ||
| 296 | check_a() | ||
| 297 | |||
| 298 | b_gen = b.iteritems(readonly=True) | ||
| 299 | self.assertTupleEqual(next(b_gen), ('a', 'c')) | ||
| 300 | self.assertTupleEqual(next(b_gen), ('b', 'b')) | ||
| 301 | self.assertTupleEqual(next(b_gen), ('c', 'b')) | ||
| 302 | self.assertTupleEqual(next(b_gen), ('dict', {})) | ||
| 303 | self.assertTupleEqual(next(b_gen), ('set2', {'o1', 'o2'})) | ||
| 304 | b_sub_set = next(b_gen) | ||
| 305 | self.assertEqual(b_sub_set[0], 'set') | ||
| 306 | self.checkStrOutput(b_sub_set[1], 2, 1) | ||
| 307 | self.assertSetEqual(set(b_sub_set[1].itervalues()), {'o1', 'o2', 'o3'}) | ||
| 308 | |||
| 309 | self.checkStrOutput(a, 1, 6) | ||
| 310 | self.checkStrOutput(b, 2, 3) | ||
| 311 | |||
| 312 | def testSetMethods(self): | ||
| 313 | s = COWSetBase() | ||
| 314 | with self.assertRaises(TypeError): | ||
| 315 | print(s.iteritems()) | ||
| 316 | with self.assertRaises(TypeError): | ||
| 317 | print(s.iterkeys()) | ||
