|
| 1 | +import gc |
| 2 | +import threading |
1 | 3 | import unittest |
2 | 4 |
|
3 | 5 | from test.support import import_helper |
| 6 | +from test.support import threading_helper |
4 | 7 |
|
5 | 8 | # Skip this test if the _testcapi, _testlimitedcapi or _testinternalcapi |
6 | 9 | # modules aren't available. |
7 | 10 | _testcapi = import_helper.import_module('_testcapi') |
8 | 11 | _testlimitedcapi = import_helper.import_module('_testlimitedcapi') |
9 | 12 | _testinternalcapi = import_helper.import_module('_testinternalcapi') |
10 | 13 |
|
| 14 | + |
11 | 15 | class set_subclass(set): |
12 | 16 | pass |
13 | 17 |
|
@@ -273,5 +277,74 @@ def test_set_next_entry(self): |
273 | 277 | # CRASHES: set_next(NULL, 0) |
274 | 278 |
|
275 | 279 |
|
| 280 | +class FrozenSetWriterTests(unittest.TestCase): |
| 281 | + def create_writer(self): |
| 282 | + # Test PyFrozenSetWriter_Create() |
| 283 | + return _testcapi.PyFrozenSetWriter() |
| 284 | + |
| 285 | + def frozenset_equal(self, result, expected): |
| 286 | + if not isinstance(expected, frozenset): |
| 287 | + expected = frozenset(expected) |
| 288 | + self.assertEqual(result, expected) |
| 289 | + self.assertEqual(type(result), frozenset) |
| 290 | + |
| 291 | + def test_append(self): |
| 292 | + # Test PyFrozenSetWriter_Append() |
| 293 | + items = (1, 2, 3) |
| 294 | + writer = self.create_writer() |
| 295 | + for item in items: |
| 296 | + writer.add(item) |
| 297 | + self.frozenset_equal(writer.finish(), items) |
| 298 | + |
| 299 | + def test_update(self): |
| 300 | + # Test PyFrozenSetWriter_Update() |
| 301 | + writer = self.create_writer() |
| 302 | + writer.update((2, 5)) |
| 303 | + writer.update((5, 7)) |
| 304 | + self.frozenset_equal(writer.finish(), {2, 5, 7}) |
| 305 | + |
| 306 | + def test_finish(self): |
| 307 | + # Test PyFrozenSetWriter_Finish(): track the frozenset in the GC if at |
| 308 | + # least one item is tracked by the GC |
| 309 | + writer = self.create_writer() |
| 310 | + writer.update([2, 5, 7]) |
| 311 | + self.assertFalse(gc.is_tracked(writer.finish())) |
| 312 | + |
| 313 | + class MyObject: |
| 314 | + pass |
| 315 | + |
| 316 | + obj = MyObject() |
| 317 | + writer = self.create_writer() |
| 318 | + writer.add(obj) |
| 319 | + self.assertTrue(gc.is_tracked(writer.finish())) |
| 320 | + |
| 321 | + # test again, now using PyFrozenSetWriter_Update() |
| 322 | + writer = self.create_writer() |
| 323 | + writer.update([obj]) |
| 324 | + self.assertTrue(gc.is_tracked(writer.finish())) |
| 325 | + |
| 326 | + def test_discard(self): |
| 327 | + # Test PyFrozenSetWriter_Discard(): must not leak memory |
| 328 | + writer = self.create_writer() |
| 329 | + writer.update([2, 5, 7]) |
| 330 | + writer.discard() |
| 331 | + |
| 332 | + @threading_helper.requires_working_threading() |
| 333 | + def test_thread(self): |
| 334 | + # PyFrozenSetWriter can be used by multiple threads: it's up to the caller |
| 335 | + # to implement a lock to prevent concurrent accesses. |
| 336 | + writer = self.create_writer() |
| 337 | + |
| 338 | + def thread_func(writer): |
| 339 | + writer.add('Hello') |
| 340 | + writer.update(['World!']) |
| 341 | + |
| 342 | + thread = threading.Thread(target=thread_func, args=(writer,)) |
| 343 | + thread.start() |
| 344 | + threading_helper.join_thread(thread) |
| 345 | + |
| 346 | + self.frozenset_equal(writer.finish(), {'Hello', 'World!'}) |
| 347 | + |
| 348 | + |
276 | 349 | if __name__ == "__main__": |
277 | 350 | unittest.main() |
0 commit comments