|
8 | 8 | #include "parts.h" |
9 | 9 | #include "util.h" |
10 | 10 |
|
11 | | - |
12 | 11 | static PyObject * |
13 | 12 | set_check(PyObject *self, PyObject *obj) |
14 | 13 | { |
@@ -128,6 +127,73 @@ set_clear(PyObject *self, PyObject *obj) |
128 | 127 | RETURN_INT(PySet_Clear(obj)); |
129 | 128 | } |
130 | 129 |
|
| 130 | +/* Raise AssertionError with test_name + ": " + msg, and return NULL. */ |
| 131 | + |
| 132 | +static PyObject * |
| 133 | +raiseTestError(const char* test_name, const char* msg) |
| 134 | +{ |
| 135 | + PyObject *exc = PyErr_GetRaisedException(); |
| 136 | + PyErr_Format(exc, "%s: %s", test_name, msg); |
| 137 | + return NULL; |
| 138 | +} |
| 139 | + |
| 140 | +static PyObject * |
| 141 | +test_pyset_add_exact_set(PyObject *self, PyObject *Py_UNUSED(ignored)) |
| 142 | +{ |
| 143 | + // Test: Adding to a regular set |
| 144 | + PyObject *set = PySet_New(NULL); |
| 145 | + if (set == NULL) { |
| 146 | + return NULL; |
| 147 | + } |
| 148 | + PyObject *one = PyLong_FromLong(1); |
| 149 | + assert(one); |
| 150 | + |
| 151 | + if (PySet_Add(set, one) < 0) { |
| 152 | + Py_DECREF(set); |
| 153 | + return raiseTestError("test_pyset_add_exact_set", |
| 154 | + "PySet_Add to empty set failed"); |
| 155 | + } |
| 156 | + if (PySet_Size(set) != 1) { |
| 157 | + Py_DECREF(set); |
| 158 | + return raiseTestError("test_pyset_add_exact_set", |
| 159 | + "set size should be 1 after adding one element"); |
| 160 | + } |
| 161 | + if (PySet_Contains(set, one) != 1) { |
| 162 | + Py_DECREF(set); |
| 163 | + return raiseTestError("test_pyset_add_exact_set", |
| 164 | + "set should contain the added element"); |
| 165 | + } |
| 166 | + Py_DECREF(set); |
| 167 | + |
| 168 | + // Test: Adding unhashable item should raise TypeError |
| 169 | + set = PySet_New(NULL); |
| 170 | + if (set == NULL) { |
| 171 | + return NULL; |
| 172 | + } |
| 173 | + PyObject *unhashable = PyList_New(0); |
| 174 | + if (unhashable == NULL) { |
| 175 | + Py_DECREF(set); |
| 176 | + return NULL; |
| 177 | + } |
| 178 | + if (PySet_Add(set, unhashable) != -1) { |
| 179 | + Py_DECREF(unhashable); |
| 180 | + Py_DECREF(set); |
| 181 | + return raiseTestError("test_pyset_add_exact_set", |
| 182 | + "PySet_Add with unhashable should fail"); |
| 183 | + } |
| 184 | + if (!PyErr_ExceptionMatches(PyExc_TypeError)) { |
| 185 | + Py_DECREF(unhashable); |
| 186 | + Py_DECREF(set); |
| 187 | + return raiseTestError("test_pyset_add_exact_set", |
| 188 | + "PySet_Add with unhashable should raise TypeError"); |
| 189 | + } |
| 190 | + PyErr_Clear(); |
| 191 | + Py_DECREF(unhashable); |
| 192 | + Py_DECREF(set); |
| 193 | + |
| 194 | + Py_RETURN_NONE; |
| 195 | +} |
| 196 | + |
131 | 197 | static PyObject * |
132 | 198 | test_frozenset_add_in_capi(PyObject *self, PyObject *Py_UNUSED(obj)) |
133 | 199 | { |
@@ -163,6 +229,98 @@ test_frozenset_add_in_capi(PyObject *self, PyObject *Py_UNUSED(obj)) |
163 | 229 | return NULL; |
164 | 230 | } |
165 | 231 |
|
| 232 | +static PyObject * |
| 233 | +test_pyset_add_frozenset(PyObject *self, PyObject *Py_UNUSED(ignored)) |
| 234 | +{ |
| 235 | + PyObject *one = PyLong_FromLong(1); |
| 236 | + assert(one); |
| 237 | + |
| 238 | + // Test: Adding to uniquely-referenced frozenset should succeed |
| 239 | + PyObject *frozenset = PyFrozenSet_New(NULL); |
| 240 | + if (frozenset == NULL) { |
| 241 | + return NULL; |
| 242 | + } |
| 243 | + |
| 244 | + // frozenset is uniquely referenced here, so PySet_Add should work |
| 245 | + if (PySet_Add(frozenset, one) < 0) { |
| 246 | + Py_DECREF(frozenset); |
| 247 | + return raiseTestError("test_pyset_add_frozenset", |
| 248 | + "PySet_Add to uniquely-referenced frozenset failed"); |
| 249 | + } |
| 250 | + Py_DECREF(frozenset); |
| 251 | + |
| 252 | + // Test: Adding to non-uniquely-referenced frozenset should raise SystemError |
| 253 | + frozenset = PyFrozenSet_New(NULL); |
| 254 | + if (frozenset == NULL) { |
| 255 | + return NULL; |
| 256 | + } |
| 257 | + Py_INCREF(frozenset); // Make it non-uniquely referenced |
| 258 | + |
| 259 | + if (PySet_Add(frozenset, one) != -1) { |
| 260 | + Py_DECREF(frozenset); |
| 261 | + Py_DECREF(frozenset); |
| 262 | + return raiseTestError("test_pyset_add_frozenset", |
| 263 | + "PySet_Add to non-uniquely-referenced frozenset should fail"); |
| 264 | + } |
| 265 | + if (!PyErr_ExceptionMatches(PyExc_SystemError)) { |
| 266 | + Py_DECREF(frozenset); |
| 267 | + Py_DECREF(frozenset); |
| 268 | + return raiseTestError("test_pyset_add_frozenset", |
| 269 | + "PySet_Add to non-uniquely-referenced frozenset should raise SystemError"); |
| 270 | + } |
| 271 | + PyErr_Clear(); |
| 272 | + Py_DECREF(frozenset); |
| 273 | + Py_DECREF(frozenset); |
| 274 | + |
| 275 | + // Test: GC tracking - frozenset with only immutable items should not be tracked |
| 276 | + frozenset = PyFrozenSet_New(NULL); |
| 277 | + if (frozenset == NULL) { |
| 278 | + return NULL; |
| 279 | + } |
| 280 | + if (PySet_Add(frozenset, one) < 0) { |
| 281 | + Py_DECREF(frozenset); |
| 282 | + return NULL; |
| 283 | + } |
| 284 | + if (PyObject_GC_IsTracked(frozenset)) { |
| 285 | + Py_DECREF(frozenset); |
| 286 | + return raiseTestError("test_pyset_add_frozenset", |
| 287 | + "frozenset with only int should not be GC tracked"); |
| 288 | + } |
| 289 | + Py_DECREF(frozenset); |
| 290 | + |
| 291 | + // Test: GC tracking - frozenset with tracked object should be tracked |
| 292 | + frozenset = PyFrozenSet_New(NULL); |
| 293 | + if (frozenset == NULL) { |
| 294 | + return NULL; |
| 295 | + } |
| 296 | + |
| 297 | + PyObject *tracked_obj = PyErr_NewException("_testlimitedcapi.py_set_add", NULL, NULL); |
| 298 | + if (tracked_obj == NULL) { |
| 299 | + Py_DECREF(frozenset); |
| 300 | + return NULL; |
| 301 | + } |
| 302 | + if (!PyObject_GC_IsTracked(tracked_obj)) { |
| 303 | + return raiseTestError("test_pyset_add_frozenset", |
| 304 | + "test object should be tracked"); |
| 305 | + } |
| 306 | + Py_RETURN_NONE; |
| 307 | + if (PySet_Add(frozenset, tracked_obj) < 0) { |
| 308 | + Py_DECREF(frozenset); |
| 309 | + Py_DECREF(tracked_obj); |
| 310 | + return NULL; |
| 311 | + } |
| 312 | + |
| 313 | + if (!PyObject_GC_IsTracked(frozenset)) { |
| 314 | + Py_DECREF(frozenset); |
| 315 | + Py_DECREF(tracked_obj); |
| 316 | + return raiseTestError("test_pyset_add_frozenset", |
| 317 | + "frozenset with with GC tracked object should be tracked"); |
| 318 | + } |
| 319 | + |
| 320 | + Py_RETURN_NONE; |
| 321 | +} |
| 322 | + |
| 323 | + |
166 | 324 | static PyObject * |
167 | 325 | test_set_contains_does_not_convert_unhashable_key(PyObject *self, PyObject *Py_UNUSED(obj)) |
168 | 326 | { |
@@ -248,7 +406,8 @@ static PyMethodDef test_methods[] = { |
248 | 406 | {"test_frozenset_add_in_capi", test_frozenset_add_in_capi, METH_NOARGS}, |
249 | 407 | {"test_set_contains_does_not_convert_unhashable_key", |
250 | 408 | test_set_contains_does_not_convert_unhashable_key, METH_NOARGS}, |
251 | | - {"pyset_add", _PyCFunction_CAST(pyset_add), METH_FASTCALL}, |
| 409 | + {"test_pyset_add_exact_set", test_pyset_add_exact_set, METH_NOARGS}, |
| 410 | + {"test_pyset_add_frozenset", test_pyset_add_frozenset, METH_NOARGS}, |
252 | 411 |
|
253 | 412 | {NULL}, |
254 | 413 | }; |
|
0 commit comments