blob: 75142649c4cde87d0687280adf3a82c01189ab86 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
2# BitBake Tests for Copy-on-Write (cow.py)
3#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: GPL-2.0-only
5#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05006# Copyright 2006 Holger Freyther <freyther@handhelds.org>
Andrew Geisslerc9f78652020-09-18 14:11:35 -05007# Copyright (C) 2020 Agilent Technologies, Inc.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05008#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05009
Andrew Geisslerc9f78652020-09-18 14:11:35 -050010import io
11import re
12import sys
Patrick Williamsc124f4f2015-09-15 14:41:29 -050013import unittest
Andrew Geisslerc9f78652020-09-18 14:11:35 -050014import contextlib
15import collections
16
17from bb.COW import COWDictBase, COWSetBase, COWDictMeta, COWSetMeta
Andrew Geissler82c905d2020-04-13 13:39:40 -050018
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019
20class COWTestCase(unittest.TestCase):
21 """
22 Test case for the COW module from mithro
23 """
24
Andrew Geisslerc9f78652020-09-18 14:11:35 -050025 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
Patrick Williamsc124f4f2015-09-15 14:41:29 -050076 def testGetSet(self):
77 """
78 Test and set
79 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080 a = COWDictBase.copy()
81
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 self.assertEqual(False, 'a' in a)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050083
84 a['a'] = 'a'
85 a['b'] = 'b'
Patrick Williamsc0f7c042017-02-23 20:41:17 -060086 self.assertEqual(True, 'a' in a)
87 self.assertEqual(True, 'b' in a)
Andrew Geisslerc9f78652020-09-18 14:11:35 -050088 self.assertEqual('a', a['a'])
89 self.assertEqual('b', a['b'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090
91 def testCopyCopy(self):
92 """
93 Test the copy of copies
94 """
95
Patrick Williamsc124f4f2015-09-15 14:41:29 -050096 # create two COW dict 'instances'
97 b = COWDictBase.copy()
98 c = COWDictBase.copy()
99
100 # assign some keys to one instance, some keys to another
101 b['a'] = 10
102 b['c'] = 20
103 c['a'] = 30
104
105 # test separation of the two instances
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600106 self.assertEqual(False, 'c' in c)
107 self.assertEqual(30, c['a'])
108 self.assertEqual(10, b['a'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500109
110 # test copy
111 b_2 = b.copy()
112 c_2 = c.copy()
113
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600114 self.assertEqual(False, 'c' in c_2)
115 self.assertEqual(10, b_2['a'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116
117 b_2['d'] = 40
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600118 self.assertEqual(False, 'd' in c_2)
119 self.assertEqual(True, 'd' in b_2)
120 self.assertEqual(40, b_2['d'])
121 self.assertEqual(False, 'd' in b)
122 self.assertEqual(False, 'd' in c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123
124 c_2['d'] = 30
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600125 self.assertEqual(True, 'd' in c_2)
126 self.assertEqual(True, 'd' in b_2)
127 self.assertEqual(30, c_2['d'])
128 self.assertEqual(40, b_2['d'])
129 self.assertEqual(False, 'd' in b)
130 self.assertEqual(False, 'd' in c)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131
132 # test copy of the copy
133 c_3 = c_2.copy()
134 b_3 = b_2.copy()
135 b_3_2 = b_2.copy()
136
137 c_3['e'] = 4711
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600138 self.assertEqual(4711, c_3['e'])
139 self.assertEqual(False, 'e' in c_2)
140 self.assertEqual(False, 'e' in b_3)
141 self.assertEqual(False, 'e' in b_3_2)
142 self.assertEqual(False, 'e' in b_2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143
144 b_3['e'] = 'viel'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600145 self.assertEqual('viel', b_3['e'])
146 self.assertEqual(4711, c_3['e'])
147 self.assertEqual(False, 'e' in c_2)
148 self.assertEqual(True, 'e' in b_3)
149 self.assertEqual(False, 'e' in b_3_2)
150 self.assertEqual(False, 'e' in b_2)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151
152 def testCow(self):
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500153 self.trackWarnings()
154
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 c = COWDictBase.copy()
156 c['123'] = 1027
157 c['other'] = 4711
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500158 c['d'] = {'abc': 10, 'bcd': 20}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500159
160 copy = c.copy()
161
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600162 self.assertEqual(1027, c['123'])
163 self.assertEqual(4711, c['other'])
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500164 self.assertEqual({'abc': 10, 'bcd': 20}, c['d'])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600165 self.assertEqual(1027, copy['123'])
166 self.assertEqual(4711, copy['other'])
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500167 with self.checkReportsWarning("Warning: Doing a copy because d is a mutable type."):
168 self.assertEqual({'abc': 10, 'bcd': 20}, copy['d'])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169
170 # cow it now
171 copy['123'] = 1028
172 copy['other'] = 4712
173 copy['d']['abc'] = 20
174
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600175 self.assertEqual(1027, c['123'])
176 self.assertEqual(4711, c['other'])
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500177 self.assertEqual({'abc': 10, 'bcd': 20}, c['d'])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 self.assertEqual(1028, copy['123'])
179 self.assertEqual(4712, copy['other'])
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500180 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())