this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "Python.h"
3#include "gtest/gtest.h"
4
5#include "capi-fixture.h"
6#include "capi-testing.h"
7
8namespace py {
9namespace testing {
10
11using DictExtensionApiTest = ExtensionApi;
12
13TEST_F(DictExtensionApiTest, GetItemFromNonDictReturnsNull) {
14 // Pass a non dictionary
15 PyObject* result = PyDict_GetItem(Py_None, Py_None);
16 EXPECT_EQ(result, nullptr);
17 EXPECT_EQ(PyErr_Occurred(), nullptr);
18}
19
20TEST_F(DictExtensionApiTest, GetItemNonExistingKeyReturnsNull) {
21 PyObjectPtr dict(PyDict_New());
22 PyObjectPtr nonkey(PyLong_FromLong(10));
23
24 // Pass a non existing key
25 PyObject* result = PyDict_GetItem(dict, nonkey);
26 EXPECT_EQ(result, nullptr);
27 EXPECT_EQ(PyErr_Occurred(), nullptr);
28}
29
30TEST_F(DictExtensionApiTest, GetItemReturnsBorrowedValue) {
31 PyObject* dict = PyDict_New();
32 PyObject* key = PyLong_FromLong(10);
33 PyObject* value = PyLong_FromLong(0);
34
35 // Insert the value into the dictionary
36 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
37
38 // Record the reference count of the value
39 long refcnt = Py_REFCNT(value);
40
41 // Get a new reference to the value from the dictionary
42 PyObject* value2 = PyDict_GetItem(dict, key);
43
44 // The new reference should be equal to the original reference
45 EXPECT_EQ(value2, value);
46
47 // The reference count should not be affected
48 EXPECT_EQ(Py_REFCNT(value), refcnt);
49
50 Py_DECREF(value);
51 Py_DECREF(key);
52 Py_DECREF(dict);
53}
54
55TEST_F(DictExtensionApiTest, GetItemWithDictSubclassReturnsValue) {
56 PyRun_SimpleString(R"(
57class Foo(dict): pass
58obj = Foo()
59 )");
60
61 PyObjectPtr obj(mainModuleGet("obj"));
62 PyObjectPtr key(PyLong_FromLong(1));
63 PyObjectPtr val(PyLong_FromLong(2));
64 ASSERT_EQ(PyDict_SetItem(obj, key, val), 0);
65 ASSERT_EQ(PyErr_Occurred(), nullptr);
66
67 PyObject* result = PyDict_GetItem(obj, key);
68 EXPECT_EQ(result, val);
69 EXPECT_EQ(PyErr_Occurred(), nullptr);
70}
71
72TEST_F(DictExtensionApiTest, GetItemWithBigHashTruncatesHash) {
73 PyRun_SimpleString(R"(
74class C:
75 def __init__(self, v):
76 self.v = v
77 def __hash__(self):
78 return 1180591620717411303424
79 def __eq__(self, other):
80 return self.v == other.v
81c1 = C(4)
82c2 = C(5)
83 )");
84
85 PyObjectPtr c1(mainModuleGet("c1"));
86 PyObjectPtr c2(mainModuleGet("c2"));
87 PyObjectPtr v1(PyLong_FromLong(1));
88 PyObjectPtr v2(PyLong_FromLong(2));
89 PyObjectPtr dict(PyDict_New());
90 ASSERT_EQ(PyDict_SetItem(dict, c1, v1), 0);
91 ASSERT_EQ(PyErr_Occurred(), nullptr);
92
93 ASSERT_EQ(PyDict_SetItem(dict, c2, v2), 0);
94 ASSERT_EQ(PyErr_Occurred(), nullptr);
95
96 PyObject* result = PyDict_GetItem(dict, c1);
97 EXPECT_EQ(PyErr_Occurred(), nullptr);
98 EXPECT_EQ(result, v1);
99}
100
101TEST_F(DictExtensionApiTest, GetItemWithIntSubclassHashUsesInt) {
102 PyRun_SimpleString(R"(
103class H(int):
104 pass
105class C:
106 def __init__(self, v):
107 self.v = v
108 def __hash__(self):
109 return H(42)
110 def __eq__(self, other):
111 return self.v == other.v
112c = C(4)
113)");
114
115 PyObjectPtr c(mainModuleGet("c"));
116 PyObjectPtr v(PyLong_FromLong(1));
117 PyObjectPtr dict(PyDict_New());
118 ASSERT_EQ(PyDict_SetItem(dict, c, v), 0);
119 ASSERT_EQ(PyErr_Occurred(), nullptr);
120
121 PyObject* result = PyDict_GetItem(dict, c);
122 EXPECT_EQ(PyErr_Occurred(), nullptr);
123 EXPECT_EQ(result, v);
124}
125
126TEST_F(DictExtensionApiTest, GetItemWithSameIdentityReturnsObject) {
127 ASSERT_EQ(PyRun_SimpleString(R"(
128called_dunder_eq = False
129class C:
130 def __eq__(self, other):
131 global called_dunder_eq
132 called_dunder_eq = True
133 def __hash__(self):
134 return 5
135c = C()
136d = {}
137d[c] = 4
138)"),
139 0);
140 PyObjectPtr c(mainModuleGet("c"));
141 PyObjectPtr dict(mainModuleGet("d"));
142 PyObject* result = PyDict_GetItem(dict, c);
143 ASSERT_EQ(PyErr_Occurred(), nullptr);
144 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
145 ASSERT_EQ(called_dunder_eq, Py_False);
146 EXPECT_EQ(PyLong_AsLong(result), 4);
147}
148
149TEST_F(DictExtensionApiTest, GetItemWithDifferentHashReturnsNull) {
150 ASSERT_EQ(PyRun_SimpleString(R"(
151called_dunder_eq = False
152class C:
153 def __init__(self, h):
154 self.h = h
155 def __eq__(self, other):
156 global called_dunder_eq
157 called_dunder_eq = True
158 return True
159 def __hash__(self):
160 return self.h
161
162c = C(1)
163d = {}
164d[C(2)] = 2
165)"),
166 0);
167 PyObjectPtr c(mainModuleGet("c"));
168 PyObjectPtr dict(mainModuleGet("d"));
169 PyObject* result = PyDict_GetItem(dict, c);
170 ASSERT_EQ(PyErr_Occurred(), nullptr);
171 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
172 ASSERT_EQ(called_dunder_eq, Py_False);
173 EXPECT_EQ(result, nullptr);
174}
175
176TEST_F(DictExtensionApiTest, GetItemWithDunderEqReturnsObject) {
177 ASSERT_EQ(PyRun_SimpleString(R"(
178called_dunder_eq = False
179class C:
180 def __eq__(self, other):
181 global called_dunder_eq
182 called_dunder_eq = True
183 return True
184 def __hash__(self):
185 return 5
186
187d = {}
188c = C()
189d[C()] = 4
190)"),
191 0);
192 PyObjectPtr c(mainModuleGet("c"));
193 PyObjectPtr dict(mainModuleGet("d"));
194 PyObject* result = PyDict_GetItem(dict, c);
195 ASSERT_EQ(PyErr_Occurred(), nullptr);
196 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
197 ASSERT_EQ(called_dunder_eq, Py_True);
198 EXPECT_EQ(PyLong_AsLong(result), 4);
199}
200
201TEST_F(DictExtensionApiTest, GetItemWithFalseDunderEqReturnsNull) {
202 ASSERT_EQ(PyRun_SimpleString(R"(
203called_dunder_eq = False
204class C:
205 def __eq__(self, other):
206 global called_dunder_eq
207 called_dunder_eq = True
208 return False
209 def __hash__(self):
210 return 5
211
212c = C()
213d = {}
214d[C()] = 4
215)"),
216 0);
217 PyObjectPtr c(mainModuleGet("c"));
218 PyObjectPtr dict(mainModuleGet("d"));
219 PyObject* result = PyDict_GetItem(dict, c);
220 ASSERT_EQ(PyErr_Occurred(), nullptr);
221 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
222 ASSERT_EQ(called_dunder_eq, Py_True);
223 EXPECT_EQ(result, nullptr);
224}
225
226TEST_F(DictExtensionApiTest,
227 GetItemWithExceptionDunderEqSwallowsAndReturnsNull) {
228 ASSERT_EQ(PyRun_SimpleString(R"(
229called_dunder_eq = False
230class C:
231 def __eq__(self, other):
232 global called_dunder_eq
233 called_dunder_eq = True
234 raise ValueError('foo')
235 def __hash__(self):
236 return 5
237
238c = C()
239d = {}
240d[C()] = 4
241)"),
242 0);
243 PyObjectPtr c(mainModuleGet("c"));
244 PyObjectPtr dict(mainModuleGet("d"));
245 PyObject* result = PyDict_GetItem(dict, c);
246 ASSERT_EQ(PyErr_Occurred(), nullptr);
247 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
248 ASSERT_EQ(called_dunder_eq, Py_True);
249 EXPECT_EQ(result, nullptr);
250}
251
252TEST_F(DictExtensionApiTest, GetItemWithNotImplementedDunderEqReturnsNull) {
253 ASSERT_EQ(PyRun_SimpleString(R"(
254called_dunder_eq = False
255class C:
256 def __eq__(self, other):
257 global called_dunder_eq
258 called_dunder_eq = True
259 return NotImplemented
260 def __hash__(self):
261 return 5
262
263c = C()
264d = {}
265d[C()] = 4
266)"),
267 0);
268 PyObjectPtr c(mainModuleGet("c"));
269 PyObjectPtr dict(mainModuleGet("d"));
270 PyObject* result = PyDict_GetItem(dict, c);
271 ASSERT_EQ(PyErr_Occurred(), nullptr);
272 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
273 ASSERT_EQ(called_dunder_eq, Py_True);
274 EXPECT_EQ(result, nullptr);
275}
276
277TEST_F(DictExtensionApiTest,
278 GetItemCallsExistingKeyDunderEqAndThenLookedKeyDunderEq) {
279 ASSERT_EQ(PyRun_SimpleString(R"(
280seq_num = 0
281
282def new_seq_num():
283 global seq_num
284 seq_num += 1
285 return seq_num
286
287c_eq = 0
288c_hash = 0
289
290class C:
291 def __eq__(self, other):
292 global c_eq
293 c_eq = new_seq_num()
294 return NotImplemented
295
296 def __hash__(self):
297 global c_hash
298 c_hash = new_seq_num()
299 return 5
300
301c = C()
302
303d_eq = 0
304d_hash = 0
305
306class D:
307 def __eq__(self, other):
308 global d_eq
309 d_eq = new_seq_num()
310 return True
311
312 def __hash__(self):
313 global d_hash
314 d_hash = new_seq_num()
315 return 5
316
317d = D()
318)"),
319 0);
320 PyObjectPtr dict(PyDict_New());
321 PyObjectPtr c(mainModuleGet("c"));
322 PyObjectPtr value(PyLong_FromLong(500));
323
324 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
325 ASSERT_EQ(PyErr_Occurred(), nullptr);
326 ASSERT_EQ(PyDict_Size(dict), 1);
327 PyObjectPtr c_eq(mainModuleGet("c_eq"));
328 ASSERT_EQ(PyLong_AsLong(c_eq), 0);
329 PyObjectPtr c_hash(mainModuleGet("c_hash"));
330 ASSERT_EQ(PyLong_AsLong(c_hash), 1);
331
332 PyObjectPtr d(mainModuleGet("d"));
333 PyObject* result = PyDict_GetItem(dict, d);
334 ASSERT_EQ(PyErr_Occurred(), nullptr);
335 ASSERT_EQ(PyLong_AsLong(result), 500);
336 c_hash = mainModuleGet("c_hash");
337 EXPECT_EQ(PyLong_AsLong(c_hash), 1);
338 PyObjectPtr d_hash(mainModuleGet("d_hash"));
339 EXPECT_EQ(PyLong_AsLong(d_hash), 2);
340 c_eq = mainModuleGet("c_eq");
341 EXPECT_EQ(PyLong_AsLong(c_eq), 3);
342 PyObjectPtr d_eq(mainModuleGet("d_eq"));
343 EXPECT_EQ(PyLong_AsLong(d_eq), 4);
344}
345
346TEST_F(DictExtensionApiTest, GetItemComparesHashValueFirst) {
347 ASSERT_EQ(PyRun_SimpleString(R"(
348class C:
349 def __init__(self, hash_code):
350 self.hash_code = hash_code
351
352 def __eq__(self, other):
353 raise UserWarning("unexpected")
354
355 def __hash__(self):
356 return self.hash_code
357
358c = C(4)
359d = C(5)
360)"),
361 0);
362 PyObjectPtr dict(PyDict_New());
363 PyObjectPtr c(mainModuleGet("c"));
364 PyObjectPtr value(PyLong_FromLong(500));
365
366 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
367 ASSERT_EQ(PyErr_Occurred(), nullptr);
368 ASSERT_EQ(PyDict_Size(dict), 1);
369
370 PyObjectPtr d(mainModuleGet("d"));
371 ASSERT_EQ(PyDict_GetItem(dict, d), nullptr);
372 ASSERT_EQ(PyErr_Occurred(), nullptr);
373}
374
375TEST_F(DictExtensionApiTest, GetItemKnownHashFromNonDictRaisesSystemError) {
376 // Pass a non dictionary
377 PyObject* result = _PyDict_GetItem_KnownHash(Py_None, Py_None, 0);
378 EXPECT_EQ(result, nullptr);
379 ASSERT_NE(PyErr_Occurred(), nullptr);
380 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
381}
382
383TEST_F(DictExtensionApiTest, GetItemKnownHashNonExistingKeyReturnsNull) {
384 PyObjectPtr dict(PyDict_New());
385 PyObjectPtr nonkey(PyLong_FromLong(11));
386
387 // Pass a non existing key
388 PyObject* result = _PyDict_GetItem_KnownHash(dict, nonkey, 0);
389 EXPECT_EQ(result, nullptr);
390 EXPECT_EQ(PyErr_Occurred(), nullptr);
391}
392
393TEST_F(DictExtensionApiTest, GetItemKnownHashReturnsBorrowedValue) {
394 PyObject* dict = PyDict_New();
395 PyObject* key = PyLong_FromLong(10);
396 PyObject* value = PyLong_FromLong(0);
397
398 // Insert the value into the dictionary
399 Py_hash_t hash = Py_hash_t{1} << ((sizeof(Py_hash_t) * CHAR_BIT) - 1);
400 ASSERT_EQ(_PyDict_SetItem_KnownHash(dict, key, value, hash), 0);
401
402 // Record the reference count of the value
403 long refcnt = Py_REFCNT(value);
404
405 // Get a new reference to the value from the dictionary
406 PyObject* value2 = _PyDict_GetItem_KnownHash(dict, key, hash);
407
408 // The new reference should be equal to the original reference
409 EXPECT_EQ(value2, value);
410
411 // The reference count should not be affected
412 EXPECT_EQ(Py_REFCNT(value), refcnt);
413
414 Py_DECREF(value);
415 Py_DECREF(key);
416 Py_DECREF(dict);
417}
418
419TEST_F(DictExtensionApiTest, GetItemStringReturnsValue) {
420 PyObjectPtr dict(PyDict_New());
421 const char* key_cstr = "key";
422 PyObjectPtr key(PyUnicode_FromString(key_cstr));
423 PyObjectPtr value(PyLong_FromLong(0));
424 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
425
426 PyObject* item = PyDict_GetItemString(dict, key_cstr);
427 EXPECT_EQ(item, value);
428}
429
430TEST_F(DictExtensionApiTest, SetItemWithNonDictRaisesSystemError) {
431 PyObjectPtr set(PySet_New(nullptr));
432 PyObjectPtr key(PyLong_FromLong(0));
433 PyObjectPtr val(PyLong_FromLong(0));
434
435 ASSERT_EQ(PyDict_SetItem(set, key, val), -1);
436 ASSERT_NE(PyErr_Occurred(), nullptr);
437 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
438}
439
440TEST_F(DictExtensionApiTest, SetItemWithNewDictReturnsZero) {
441 PyObjectPtr dict(PyDict_New());
442 PyObjectPtr key(PyLong_FromLong(0));
443 PyObjectPtr val(PyLong_FromLong(0));
444
445 EXPECT_EQ(PyDict_SetItem(dict, key, val), 0);
446 EXPECT_EQ(PyErr_Occurred(), nullptr);
447}
448
449TEST_F(DictExtensionApiTest, SetItemWithNewDictSubclassReturnsZero) {
450 PyRun_SimpleString(R"(
451class Foo(dict): pass
452obj = Foo()
453 )");
454
455 PyObjectPtr obj(mainModuleGet("obj"));
456 PyObjectPtr key(PyLong_FromLong(0));
457 PyObjectPtr val(PyLong_FromLong(0));
458
459 EXPECT_EQ(PyDict_SetItem(obj, key, val), 0);
460 EXPECT_EQ(PyErr_Occurred(), nullptr);
461}
462
463TEST_F(DictExtensionApiTest,
464 SetItemWithDunderHashReturningNonIntRaisesTypeError) {
465 PyRun_SimpleString(R"(
466class C:
467 def __hash__(self):
468 return "foo"
469 def __eq__(self, other):
470 return self == other
471c = C()
472)");
473 PyObjectPtr dict(PyDict_New());
474 PyObjectPtr key(mainModuleGet("c"));
475 PyObjectPtr val(PyLong_FromLong(0));
476
477 ASSERT_EQ(PyDict_SetItem(dict, key, val), -1);
478 ASSERT_NE(PyErr_Occurred(), nullptr);
479 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
480}
481
482TEST_F(DictExtensionApiTest, SetItemWithIntSubclassHashReturnsZero) {
483 PyRun_SimpleString(R"(
484class H(int):
485 pass
486class C:
487 def __init__(self, v):
488 self.v = v
489 def __hash__(self):
490 return H(42)
491 def __eq__(self, other):
492 return self.v == other.v
493c = C(4)
494)");
495
496 PyObjectPtr c(mainModuleGet("c"));
497 PyObjectPtr v(PyLong_FromLong(1));
498 PyObjectPtr dict(PyDict_New());
499 EXPECT_EQ(PyDict_SetItem(dict, c, v), 0);
500 EXPECT_EQ(PyErr_Occurred(), nullptr);
501}
502
503TEST_F(DictExtensionApiTest, SetItemWithSameIdentitySupersedesValue) {
504 ASSERT_EQ(PyRun_SimpleString(R"(
505called_dunder_eq = False
506class C:
507 def __eq__(self, other):
508 global called_dunder_eq
509 called_dunder_eq = True
510 def __hash__(self):
511 return 5
512
513c = C()
514d = {}
515d[c] = 0
516)"),
517 0);
518 PyObjectPtr c(mainModuleGet("c"));
519 PyObjectPtr dict(mainModuleGet("d"));
520 PyObjectPtr value(PyLong_FromLong(1));
521 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
522 ASSERT_EQ(PyDict_Size(dict), 1);
523 PyObject* result = PyDict_GetItem(dict, c);
524 ASSERT_EQ(PyErr_Occurred(), nullptr);
525 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
526 ASSERT_EQ(called_dunder_eq, Py_False);
527 EXPECT_EQ(value, result);
528}
529
530TEST_F(DictExtensionApiTest, SetItemWithDifferentHashInsertsValue) {
531 ASSERT_EQ(PyRun_SimpleString(R"(
532called_dunder_eq = False
533class C:
534 def __init__(self, h):
535 self.h = h
536 def __eq__(self, other):
537 global called_dunder_eq
538 called_dunder_eq = True
539 return True
540 def __hash__(self):
541 return self.h
542
543c = C(1)
544d = {}
545d[C(2)] = 2
546)"),
547 0);
548 PyObjectPtr c(mainModuleGet("c"));
549 PyObjectPtr dict(mainModuleGet("d"));
550 PyObjectPtr value(PyLong_FromLong(1));
551 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
552 ASSERT_EQ(PyErr_Occurred(), nullptr);
553 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
554 ASSERT_EQ(called_dunder_eq, Py_False);
555 EXPECT_EQ(PyDict_Size(dict), 2);
556}
557
558TEST_F(DictExtensionApiTest, SetItemWithDunderEqSupersedesValue) {
559 ASSERT_EQ(PyRun_SimpleString(R"(
560called_dunder_eq = False
561class C:
562 def __eq__(self, other):
563 global called_dunder_eq
564 called_dunder_eq = True
565 return True
566 def __hash__(self):
567 return 5
568
569c = C()
570d = {}
571d[C()] = 0
572)"),
573 0);
574 PyObjectPtr c(mainModuleGet("c"));
575 PyObjectPtr dict(mainModuleGet("d"));
576 PyObjectPtr value(PyLong_FromLong(1));
577 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
578 ASSERT_EQ(PyDict_Size(dict), 1);
579 PyObject* result = PyDict_GetItem(dict, c);
580 ASSERT_EQ(PyErr_Occurred(), nullptr);
581 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
582 ASSERT_EQ(called_dunder_eq, Py_True);
583 EXPECT_EQ(value, result);
584}
585
586TEST_F(DictExtensionApiTest, SetItemWithFalseDunderEqInsertsValue) {
587 ASSERT_EQ(PyRun_SimpleString(R"(
588called_dunder_eq = False
589class C:
590 def __eq__(self, other):
591 global called_dunder_eq
592 called_dunder_eq = True
593 return False
594 def __hash__(self):
595 return 5
596
597c = C()
598d = {}
599d[C()] = 0
600)"),
601 0);
602 PyObjectPtr c(mainModuleGet("c"));
603 PyObjectPtr dict(mainModuleGet("d"));
604 PyObjectPtr value(PyLong_FromLong(1));
605 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
606 ASSERT_EQ(PyErr_Occurred(), nullptr);
607 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
608 ASSERT_EQ(called_dunder_eq, Py_True);
609 EXPECT_EQ(PyDict_Size(dict), 2);
610}
611
612TEST_F(DictExtensionApiTest, SetItemWithExceptionDunderEqRaisesException) {
613 ASSERT_EQ(PyRun_SimpleString(R"(
614called_dunder_eq = False
615class C:
616 def __eq__(self, other):
617 global called_dunder_eq
618 called_dunder_eq = True
619 raise ValueError('foo')
620 def __hash__(self):
621 return 5
622
623c = C()
624d = {}
625d[C()] = 0
626)"),
627 0);
628 PyObjectPtr c(mainModuleGet("c"));
629 PyObjectPtr dict(mainModuleGet("d"));
630 PyObjectPtr value(PyLong_FromLong(1));
631 EXPECT_EQ(PyDict_SetItem(dict, c, value), -1);
632 EXPECT_NE(PyErr_Occurred(), nullptr);
633 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_ValueError));
634 PyErr_Clear();
635 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
636 ASSERT_EQ(called_dunder_eq, Py_True);
637 EXPECT_EQ(PyDict_Size(dict), 1);
638}
639
640TEST_F(DictExtensionApiTest, SetItemWithNotImplementedDunderEqInsertsValue) {
641 ASSERT_EQ(PyRun_SimpleString(R"(
642called_dunder_eq = False
643class C:
644 def __eq__(self, other):
645 global called_dunder_eq
646 called_dunder_eq = True
647 return NotImplemented
648 def __hash__(self):
649 return 5
650
651c = C()
652d = {}
653d[C()] = 0
654)"),
655 0);
656 PyObjectPtr c(mainModuleGet("c"));
657 PyObjectPtr dict(mainModuleGet("d"));
658 PyObjectPtr value(PyLong_FromLong(1));
659 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
660 ASSERT_EQ(PyErr_Occurred(), nullptr);
661 PyObjectPtr called_dunder_eq(mainModuleGet("called_dunder_eq"));
662 ASSERT_EQ(called_dunder_eq, Py_True);
663 EXPECT_EQ(PyDict_Size(dict), 2);
664}
665
666TEST_F(DictExtensionApiTest,
667 SetItemCallsExistingKeyDunderEqAndThenLookedKeyDunderEq) {
668 ASSERT_EQ(PyRun_SimpleString(R"(
669seq_num = 0
670
671def new_seq_num():
672 global seq_num
673 seq_num += 1
674 return seq_num
675
676c_eq = 0
677c_hash = 0
678
679class C:
680 def __eq__(self, other):
681 global c_eq
682 c_eq = new_seq_num()
683 return NotImplemented
684
685 def __hash__(self):
686 global c_hash
687 c_hash = new_seq_num()
688 return 5
689
690c = C()
691
692d_eq = 0
693d_hash = 0
694
695class D:
696 def __eq__(self, other):
697 global d_eq
698 d_eq = new_seq_num()
699 return True
700
701 def __hash__(self):
702 global d_hash
703 d_hash = new_seq_num()
704 return 5
705
706d = D()
707)"),
708 0);
709 PyObjectPtr dict(PyDict_New());
710 PyObjectPtr c(mainModuleGet("c"));
711 PyObjectPtr value(PyLong_FromLong(1));
712
713 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
714 ASSERT_EQ(PyErr_Occurred(), nullptr);
715 ASSERT_EQ(PyDict_Size(dict), 1);
716 PyObjectPtr c_eq(mainModuleGet("c_eq"));
717 ASSERT_EQ(PyLong_AsLong(c_eq), 0);
718 PyObjectPtr c_hash(mainModuleGet("c_hash"));
719 ASSERT_EQ(PyLong_AsLong(c_hash), 1);
720
721 PyObjectPtr d(mainModuleGet("d"));
722 ASSERT_EQ(PyDict_SetItem(dict, d, value), 0);
723 ASSERT_EQ(PyErr_Occurred(), nullptr);
724 ASSERT_EQ(PyDict_Size(dict), 1);
725 c_hash = mainModuleGet("c_hash");
726 EXPECT_EQ(PyLong_AsLong(c_hash), 1);
727 PyObjectPtr d_hash(mainModuleGet("d_hash"));
728 EXPECT_EQ(PyLong_AsLong(d_hash), 2);
729 c_eq = mainModuleGet("c_eq");
730 EXPECT_EQ(PyLong_AsLong(c_eq), 3);
731 PyObjectPtr d_eq(mainModuleGet("d_eq"));
732 EXPECT_EQ(PyLong_AsLong(d_eq), 4);
733}
734
735TEST_F(DictExtensionApiTest, SetItemRetainsExistingKeyObject) {
736 ASSERT_EQ(PyRun_SimpleString(R"(
737class C:
738 def __eq__(self, other):
739 return True
740
741 def __hash__(self):
742 return 5
743
744c = C()
745d = C()
746)"),
747 0);
748 PyObjectPtr dict(PyDict_New());
749 PyObjectPtr c(mainModuleGet("c"));
750 PyObjectPtr d(mainModuleGet("d"));
751 PyObjectPtr c_value(PyLong_FromLong(1));
752 PyObjectPtr d_value(PyLong_FromLong(2));
753
754 ASSERT_EQ(PyDict_SetItem(dict, c, c_value), 0);
755 ASSERT_EQ(PyErr_Occurred(), nullptr);
756 ASSERT_EQ(PyDict_Size(dict), 1);
757
758 ASSERT_EQ(PyDict_SetItem(dict, d, d_value), 0);
759 ASSERT_EQ(PyErr_Occurred(), nullptr);
760 ASSERT_EQ(PyDict_Size(dict), 1);
761
762 PyObjectPtr result(PyDict_Items(dict));
763 ASSERT_NE(result, nullptr);
764 ASSERT_EQ(PyErr_Occurred(), nullptr);
765 ASSERT_TRUE(PyList_CheckExact(result));
766 EXPECT_EQ(PyList_Size(result), 1);
767
768 PyObjectPtr kv(borrow(PyList_GetItem(result, 0)));
769 ASSERT_TRUE(PyTuple_CheckExact(kv));
770 ASSERT_EQ(PyTuple_Size(kv), 2);
771 EXPECT_EQ(PyTuple_GetItem(kv, 0), c);
772 EXPECT_EQ(PyTuple_GetItem(kv, 1), d_value);
773}
774
775TEST_F(DictExtensionApiTest, SetItemComparesHashValueFirst) {
776 ASSERT_EQ(PyRun_SimpleString(R"(
777class C:
778 def __init__(self, hash_code):
779 self.hash_code = hash_code
780
781 def __eq__(self, other):
782 raise UserWarning("unexpected")
783
784 def __hash__(self):
785 return self.hash_code
786
787c = C(4)
788d = C(5)
789)"),
790 0);
791 PyObjectPtr dict(PyDict_New());
792 PyObjectPtr c(mainModuleGet("c"));
793 PyObjectPtr value(PyLong_FromLong(500));
794
795 ASSERT_EQ(PyDict_SetItem(dict, c, value), 0);
796 ASSERT_EQ(PyErr_Occurred(), nullptr);
797 ASSERT_EQ(PyDict_Size(dict), 1);
798
799 PyObjectPtr d(mainModuleGet("d"));
800 ASSERT_EQ(PyDict_SetItem(dict, d, value), 0);
801 ASSERT_EQ(PyErr_Occurred(), nullptr);
802 ASSERT_EQ(PyDict_Size(dict), 2);
803}
804
805TEST_F(DictExtensionApiTest, SetItemStringInternsKeys) {
806 PyObjectPtr one(PyLong_FromLong(1));
807 const char* key = "unique-never-before-seen-test-key";
808 PyObjectPtr pydict(PyDict_New());
809
810 // Calling PyDict_SetItemString should create an interned string for the key
811 PyDict_SetItemString(pydict, key, one);
812 PyObjectPtr result(PyDict_Keys(pydict));
813 ASSERT_NE(result, nullptr);
814 ASSERT_EQ(PyErr_Occurred(), nullptr);
815 ASSERT_TRUE(PyList_Check(result));
816 EXPECT_EQ(PyList_Size(result), 1);
817 PyObjectPtr interned_str(borrow(PyList_GetItem(result, 0)));
818 ASSERT_EQ(PyErr_Occurred(), nullptr);
819
820 // Use a PyObject* directly because InternInPlace requires a reference to a
821 // PyObject*
822 PyObject* str = PyUnicode_FromString(key);
823 // Prior to interning str it should be a different object than
824 // the dictionary key
825 ASSERT_NE(str, interned_str);
826 // InternInPlace will update the object str points to to be the same as
827 // the dictionary key.
828 PyUnicode_InternInPlace(&str);
829 ASSERT_EQ(PyErr_Occurred(), nullptr);
830 ASSERT_EQ(str, interned_str);
831 Py_DECREF(str);
832}
833
834TEST_F(DictExtensionApiTest, SizeWithNonDictReturnsNegative) {
835 PyObject* list = PyList_New(0);
836 EXPECT_EQ(PyDict_Size(list), -1);
837 ASSERT_NE(PyErr_Occurred(), nullptr);
838 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
839
840 Py_DECREF(list);
841}
842
843TEST_F(DictExtensionApiTest, SizeWithEmptyDictReturnsZero) {
844 PyObjectPtr dict(PyDict_New());
845 EXPECT_EQ(PyDict_Size(dict), 0);
846}
847
848TEST_F(DictExtensionApiTest, SizeWithNonEmptyDict) {
849 PyObjectPtr dict(PyDict_New());
850 PyObjectPtr key1(PyLong_FromLong(1));
851 PyObjectPtr key2(PyLong_FromLong(2));
852 PyObjectPtr value1(PyLong_FromLong(0));
853 PyObjectPtr value2(PyLong_FromLong(0));
854 PyObjectPtr value3(PyLong_FromLong(0));
855
856 // Dict starts out empty
857 EXPECT_EQ(PyDict_Size(dict), 0);
858
859 // Inserting items for two different keys
860 ASSERT_EQ(PyDict_SetItem(dict, key1, value1), 0);
861 ASSERT_EQ(PyDict_SetItem(dict, key2, value2), 0);
862 EXPECT_EQ(PyDict_Size(dict), 2);
863
864 // Replace value for existing key
865 ASSERT_EQ(PyDict_SetItem(dict, key1, value3), 0);
866 EXPECT_EQ(PyDict_Size(dict), 2);
867}
868
869TEST_F(DictExtensionApiTest, ContainsWithKeyInDictReturnsOne) {
870 PyObjectPtr dict(PyDict_New());
871 PyObjectPtr key(PyLong_FromLong(10));
872 PyObjectPtr value(PyLong_FromLong(11));
873 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
874 EXPECT_EQ(PyDict_Contains(dict, key), 1);
875 EXPECT_EQ(PyErr_Occurred(), nullptr);
876}
877
878TEST_F(DictExtensionApiTest, ContainsWithKeyNotInDictReturnsZero) {
879 PyObjectPtr dict(PyDict_New());
880 PyObjectPtr key(PyLong_FromLong(10));
881 PyObjectPtr value(PyLong_FromLong(11));
882 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
883 ASSERT_EQ(PyErr_Occurred(), nullptr);
884 PyObjectPtr key2(PyLong_FromLong(666));
885 EXPECT_EQ(PyDict_Contains(dict, key2), 0);
886 EXPECT_EQ(PyErr_Occurred(), nullptr);
887}
888
889TEST_F(DictExtensionApiTest, ItemsWithNonDictRaisesSystemError) {
890 ASSERT_EQ(PyDict_Items(Py_None), nullptr);
891 ASSERT_NE(PyErr_Occurred(), nullptr);
892 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
893}
894
895TEST_F(DictExtensionApiTest, ItemsWithDictReturnsList) {
896 PyObjectPtr dict(PyDict_New());
897 PyObjectPtr key(PyLong_FromLong(10));
898 PyObjectPtr value(PyLong_FromLong(11));
899 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
900
901 PyObjectPtr result(PyDict_Items(dict));
902 ASSERT_NE(result, nullptr);
903 ASSERT_EQ(PyErr_Occurred(), nullptr);
904 ASSERT_TRUE(PyList_CheckExact(result));
905 EXPECT_EQ(PyList_Size(result), 1);
906
907 PyObject* kv = PyList_GetItem(result, 0);
908 ASSERT_TRUE(PyTuple_CheckExact(kv));
909 ASSERT_EQ(PyTuple_Size(kv), 2);
910 EXPECT_EQ(PyTuple_GetItem(kv, 0), key);
911 EXPECT_EQ(PyTuple_GetItem(kv, 1), value);
912}
913
914TEST_F(DictExtensionApiTest, KeysWithNonDictRaisesSystemError) {
915 EXPECT_EQ(PyDict_Keys(Py_None), nullptr);
916 ASSERT_NE(PyErr_Occurred(), nullptr);
917 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
918}
919
920TEST_F(DictExtensionApiTest, KeysWithDictReturnsList) {
921 PyObjectPtr dict(PyDict_New());
922
923 PyObjectPtr key(PyLong_FromLong(10));
924 PyObjectPtr value(PyLong_FromLong(11));
925 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
926
927 PyObjectPtr result(PyDict_Keys(dict));
928 ASSERT_NE(result, nullptr);
929 ASSERT_EQ(PyErr_Occurred(), nullptr);
930 ASSERT_TRUE(PyList_CheckExact(result));
931 EXPECT_EQ(PyList_Size(result), 1);
932 EXPECT_EQ(PyList_GetItem(result, 0), key);
933}
934
935TEST_F(DictExtensionApiTest, ValuesWithNonDictReturnsNull) {
936 EXPECT_EQ(PyDict_Values(Py_None), nullptr);
937 ASSERT_NE(PyErr_Occurred(), nullptr);
938 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
939}
940
941TEST_F(DictExtensionApiTest, ValuesWithEmptyDictReturnsEmptyList) {
942 PyObjectPtr dict(PyDict_New());
943 PyObjectPtr result(PyDict_Values(dict));
944 ASSERT_NE(result, nullptr);
945 ASSERT_EQ(PyErr_Occurred(), nullptr);
946 EXPECT_TRUE(PyList_CheckExact(result));
947 EXPECT_EQ(PyList_Size(result), 0);
948}
949
950TEST_F(DictExtensionApiTest, ValuesWithNonEmptyDictReturnsNonEmptyList) {
951 PyObjectPtr dict(PyDict_New());
952
953 PyObjectPtr key(PyLong_FromLong(10));
954 PyObjectPtr value(PyLong_FromLong(11));
955 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
956
957 PyObjectPtr result(PyDict_Values(dict));
958 ASSERT_NE(result, nullptr);
959 ASSERT_EQ(PyErr_Occurred(), nullptr);
960 ASSERT_TRUE(PyList_CheckExact(result));
961 EXPECT_EQ(PyList_Size(result), 1);
962 EXPECT_EQ(PyList_GetItem(result, 0), value);
963}
964
965TEST_F(DictExtensionApiTest, ClearWithNonDictDoesNotRaise) {
966 PyDict_Clear(Py_None);
967 ASSERT_EQ(PyErr_Occurred(), nullptr);
968}
969
970TEST_F(DictExtensionApiTest, ClearRemovesAllItems) {
971 PyObjectPtr dict(PyDict_New());
972 PyObjectPtr one(PyLong_FromLong(1));
973 PyObjectPtr two(PyLong_FromLong(1));
974 PyDict_SetItem(dict, one, two);
975 ASSERT_EQ(PyErr_Occurred(), nullptr);
976 PyObjectPtr three(PyLong_FromLong(1));
977 PyObjectPtr four(PyLong_FromLong(1));
978 PyDict_SetItem(dict, three, four);
979 ASSERT_EQ(PyErr_Occurred(), nullptr);
980
981 PyDict_Clear(dict);
982 EXPECT_EQ(PyErr_Occurred(), nullptr);
983 EXPECT_EQ(PyDict_Size(dict), 0);
984}
985
986TEST_F(DictExtensionApiTest, GETSIZEWithEmptyDictReturnsZero) {
987 PyObjectPtr dict(PyDict_New());
988 EXPECT_EQ(PyDict_GET_SIZE(dict.get()), 0);
989}
990
991TEST_F(DictExtensionApiTest, GETSIZEWithNonEmptyDict) {
992 PyObjectPtr dict(PyDict_New());
993 PyObjectPtr key1(PyLong_FromLong(1));
994 PyObjectPtr key2(PyLong_FromLong(2));
995 PyObjectPtr value1(PyLong_FromLong(0));
996 PyObjectPtr value2(PyLong_FromLong(0));
997 PyObjectPtr value3(PyLong_FromLong(0));
998
999 // Dict starts out empty
1000 EXPECT_EQ(PyDict_GET_SIZE(dict.get()), 0);
1001
1002 // Inserting items for two different keys
1003 ASSERT_EQ(PyDict_SetItem(dict, key1, value1), 0);
1004 ASSERT_EQ(PyDict_SetItem(dict, key2, value2), 0);
1005 EXPECT_EQ(PyDict_GET_SIZE(dict.get()), 2);
1006
1007 // Replace value for existing key
1008 ASSERT_EQ(PyDict_SetItem(dict, key1, value3), 0);
1009 EXPECT_EQ(PyDict_GET_SIZE(dict.get()), 2);
1010}
1011
1012TEST_F(DictExtensionApiTest, GetItemWithErrorNonExistingKeyReturnsNull) {
1013 PyObjectPtr dict(PyDict_New());
1014 PyObjectPtr key(PyLong_FromLong(666));
1015 PyObjectPtr result(PyDict_GetItemWithError(dict, key));
1016 EXPECT_EQ(result, nullptr);
1017 ASSERT_EQ(PyErr_Occurred(), nullptr);
1018}
1019
1020TEST_F(DictExtensionApiTest, GetItemWithErrorReturnsBorrowedValue) {
1021 PyObjectPtr dict(PyDict_New());
1022 PyObjectPtr key(PyLong_FromLong(10));
1023 PyObjectPtr value(PyLong_FromLong(666));
1024
1025 // Insert the value into the dictionary
1026 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
1027
1028 // Record the reference count of the value
1029 long refcnt = Py_REFCNT(value);
1030
1031 // Get a new reference to the value from the dictionary
1032 PyObject* value2 = PyDict_GetItemWithError(dict, key);
1033 ASSERT_EQ(PyErr_Occurred(), nullptr);
1034
1035 // The new reference should be equal to the original reference
1036 EXPECT_EQ(value2, value);
1037
1038 // The reference count should not be affected
1039 EXPECT_EQ(Py_REFCNT(value), refcnt);
1040}
1041
1042TEST_F(DictExtensionApiTest, GetItemWithErrorWithDictSubclassReturnsValue) {
1043 PyRun_SimpleString(R"(
1044class Foo(dict): pass
1045obj = Foo()
1046 )");
1047
1048 PyObjectPtr obj(mainModuleGet("obj"));
1049 PyObjectPtr key(PyLong_FromLong(1));
1050 PyObjectPtr val(PyLong_FromLong(2));
1051 ASSERT_EQ(PyDict_SetItem(obj, key, val), 0);
1052 ASSERT_EQ(PyErr_Occurred(), nullptr);
1053
1054 PyObject* result = PyDict_GetItemWithError(obj, key);
1055 ASSERT_EQ(PyErr_Occurred(), nullptr);
1056 EXPECT_EQ(result, val);
1057 EXPECT_EQ(PyErr_Occurred(), nullptr);
1058}
1059
1060TEST_F(DictExtensionApiTest,
1061 GetItemWithErrorWithUnhashableObjectRaisesTypeError) {
1062 PyRun_SimpleString(R"(
1063class C:
1064 __hash__ = None
1065obj = C()
1066)");
1067 PyObjectPtr dict(PyDict_New());
1068 PyObjectPtr key(mainModuleGet("obj"));
1069 EXPECT_EQ(PyDict_GetItemWithError(dict, key), nullptr);
1070 ASSERT_NE(PyErr_Occurred(), nullptr);
1071 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
1072}
1073
1074TEST_F(DictExtensionApiTest, DelItemWithNonDictReturnsNegativeOne) {
1075 EXPECT_EQ(PyDict_DelItem(Py_None, Py_None), -1);
1076 ASSERT_NE(PyErr_Occurred(), nullptr);
1077 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1078}
1079
1080TEST_F(DictExtensionApiTest, DelItemWithKeyInDictReturnsZero) {
1081 PyObjectPtr dict(PyDict_New());
1082 PyObjectPtr key(PyLong_FromLong(10));
1083 PyObjectPtr value(PyLong_FromLong(11));
1084 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
1085 EXPECT_EQ(PyDict_DelItem(dict, key), 0);
1086 ASSERT_EQ(PyErr_Occurred(), nullptr);
1087}
1088
1089TEST_F(DictExtensionApiTest, DelItemWithKeyNotInDictRaisesKeyError) {
1090 PyObjectPtr dict(PyDict_New());
1091 PyObjectPtr key(PyLong_FromLong(10));
1092 EXPECT_EQ(PyDict_DelItem(dict, key), -1);
1093 ASSERT_NE(PyErr_Occurred(), nullptr);
1094 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_KeyError));
1095}
1096
1097TEST_F(DictExtensionApiTest, DelItemWithUnhashableObjectRaisesTypeError) {
1098 PyRun_SimpleString(R"(
1099class C:
1100 __hash__ = None
1101c = C()
1102)");
1103 PyObjectPtr dict(PyDict_New());
1104 PyObject* main = PyImport_AddModule("__main__");
1105 PyObjectPtr key(PyObject_GetAttrString(main, "c"));
1106 EXPECT_EQ(PyDict_DelItem(dict, key), -1);
1107 ASSERT_NE(PyErr_Occurred(), nullptr);
1108 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
1109}
1110
1111TEST_F(DictExtensionApiTest, DelItemStringWithNonDictReturnsNegativeOne) {
1112 EXPECT_EQ(PyDict_DelItemString(Py_None, "hello, there"), -1);
1113 ASSERT_NE(PyErr_Occurred(), nullptr);
1114 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1115}
1116
1117TEST_F(DictExtensionApiTest, DelItemStringWithKeyInDictReturnsZero) {
1118 PyObjectPtr dict(PyDict_New());
1119 const char* strkey = "hello, there";
1120 PyObjectPtr key(PyUnicode_FromString(strkey));
1121 PyObjectPtr value(PyLong_FromLong(666));
1122 ASSERT_EQ(PyDict_SetItem(dict, key, value), 0);
1123 EXPECT_EQ(PyDict_DelItemString(dict, strkey), 0);
1124 ASSERT_EQ(PyErr_Occurred(), nullptr);
1125}
1126
1127TEST_F(DictExtensionApiTest, DelItemStringWithKeyNotInDictReturnsNegativeOne) {
1128 PyObjectPtr dict(PyDict_New());
1129 EXPECT_EQ(PyDict_DelItemString(dict, "hello, there"), -1);
1130 ASSERT_NE(PyErr_Occurred(), nullptr);
1131 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_KeyError));
1132}
1133
1134TEST_F(DictExtensionApiTest, NextWithEmptyDictReturnsFalse) {
1135 PyObject* key = nullptr;
1136 PyObject* value = nullptr;
1137 Py_ssize_t pos = 0;
1138 PyObjectPtr dict(PyDict_New());
1139 EXPECT_EQ(PyDict_Next(dict, &pos, &key, &value), 0);
1140 ASSERT_EQ(PyErr_Occurred(), nullptr);
1141}
1142
1143TEST_F(DictExtensionApiTest, NextWithNonEmptyDictReturnsKeysAndValues) {
1144 PyObjectPtr dict(PyDict_New());
1145 PyObjectPtr one(PyLong_FromLong(1));
1146 PyObjectPtr two(PyLong_FromLong(2));
1147 PyDict_SetItem(dict, one, two);
1148 PyObjectPtr three(PyLong_FromLong(3));
1149 PyObjectPtr four(PyLong_FromLong(4));
1150 PyDict_SetItem(dict, three, four);
1151
1152 Py_ssize_t pos = 0;
1153 PyObject* key = nullptr;
1154 PyObject* value = nullptr;
1155 ASSERT_EQ(PyDict_Next(dict, &pos, &key, &value), 1);
1156 ASSERT_EQ(PyErr_Occurred(), nullptr);
1157 EXPECT_EQ(key, one);
1158 EXPECT_EQ(value, two);
1159
1160 ASSERT_EQ(PyDict_Next(dict, &pos, &key, &value), 1);
1161 ASSERT_EQ(PyErr_Occurred(), nullptr);
1162 EXPECT_EQ(key, three);
1163 EXPECT_EQ(value, four);
1164
1165 ASSERT_EQ(PyDict_Next(dict, &pos, &key, &value), 0);
1166 ASSERT_EQ(PyErr_Occurred(), nullptr);
1167}
1168
1169TEST_F(DictExtensionApiTest, NextAcceptsNullKeyPointer) {
1170 PyObjectPtr dict(PyDict_New());
1171 PyObjectPtr one(PyLong_FromLong(1));
1172 PyObjectPtr two(PyLong_FromLong(2));
1173 PyDict_SetItem(dict, one, two);
1174 PyObjectPtr three(PyLong_FromLong(3));
1175 PyObjectPtr four(PyLong_FromLong(4));
1176 PyDict_SetItem(dict, three, four);
1177
1178 Py_ssize_t pos = 0;
1179 PyObject* value = nullptr;
1180 ASSERT_EQ(PyDict_Next(dict, &pos, nullptr, &value), 1);
1181 ASSERT_EQ(PyErr_Occurred(), nullptr);
1182}
1183
1184TEST_F(DictExtensionApiTest, NextAcceptsNullValuePointer) {
1185 PyObjectPtr dict(PyDict_New());
1186 PyObjectPtr one(PyLong_FromLong(1));
1187 PyObjectPtr two(PyLong_FromLong(2));
1188 PyDict_SetItem(dict, one, two);
1189 PyObjectPtr three(PyLong_FromLong(3));
1190 PyObjectPtr four(PyLong_FromLong(4));
1191 PyDict_SetItem(dict, three, four);
1192
1193 Py_ssize_t pos = 0;
1194 PyObject* key = nullptr;
1195 ASSERT_EQ(PyDict_Next(dict, &pos, &key, nullptr), 1);
1196 ASSERT_EQ(PyErr_Occurred(), nullptr);
1197}
1198
1199TEST_F(DictExtensionApiTest, UnderNextWithEmptyDictReturnsFalse) {
1200 PyObject* key = nullptr;
1201 PyObject* value = nullptr;
1202 Py_hash_t hash = 0;
1203 Py_ssize_t pos = 0;
1204 PyObjectPtr dict(PyDict_New());
1205 EXPECT_EQ(_PyDict_Next(dict, &pos, &key, &value, &hash), 0);
1206 ASSERT_EQ(PyErr_Occurred(), nullptr);
1207}
1208
1209TEST_F(DictExtensionApiTest, UnderNextWithNonEmptyDictReturnsKeysAndValues) {
1210 PyObjectPtr dict(PyDict_New());
1211 PyObjectPtr one(PyLong_FromLong(1));
1212 PyObjectPtr two(PyLong_FromLong(2));
1213 PyDict_SetItem(dict, one, two);
1214 PyObjectPtr three(PyLong_FromLong(3));
1215 PyObjectPtr four(PyLong_FromLong(4));
1216 PyDict_SetItem(dict, three, four);
1217
1218 Py_ssize_t pos = 0;
1219 PyObject* key = nullptr;
1220 PyObject* value = nullptr;
1221 Py_hash_t hash = 0;
1222 ASSERT_EQ(_PyDict_Next(dict, &pos, &key, &value, &hash), 1);
1223 ASSERT_EQ(PyErr_Occurred(), nullptr);
1224 EXPECT_EQ(key, one);
1225 EXPECT_EQ(value, two);
1226 EXPECT_EQ(hash, 1);
1227
1228 ASSERT_EQ(_PyDict_Next(dict, &pos, &key, &value, &hash), 1);
1229 ASSERT_EQ(PyErr_Occurred(), nullptr);
1230 EXPECT_EQ(key, three);
1231 EXPECT_EQ(value, four);
1232 EXPECT_EQ(hash, 3);
1233
1234 ASSERT_EQ(_PyDict_Next(dict, &pos, &key, &value, &hash), 0);
1235 ASSERT_EQ(PyErr_Occurred(), nullptr);
1236}
1237
1238TEST_F(DictExtensionApiTest, UnderNextAcceptsNullKeyPointer) {
1239 PyObjectPtr dict(PyDict_New());
1240 PyObjectPtr one(PyLong_FromLong(1));
1241 PyObjectPtr two(PyLong_FromLong(2));
1242 PyDict_SetItem(dict, one, two);
1243 PyObjectPtr three(PyLong_FromLong(3));
1244 PyObjectPtr four(PyLong_FromLong(4));
1245 PyDict_SetItem(dict, three, four);
1246
1247 Py_ssize_t pos = 0;
1248 PyObject* value = nullptr;
1249 Py_hash_t hash = 0;
1250 ASSERT_EQ(_PyDict_Next(dict, &pos, nullptr, &value, &hash), 1);
1251 ASSERT_EQ(PyErr_Occurred(), nullptr);
1252}
1253
1254TEST_F(DictExtensionApiTest, UnderNextAcceptsNullValuePointer) {
1255 PyObjectPtr dict(PyDict_New());
1256 PyObjectPtr one(PyLong_FromLong(1));
1257 PyObjectPtr two(PyLong_FromLong(2));
1258 PyDict_SetItem(dict, one, two);
1259 PyObjectPtr three(PyLong_FromLong(3));
1260 PyObjectPtr four(PyLong_FromLong(4));
1261 PyDict_SetItem(dict, three, four);
1262
1263 Py_ssize_t pos = 0;
1264 PyObject* key = nullptr;
1265 Py_hash_t hash = 0;
1266 ASSERT_EQ(_PyDict_Next(dict, &pos, &key, nullptr, &hash), 1);
1267 ASSERT_EQ(PyErr_Occurred(), nullptr);
1268}
1269
1270TEST_F(DictExtensionApiTest, UnderNextAcceptsNullHashPointer) {
1271 PyObjectPtr dict(PyDict_New());
1272 PyObjectPtr one(PyLong_FromLong(1));
1273 PyObjectPtr two(PyLong_FromLong(2));
1274 PyDict_SetItem(dict, one, two);
1275 PyObjectPtr three(PyLong_FromLong(3));
1276 PyObjectPtr four(PyLong_FromLong(4));
1277 PyDict_SetItem(dict, three, four);
1278
1279 Py_ssize_t pos = 0;
1280 PyObject* key = nullptr;
1281 PyObject* value = nullptr;
1282 ASSERT_EQ(_PyDict_Next(dict, &pos, &key, &value, nullptr), 1);
1283 ASSERT_EQ(PyErr_Occurred(), nullptr);
1284}
1285
1286TEST_F(DictExtensionApiTest, CopyWithNullRaisesSystemError) {
1287 EXPECT_EQ(PyDict_Copy(nullptr), nullptr);
1288 ASSERT_NE(PyErr_Occurred(), nullptr);
1289 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1290}
1291
1292TEST_F(DictExtensionApiTest, CopyWithNonDictInstanceRaisesSystemError) {
1293 EXPECT_EQ(PyDict_Copy(Py_None), nullptr);
1294 ASSERT_NE(PyErr_Occurred(), nullptr);
1295 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1296}
1297
1298TEST_F(DictExtensionApiTest, CopyMakesShallowCopyOfDictElements) {
1299 PyObjectPtr dict(PyDict_New());
1300 PyObjectPtr one(PyLong_FromLong(1));
1301 PyObjectPtr val1(PyTuple_New(0));
1302 PyDict_SetItem(dict, one, val1);
1303 PyObjectPtr three(PyLong_FromLong(3));
1304 PyObjectPtr val2(PyTuple_New(0));
1305 PyDict_SetItem(dict, three, val2);
1306
1307 PyObjectPtr copy(PyDict_Copy(dict));
1308 ASSERT_NE(copy, nullptr);
1309 ASSERT_EQ(PyErr_Occurred(), nullptr);
1310 ASSERT_TRUE(PyDict_CheckExact(copy));
1311 EXPECT_EQ(PyDict_Size(copy), 2);
1312 EXPECT_EQ(PyDict_GetItem(copy, one), val1);
1313 EXPECT_EQ(PyDict_GetItem(copy, three), val2);
1314}
1315
1316TEST_F(DictExtensionApiTest, MergeWithNullLhsRaisesSystemError) {
1317 PyObjectPtr rhs(PyDict_New());
1318 ASSERT_EQ(PyDict_Merge(nullptr, rhs, 0), -1);
1319 ASSERT_NE(PyErr_Occurred(), nullptr);
1320 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1321}
1322
1323TEST_F(DictExtensionApiTest, MergeWithNonDictLhsRaisesSystemError) {
1324 PyObjectPtr rhs(PyDict_New());
1325 ASSERT_EQ(PyDict_Merge(Py_None, rhs, 0), -1);
1326 ASSERT_NE(PyErr_Occurred(), nullptr);
1327 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1328}
1329
1330TEST_F(DictExtensionApiTest, MergeWithNullRhsRaisesSystemError) {
1331 PyObjectPtr lhs(PyDict_New());
1332 ASSERT_EQ(PyDict_Merge(lhs, nullptr, 0), -1);
1333 ASSERT_NE(PyErr_Occurred(), nullptr);
1334 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1335}
1336
1337TEST_F(DictExtensionApiTest, MergeAddsKeysToLhs) {
1338 PyObjectPtr rhs(PyDict_New());
1339 PyObjectPtr one(PyLong_FromLong(1));
1340 PyObjectPtr two(PyLong_FromLong(2));
1341 PyDict_SetItem(rhs, one, two);
1342 PyObjectPtr three(PyLong_FromLong(3));
1343 PyObjectPtr four(PyLong_FromLong(4));
1344 PyDict_SetItem(rhs, three, four);
1345
1346 PyObjectPtr lhs(PyDict_New());
1347 ASSERT_EQ(PyDict_Merge(lhs, rhs, 0), 0);
1348 ASSERT_EQ(PyErr_Occurred(), nullptr);
1349 EXPECT_EQ(PyDict_Size(lhs), 2);
1350
1351 EXPECT_TRUE(PyDict_Contains(lhs, one));
1352 EXPECT_EQ(PyDict_GetItem(lhs, one), two);
1353
1354 EXPECT_TRUE(PyDict_Contains(lhs, three));
1355 EXPECT_EQ(PyDict_GetItem(lhs, three), four);
1356}
1357
1358TEST_F(DictExtensionApiTest, MergeWithoutOverrideIgnoresKeys) {
1359 PyObjectPtr lhs(PyDict_New());
1360 PyObjectPtr rhs(PyDict_New());
1361 PyObjectPtr one(PyLong_FromLong(1));
1362 PyObjectPtr two(PyLong_FromLong(2));
1363 PyDict_SetItem(lhs, one, two);
1364 PyDict_SetItem(rhs, one, two);
1365 PyObjectPtr three(PyLong_FromLong(3));
1366 PyObjectPtr four(PyLong_FromLong(4));
1367 PyDict_SetItem(rhs, three, four);
1368 PyObjectPtr not_in_rhs(PyLong_FromLong(666));
1369 PyDict_SetItem(lhs, three, not_in_rhs);
1370
1371 ASSERT_EQ(PyDict_Merge(lhs, rhs, 0), 0);
1372 ASSERT_EQ(PyErr_Occurred(), nullptr);
1373 EXPECT_EQ(PyDict_Size(lhs), 2);
1374 EXPECT_EQ(PyDict_GetItem(lhs, one), two);
1375 EXPECT_EQ(PyDict_GetItem(lhs, three), not_in_rhs);
1376}
1377
1378TEST_F(DictExtensionApiTest, MergeWithOverrideReplacesKeys) {
1379 PyObjectPtr lhs(PyDict_New());
1380 PyObjectPtr rhs(PyDict_New());
1381 PyObjectPtr one(PyLong_FromLong(1));
1382 PyObjectPtr two(PyLong_FromLong(2));
1383 PyDict_SetItem(lhs, one, two);
1384 PyDict_SetItem(rhs, one, two);
1385 PyObjectPtr three(PyLong_FromLong(3));
1386 PyObjectPtr four(PyLong_FromLong(4));
1387 PyDict_SetItem(rhs, three, four);
1388 PyObjectPtr not_in_rhs(PyLong_FromLong(666));
1389 PyDict_SetItem(lhs, three, not_in_rhs);
1390
1391 ASSERT_EQ(PyDict_Merge(lhs, rhs, 1), 0);
1392 ASSERT_EQ(PyErr_Occurred(), nullptr);
1393 EXPECT_EQ(PyDict_Size(lhs), 2);
1394
1395 EXPECT_TRUE(PyDict_Contains(lhs, one));
1396 EXPECT_EQ(PyDict_GetItem(lhs, one), two);
1397
1398 EXPECT_TRUE(PyDict_Contains(lhs, three));
1399 EXPECT_EQ(PyDict_GetItem(lhs, three), four);
1400}
1401
1402TEST_F(DictExtensionApiTest, MergeWithNonMappingRaisesAttributeError) {
1403 PyRun_SimpleString(R"(
1404class Mapping:
1405 pass
1406m = Mapping()
1407)");
1408 PyObjectPtr rhs(mainModuleGet("m"));
1409 PyObjectPtr lhs(PyDict_New());
1410 ASSERT_EQ(PyDict_Merge(lhs, rhs, 0), -1);
1411 ASSERT_NE(PyErr_Occurred(), nullptr);
1412 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_AttributeError));
1413}
1414
1415TEST_F(DictExtensionApiTest, MergeWithMappingRhsAddsKeysToLhs) {
1416 PyRun_SimpleString(R"(
1417class Mapping:
1418 def __init__(self):
1419 self.d = {1:2, 3:4}
1420 def keys(self):
1421 return self.d.keys()
1422 def __getitem__(self, i):
1423 return self.d[i]
1424m = Mapping()
1425)");
1426 PyObjectPtr rhs(mainModuleGet("m"));
1427 PyObjectPtr lhs(PyDict_New());
1428 ASSERT_EQ(PyDict_Merge(lhs, rhs, 0), 0);
1429 ASSERT_EQ(PyErr_Occurred(), nullptr);
1430 EXPECT_EQ(PyDict_Size(lhs), 2);
1431
1432 PyObjectPtr one(PyLong_FromLong(1));
1433 EXPECT_TRUE(PyDict_Contains(lhs, one));
1434 PyObject* two = PyDict_GetItem(lhs, one);
1435 EXPECT_EQ(PyLong_AsLong(two), 2);
1436
1437 PyObjectPtr three(PyLong_FromLong(3));
1438 EXPECT_TRUE(PyDict_Contains(lhs, three));
1439 PyObject* four = PyDict_GetItem(lhs, three);
1440 EXPECT_EQ(PyLong_AsLong(four), 4);
1441}
1442
1443TEST_F(DictExtensionApiTest, UpdateWithNullLhsRaisesSystemError) {
1444 PyObjectPtr rhs(PyDict_New());
1445 ASSERT_EQ(PyDict_Update(nullptr, rhs), -1);
1446 ASSERT_NE(PyErr_Occurred(), nullptr);
1447 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1448}
1449
1450TEST_F(DictExtensionApiTest, UpdateWithNonListLhsRaisesSystemError) {
1451 PyObjectPtr rhs(PyDict_New());
1452 ASSERT_EQ(PyDict_Update(Py_None, rhs), -1);
1453 ASSERT_NE(PyErr_Occurred(), nullptr);
1454 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1455}
1456
1457TEST_F(DictExtensionApiTest, UpdateWithNullRhsRaisesSystemError) {
1458 PyObjectPtr lhs(PyDict_New());
1459 ASSERT_EQ(PyDict_Update(lhs, nullptr), -1);
1460 ASSERT_NE(PyErr_Occurred(), nullptr);
1461 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_SystemError));
1462}
1463
1464TEST_F(DictExtensionApiTest, UpdateWithLhsEqualRhsDoesNothing) {
1465 PyObjectPtr lhs(PyDict_New());
1466 PyObject* rhs = lhs;
1467 ASSERT_EQ(PyDict_Update(lhs, rhs), 0);
1468 ASSERT_EQ(PyErr_Occurred(), nullptr);
1469 EXPECT_EQ(lhs, rhs);
1470}
1471
1472TEST_F(DictExtensionApiTest, UpdateWithEmptyRhsDoesNothing) {
1473 PyObjectPtr lhs(PyDict_New());
1474
1475 PyObjectPtr one(PyLong_FromLong(1));
1476 PyObjectPtr two(PyLong_FromLong(2));
1477 PyDict_SetItem(lhs, one, two);
1478 PyObjectPtr three(PyLong_FromLong(3));
1479 PyObjectPtr four(PyLong_FromLong(4));
1480 PyDict_SetItem(lhs, three, four);
1481 ASSERT_EQ(PyDict_Size(lhs), 2);
1482
1483 PyObjectPtr rhs(PyDict_New());
1484 ASSERT_EQ(PyDict_Update(lhs, rhs), 0);
1485 ASSERT_EQ(PyErr_Occurred(), nullptr);
1486 EXPECT_EQ(PyDict_Size(lhs), 2);
1487 EXPECT_EQ(PyDict_GetItem(lhs, one), two);
1488 EXPECT_EQ(PyDict_GetItem(lhs, three), four);
1489}
1490
1491TEST_F(DictExtensionApiTest, UpdateWithEmptyLhsAddsKeysToLhs) {
1492 PyObjectPtr rhs(PyDict_New());
1493 PyObjectPtr one(PyLong_FromLong(1));
1494 PyObjectPtr two(PyLong_FromLong(2));
1495 PyDict_SetItem(rhs, one, two);
1496 PyObjectPtr three(PyLong_FromLong(3));
1497 PyObjectPtr four(PyLong_FromLong(4));
1498 PyDict_SetItem(rhs, three, four);
1499 ASSERT_EQ(PyDict_Size(rhs), 2);
1500
1501 PyObjectPtr lhs(PyDict_New());
1502 ASSERT_EQ(PyDict_Update(lhs, rhs), 0);
1503 ASSERT_EQ(PyErr_Occurred(), nullptr);
1504 EXPECT_EQ(PyDict_Size(lhs), 2);
1505
1506 EXPECT_TRUE(PyDict_Contains(lhs, one));
1507 EXPECT_EQ(PyDict_GetItem(lhs, one), two);
1508 EXPECT_TRUE(PyDict_Contains(lhs, three));
1509 EXPECT_EQ(PyDict_GetItem(lhs, three), four);
1510}
1511
1512TEST_F(DictExtensionApiTest, UpdateOverwritesKeys) {
1513 PyObjectPtr rhs(PyDict_New());
1514 PyObjectPtr one(PyLong_FromLong(1));
1515 PyObjectPtr two(PyLong_FromLong(2));
1516 PyDict_SetItem(rhs, one, two);
1517 PyObjectPtr three(PyLong_FromLong(3));
1518 PyObjectPtr four(PyLong_FromLong(4));
1519 PyDict_SetItem(rhs, three, four);
1520 ASSERT_EQ(PyDict_Size(rhs), 2);
1521
1522 PyObjectPtr lhs(PyDict_New());
1523 PyObjectPtr not_in_rhs(PyLong_FromLong(666));
1524 ASSERT_EQ(PyDict_SetItem(lhs, one, not_in_rhs), 0);
1525 ASSERT_EQ(PyDict_GetItem(lhs, one), not_in_rhs);
1526
1527 ASSERT_EQ(PyDict_Update(lhs, rhs), 0);
1528 ASSERT_EQ(PyErr_Occurred(), nullptr);
1529 EXPECT_EQ(PyDict_Size(lhs), 2);
1530
1531 EXPECT_TRUE(PyDict_Contains(lhs, one));
1532 EXPECT_EQ(PyDict_GetItem(lhs, one), two);
1533
1534 EXPECT_TRUE(PyDict_Contains(lhs, three));
1535 EXPECT_EQ(PyDict_GetItem(lhs, three), four);
1536}
1537
1538TEST_F(DictExtensionApiTest,
1539 ObjectGenericGetDictWithInstanceRaisesAttributeError) {
1540 PyObjectPtr obj(PyLong_FromLong(0));
1541 ASSERT_EQ(PyObject_GenericGetDict(obj, nullptr), nullptr);
1542 ASSERT_NE(PyErr_Occurred(), nullptr);
1543 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_AttributeError));
1544}
1545
1546TEST_F(DictExtensionApiTest, ObjectGenericGetDictWithTypeReturnsTypeDict) {
1547 PyObject* obj = reinterpret_cast<PyObject*>(&PyLong_Type);
1548 PyObjectPtr dict(PyObject_GenericGetDict(obj, nullptr));
1549 ASSERT_TRUE(PyMapping_Check(dict));
1550 EXPECT_GT(PyMapping_Length(dict), 0);
1551}
1552
1553} // namespace testing
1554} // namespace py