1
+ # Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2
+ # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3
+ #
4
+ # The Universal Permissive License (UPL), Version 1.0
5
+ #
6
+ # Subject to the condition set forth below, permission is hereby granted to any
7
+ # person obtaining a copy of this software, associated documentation and/or
8
+ # data (collectively the "Software"), free of charge and under any and all
9
+ # copyright rights in the Software, and any and all patent rights owned or
10
+ # freely licensable by each licensor hereunder covering either (i) the
11
+ # unmodified Software as contributed to or provided by such licensor, or (ii)
12
+ # the Larger Works (as defined below), to deal in both
13
+ #
14
+ # (a) the Software, and
15
+ #
16
+ # (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17
+ # one is included with the Software each a "Larger Work" to which the Software
18
+ # is contributed by such licensors),
19
+ #
20
+ # without restriction, including without limitation the rights to copy, create
21
+ # derivative works of, display, perform, and distribute the Software and make,
22
+ # use, sell, offer for sale, import, export, have made, and have sold the
23
+ # Software and the Larger Work(s), and to sublicense the foregoing rights on
24
+ # either these or other terms.
25
+ #
26
+ # This license is subject to the following condition:
27
+ #
28
+ # The above copyright notice and either this complete permission notice or at a
29
+ # minimum a reference to the UPL must be included in all copies or substantial
30
+ # portions of the Software.
31
+ #
32
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
+ # SOFTWARE.
39
+
40
+
41
+ class MyCustomString (str ):
42
+ def __hash__ (self ):
43
+ return super (MyCustomString , self ).__hash__ () | 2
44
+ def __eq__ (self , other ):
45
+ return super (MyCustomString , self ).__eq__ (other )
46
+
47
+
48
+ try :
49
+ set_strategy = __graalpython__ .set_storage_strategy
50
+ FACTORIES = [
51
+ lambda : set_strategy (dict (), 'empty' ),
52
+ lambda : set_strategy (dict (), 'hashmap' ),
53
+ lambda : set_strategy (dict (), 'dynamicobject' ),
54
+ lambda : set_strategy (dict (), 'economicmap' ),
55
+ ]
56
+ except NameError :
57
+ # For CPython, just to verify the test results
58
+ FACTORIES = [lambda : dict ()]
59
+
60
+
61
+ ALL_KEYS = [1 , 1.5 , 'foo' , MyCustomString ()]
62
+ ALL_VALUES = list (range (len (ALL_KEYS )))
63
+
64
+
65
+ # Several interesting combinations of keys to trigger the transitions at different points
66
+ KEYS = [
67
+ ALL_KEYS ,
68
+ ['foo' , MyCustomString (), 1 ],
69
+ [MyCustomString (), 'foo' ],
70
+ ['foo1' , 'foo2' ],
71
+ ]
72
+ VALUES = [list (range (len (k ))) for k in KEYS ]
73
+ KEYS_VALUES = [list (zip (k , v )) for (k , v ) in zip (KEYS , VALUES )]
74
+
75
+
76
+ def assert_raises_keyerror (d , key ):
77
+ raised = False
78
+ try :
79
+ d [key ]
80
+ except KeyError :
81
+ raised = True
82
+ assert raised
83
+
84
+
85
+ def test_add_contains_one_key ():
86
+ keys_values = list (zip (ALL_KEYS , ALL_VALUES ))
87
+ combinations = [(f , kv ) for f in FACTORIES for kv in keys_values ]
88
+ for (f , (k , v )) in combinations :
89
+ d = f ()
90
+ d [k ] = v
91
+ assert d [k ] == v
92
+ for (k2 , v2 ) in keys_values + [(k , v )]:
93
+ # the key we just inserted is set and all other keys are not set
94
+ assert (k2 == k ) == (k2 in d )
95
+ assert d [k ] == v
96
+
97
+
98
+ def test_add_contains_copy ():
99
+ for i in range (len (KEYS )):
100
+ keys = KEYS [i ]
101
+ keys_values = KEYS_VALUES [i ]
102
+ for f in FACTORIES :
103
+ d = f ()
104
+
105
+ # check that it's really empty
106
+ assert len (d ) == 0
107
+ for k in keys :
108
+ assert k not in d
109
+ assert_raises_keyerror (d , k )
110
+
111
+ # inset the items one by one, check that
112
+ expected_len = 0
113
+ for (k , v ) in keys_values :
114
+ d [k ] = v
115
+ expected_len += 1
116
+ assert k in d
117
+ assert d [k ] == v
118
+ assert 2 not in d
119
+ assert_raises_keyerror (d , 2 )
120
+ assert_raises_keyerror (d , 'bar' )
121
+ assert len (d ) == expected_len
122
+
123
+ # insertion order is preserved
124
+ assert keys == [k for k in d ]
125
+ assert keys == list (d .keys ())
126
+ assert keys_values == list (d .items ())
127
+
128
+ cpy = d .copy ()
129
+ cpy [42 ] = 'answer'
130
+ assert_raises_keyerror (d , 42 )
131
+ assert cpy [42 ] == 'answer'
132
+
133
+ d .clear ()
134
+ assert len (d ) == 0
135
+ assert len (cpy ) > 0
136
+ for (k , v ) in keys_values :
137
+ assert cpy [k ] == v
138
+ assert_raises_keyerror (d , k )
139
+
140
+
141
+ def test_pop ():
142
+ for keys_values in KEYS_VALUES :
143
+ for f in FACTORIES :
144
+ d = f ()
145
+ for (k , v ) in keys_values :
146
+ d [k ] = v
147
+ assert d .pop ('bar' , 'default' ) == 'default'
148
+ expected_len = len (d )
149
+ for (k , v ) in keys_values :
150
+ assert d .pop (k ) == v
151
+ expected_len -= 1
152
+ assert expected_len == len (d )
153
+
154
+
155
+ def test_popitem ():
156
+ for keys_values in KEYS_VALUES :
157
+ for f in FACTORIES :
158
+ d = f ()
159
+ for (k , v ) in keys_values :
160
+ d [k ] = v
161
+
162
+ expected_len = len (d )
163
+ reversed_key_values = keys_values
164
+ reversed_key_values .reverse ()
165
+ for (k , v ) in reversed_key_values :
166
+ (actual_k , actual_v ) = d .popitem ()
167
+ assert actual_k == k
168
+ assert actual_v == v
169
+ expected_len -= 1
170
+ assert expected_len == len (d )
171
+
172
+
173
+ def test_delete ():
174
+ for keys_values in KEYS_VALUES :
175
+ for f in FACTORIES :
176
+ for (k , v ) in keys_values :
177
+ d = f ()
178
+ for (k2 , v2 ) in keys_values :
179
+ d [k2 ] = v2
180
+ del d [k ]
181
+ assert_raises_keyerror (d , k )
182
+ for (k2 , v2 ) in keys_values :
183
+ if k2 != k :
184
+ assert d [k2 ] == v2
185
+
186
+
187
+ def test_update ():
188
+ # take all possible combinations of two dict storages, setting one key and value to each,
189
+ # then updating one with the other and then checking the result
190
+ factories2 = [(f1 , f2 ) for f1 in FACTORIES for f2 in FACTORIES ]
191
+ all_keys_vals = list (zip (ALL_KEYS , ALL_VALUES ))
192
+ all_keys_values2 = [(a , b ) for a in all_keys_vals for b in all_keys_vals ]
193
+ combinations = [(f , k ) for f in factories2 for k in all_keys_values2 ]
194
+ for ((f1 , f2 ), ((k1 , v1 ), (k2 , v2 ))) in combinations :
195
+ d1 = f1 ()
196
+ d2 = f2 ()
197
+ d1 [k1 ] = v1
198
+ d2 [k2 ] = v2
199
+ d1 .update (d2 )
200
+ assert k1 in d1
201
+ assert k2 in d2
202
+ if k1 == k2 :
203
+ assert len (d1 ) == 1
204
+ assert d1 [k1 ] == v2
205
+ else :
206
+ assert len (d1 ) == 2
207
+ assert d1 [k1 ] == v1
208
+ assert d1 [k2 ] == v2
209
+
210
+
211
+ log = []
212
+ class LoggingStr (str ):
213
+ def __hash__ (self ):
214
+ log .append ('Hash on %r' % self )
215
+ return 1
216
+ def __eq__ (self , other ):
217
+ log .append ('Eq on %r and %r' % (self , other ))
218
+ return super (LoggingStr , self ).__eq__ (other )
219
+
220
+ def test_side_effects ():
221
+ for (f , key ) in zip (FACTORIES , ['a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' ]):
222
+ log .clear ()
223
+ d = f ()
224
+ d [LoggingStr (key )] = 42
225
+ assert log == ["Hash on '%s'" % key ]
226
+ log .clear ()
227
+ assert d [LoggingStr (key )] == 42
228
+ assert log == [
229
+ "Hash on '%s'" % key ,
230
+ "Eq on '%s' and '%s'" % (key , key )]
231
+ log .clear ()
232
+ assert_raises_keyerror (d , LoggingStr ('foo' ))
233
+ assert log == [
234
+ "Hash on 'foo'" ,
235
+ "Eq on '%s' and 'foo'" % key ]
0 commit comments