blob: cb79e7434c29d4f17858e00a655bd198b6b9e7e5 [file] [log] [blame]
Victor Stinner3b6a6b42016-09-08 12:51:24 -07001"""
2Test implementation of the PEP 509: dictionary versionning.
3"""
4import unittest
5from test import support
6
7# PEP 509 is implemented in CPython but other Python implementations
8# don't require to implement it
9_testcapi = support.import_module('_testcapi')
10
11
12class DictVersionTests(unittest.TestCase):
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
Inada Naoki91234a12019-06-03 21:30:58 +090083 self.check_version_dont_change(d, d.__setitem__, 'key', value)
Victor Stinner3b6a6b42016-09-08 12:51:24 -070084
85 # setting a key to the same value with dict.update
86 # must change the version
Inada Naoki91234a12019-06-03 21:30:58 +090087 self.check_version_dont_change(d, d.update, key=value)
Victor Stinner3b6a6b42016-09-08 12:51:24 -070088
89 d2 = self.new_dict(key=value)
Inada Naoki91234a12019-06-03 21:30:58 +090090 self.check_version_dont_change(d, d.update, d2)
Victor Stinner3b6a6b42016-09-08 12:51:24 -070091
92 def test_setitem_equal(self):
93 class AlwaysEqual:
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
102 d = self.new_dict()
103 self.check_version_changed(d, d.__setitem__, 'key', value1)
104
105 # setting a key to a value equal to the current value
106 # with dict.__setitem__() must change the version
107 self.check_version_changed(d, d.__setitem__, 'key', value2)
108
109 # setting a key to a value equal to the current value
110 # with dict.update() must change the version
111 self.check_version_changed(d, d.update, key=value1)
112
113 d2 = self.new_dict(key=value2)
114 self.check_version_changed(d, d.update, d2)
115
116 def test_setdefault(self):
117 d = self.new_dict()
118
119 # setting a key with dict.setdefault() must change the version
120 self.check_version_changed(d, d.setdefault, 'key', 'value1')
121
122 # don't change the version if the key already exists
123 self.check_version_dont_change(d, d.setdefault, 'key', 'value2')
124
125 def test_delitem(self):
126 d = self.new_dict(key='value')
127
128 # deleting a key with dict.__delitem__() must change the version
129 self.check_version_changed(d, d.__delitem__, 'key')
130
131 # don't change the version if the key doesn't exist
132 self.check_version_dont_change(d, self.assertRaises, KeyError,
133 d.__delitem__, 'key')
134
135 def test_pop(self):
136 d = self.new_dict(key='value')
137
138 # pop() must change the version if the key exists
139 self.check_version_changed(d, d.pop, 'key')
140
141 # pop() must not change the version if the key does not exist
142 self.check_version_dont_change(d, self.assertRaises, KeyError,
143 d.pop, 'key')
144
145 def test_popitem(self):
146 d = self.new_dict(key='value')
147
148 # popitem() must change the version if the dict is not empty
149 self.check_version_changed(d, d.popitem)
150
151 # popitem() must not change the version if the dict is empty
152 self.check_version_dont_change(d, self.assertRaises, KeyError,
153 d.popitem)
154
155 def test_update(self):
156 d = self.new_dict(key='value')
157
158 # update() calling with no argument must not change the version
159 self.check_version_dont_change(d, d.update)
160
161 # update() must change the version
162 self.check_version_changed(d, d.update, key='new value')
163
164 d2 = self.new_dict(key='value 3')
165 self.check_version_changed(d, d.update, d2)
166
167 def test_clear(self):
168 d = self.new_dict(key='value')
169
170 # clear() must change the version if the dict is not empty
171 self.check_version_changed(d, d.clear)
172
173 # clear() must not change the version if the dict is empty
174 self.check_version_dont_change(d, d.clear)
175
176
177class Dict(dict):
178 pass
179
180
181class DictSubtypeVersionTests(DictVersionTests):
182 type2test = Dict
183
184
185if __name__ == "__main__":
186 unittest.main()