diff options
Diffstat (limited to 'bitbake/lib')
-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()) | ||