1 """
2 Test implementation of the PEP 509: dictionary versioning.
3 """
4 import unittest
5 from test.support import import_helper
6
7 # PEP 509 is implemented in CPython but other Python implementations
8 # don't require to implement it
9 _testcapi = import_helper.import_module('_testcapi')
10
11
12 class ESC[4;38;5;81mDictVersionTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
13 type2test = dict
14
15 def setUp(self):
16 self.seen_versions = set()
17 self.dict = None
18
19 def check_version_unique(self, mydict):
20 version = _testcapi.dict_get_version(mydict)
21 self.assertNotIn(version, self.seen_versions)
22 self.seen_versions.add(version)
23
24 def check_version_changed(self, mydict, method, *args, **kw):
25 result = method(*args, **kw)
26 self.check_version_unique(mydict)
27 return result
28
29 def check_version_dont_change(self, mydict, method, *args, **kw):
30 version1 = _testcapi.dict_get_version(mydict)
31 self.seen_versions.add(version1)
32
33 result = method(*args, **kw)
34
35 version2 = _testcapi.dict_get_version(mydict)
36 self.assertEqual(version2, version1, "version changed")
37
38 return result
39
40 def new_dict(self, *args, **kw):
41 d = self.type2test(*args, **kw)
42 self.check_version_unique(d)
43 return d
44
45 def test_constructor(self):
46 # new empty dictionaries must all have an unique version
47 empty1 = self.new_dict()
48 empty2 = self.new_dict()
49 empty3 = self.new_dict()
50
51 # non-empty dictionaries must also have an unique version
52 nonempty1 = self.new_dict(x='x')
53 nonempty2 = self.new_dict(x='x', y='y')
54
55 def test_copy(self):
56 d = self.new_dict(a=1, b=2)
57
58 d2 = self.check_version_dont_change(d, d.copy)
59
60 # dict.copy() must create a dictionary with a new unique version
61 self.check_version_unique(d2)
62
63 def test_setitem(self):
64 d = self.new_dict()
65
66 # creating new keys must change the version
67 self.check_version_changed(d, d.__setitem__, 'x', 'x')
68 self.check_version_changed(d, d.__setitem__, 'y', 'y')
69
70 # changing values must change the version
71 self.check_version_changed(d, d.__setitem__, 'x', 1)
72 self.check_version_changed(d, d.__setitem__, 'y', 2)
73
74 def test_setitem_same_value(self):
75 value = object()
76 d = self.new_dict()
77
78 # setting a key must change the version
79 self.check_version_changed(d, d.__setitem__, 'key', value)
80
81 # setting a key to the same value with dict.__setitem__
82 # must change the version
83 self.check_version_dont_change(d, d.__setitem__, 'key', value)
84
85 # setting a key to the same value with dict.update
86 # must change the version
87 self.check_version_dont_change(d, d.update, key=value)
88
89 d2 = self.new_dict(key=value)
90 self.check_version_dont_change(d, d.update, d2)
91
92 def test_setitem_equal(self):
93 class ESC[4;38;5;81mAlwaysEqual:
94 def __eq__(self, other):
95 return True
96
97 value1 = AlwaysEqual()
98 value2 = AlwaysEqual()
99 self.assertTrue(value1 == value2)
100 self.assertFalse(value1 != value2)
101 self.assertIsNot(value1, value2)
102
103 d = self.new_dict()
104 self.check_version_changed(d, d.__setitem__, 'key', value1)
105 self.assertIs(d['key'], value1)
106
107 # setting a key to a value equal to the current value
108 # with dict.__setitem__() must change the version
109 self.check_version_changed(d, d.__setitem__, 'key', value2)
110 self.assertIs(d['key'], value2)
111
112 # setting a key to a value equal to the current value
113 # with dict.update() must change the version
114 self.check_version_changed(d, d.update, key=value1)
115 self.assertIs(d['key'], value1)
116
117 d2 = self.new_dict(key=value2)
118 self.check_version_changed(d, d.update, d2)
119 self.assertIs(d['key'], value2)
120
121 def test_setdefault(self):
122 d = self.new_dict()
123
124 # setting a key with dict.setdefault() must change the version
125 self.check_version_changed(d, d.setdefault, 'key', 'value1')
126
127 # don't change the version if the key already exists
128 self.check_version_dont_change(d, d.setdefault, 'key', 'value2')
129
130 def test_delitem(self):
131 d = self.new_dict(key='value')
132
133 # deleting a key with dict.__delitem__() must change the version
134 self.check_version_changed(d, d.__delitem__, 'key')
135
136 # don't change the version if the key doesn't exist
137 self.check_version_dont_change(d, self.assertRaises, KeyError,
138 d.__delitem__, 'key')
139
140 def test_pop(self):
141 d = self.new_dict(key='value')
142
143 # pop() must change the version if the key exists
144 self.check_version_changed(d, d.pop, 'key')
145
146 # pop() must not change the version if the key does not exist
147 self.check_version_dont_change(d, self.assertRaises, KeyError,
148 d.pop, 'key')
149
150 def test_popitem(self):
151 d = self.new_dict(key='value')
152
153 # popitem() must change the version if the dict is not empty
154 self.check_version_changed(d, d.popitem)
155
156 # popitem() must not change the version if the dict is empty
157 self.check_version_dont_change(d, self.assertRaises, KeyError,
158 d.popitem)
159
160 def test_update(self):
161 d = self.new_dict(key='value')
162
163 # update() calling with no argument must not change the version
164 self.check_version_dont_change(d, d.update)
165
166 # update() must change the version
167 self.check_version_changed(d, d.update, key='new value')
168
169 d2 = self.new_dict(key='value 3')
170 self.check_version_changed(d, d.update, d2)
171
172 def test_clear(self):
173 d = self.new_dict(key='value')
174
175 # clear() must change the version if the dict is not empty
176 self.check_version_changed(d, d.clear)
177
178 # clear() must not change the version if the dict is empty
179 self.check_version_dont_change(d, d.clear)
180
181
182 class ESC[4;38;5;81mDict(ESC[4;38;5;149mdict):
183 pass
184
185
186 class ESC[4;38;5;81mDictSubtypeVersionTests(ESC[4;38;5;149mDictVersionTests):
187 type2test = Dict
188
189
190 if __name__ == "__main__":
191 unittest.main()