Test tracking allocations in a native extension which spawns multiple threads, each thread allocating and freeing memory.
(tmpdir, monkeypatch)
| 19 | |
| 20 | @pytest.mark.valgrind |
| 21 | def test_multithreaded_extension(tmpdir, monkeypatch): |
| 22 | """Test tracking allocations in a native extension which spawns multiple threads, |
| 23 | each thread allocating and freeing memory.""" |
| 24 | # GIVEN |
| 25 | output = Path(tmpdir) / "test.bin" |
| 26 | extension_name = "multithreaded_extension" |
| 27 | extension_path = tmpdir / extension_name |
| 28 | shutil.copytree(TEST_MULTITHREADED_EXTENSION, extension_path) |
| 29 | subprocess.run( |
| 30 | [sys.executable, str(extension_path / "setup.py"), "build_ext", "--inplace"], |
| 31 | check=True, |
| 32 | cwd=extension_path, |
| 33 | capture_output=True, |
| 34 | ) |
| 35 | |
| 36 | # WHEN |
| 37 | with monkeypatch.context() as ctx: |
| 38 | ctx.setattr(sys, "path", [*sys.path, str(extension_path)]) |
| 39 | from testext import run # type: ignore |
| 40 | |
| 41 | with Tracker(output): |
| 42 | run() |
| 43 | |
| 44 | # THEN |
| 45 | records = list(FileReader(output).get_allocation_records()) |
| 46 | assert records |
| 47 | |
| 48 | memaligns = [ |
| 49 | record for record in records if record.allocator == AllocatorType.POSIX_MEMALIGN |
| 50 | ] |
| 51 | assert len(memaligns) == 100 * 100 # 100 threads allocate 100 times in testext |
| 52 | |
| 53 | # We don't keep track of the native stacks. Make sure they are empty |
| 54 | assert all(len(memalign.stack_trace()) == 0 for memalign in memaligns) |
| 55 | |
| 56 | memaligns_addr = {record.address for record in memaligns} |
| 57 | memalign_frees = [ |
| 58 | record |
| 59 | for record in records |
| 60 | if record.address in memaligns_addr and record.allocator == AllocatorType.FREE |
| 61 | ] |
| 62 | |
| 63 | assert len(memalign_frees) >= 100 * 100 |
| 64 | |
| 65 | |
| 66 | def test_misbehaving_extension(tmpdir, monkeypatch): |
nothing calls this directly
no test coverage detected
searching dependent graphs…