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