McRogueFace/tests/regression
John McCardle 55968d4c09 fix(bindings): pin a subclassed drawable's wrapper while a collection owns it
PythonObjectCache holds weakrefs, so #369 preserved object identity only
while Python still held a reference to the wrapper. Drop that reference
while C++ still owns the object, and the wrapper is collected; the next
.parent / find() / indexing lookup misses the cache and allocates a fresh
BASE-type wrapper. The subclass, and every attribute the user set on it,
are silently gone:

    del parent; gc.collect()
    type(kid.parent)   # mcrfpy.Frame, not MyFrame
    kid.parent.hp      # AttributeError

Entities already solve this with a strong ref from the C++ object back to
its wrapper, pinned for as long as the entity is in a grid (#266). #373
noted drawables had no equivalent boundary. They do: membership in a
children collection. It is already represented by the parent link, and
setParent()/setParentScene() are the choke points every collection mutator
goes through -- so that is where the pin is taken and dropped.

Only subclassed drawables are pinned. A base-type wrapper carries no state
of its own (the types have no __dict__), so re-creating one on a cache miss
is unobservable, and pinning every drawable ever created would cost memory
for nothing.

The pin is a reference cycle (wrapper -> shared_ptr -> drawable -> wrapper)
that Python's GC cannot see through the C++ side, so it must be released on
the way out or the object leaks forever. Every exit is covered:

  * unlinking calls setParent(nullptr), which drops the pin;
  * removeFromParent() re-evaluates it as its LAST statement, so releasing
    the last reference to the wrapper -- whose dealloc drops the wrapper's
    shared_ptr to the drawable -- cannot touch a freed `this`;
  * an owner destroyed while it still holds children releases their pins
    first (releaseChildPins), since nothing unlinks them on that path: the
    vector simply dies. Without it the whole subtree would be stranded.

Scene gains the virtual destructor it needed for that release -- it was a
polymorphic base that GameEngine deletes through a Scene*, so it needed one
regardless.

Depends on #377: the pin keys off the parent link, which four of six
collection mutators did not maintain.

closes #373

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 07:53:42 -04:00
..
frame_children_mutation_test.py Add regression tests for Frame.children mutation and parent=None removal 2026-04-01 11:27:47 -04:00
grid_backward_compat_test.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_9_rendertexture_resize_test.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_37_simple_test.py update tests: new scene API 2026-01-03 10:59:52 -05:00
issue_76_test.py Test suite modernization 2026-02-09 08:15:18 -05:00
issue_79_color_properties_test.py Test suite modernization 2026-02-09 08:15:18 -05:00
issue_99_texture_font_properties_test.py Test suite modernization 2026-02-09 08:15:18 -05:00
issue_123_chunk_system_test.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_146_fov_returns_none.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_147_grid_layers.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_148_layer_dirty_flags.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_176_entity_position_test.py Phase 2: Entity data model extensions for behavior system 2026-03-15 22:05:06 -04:00
issue_177_gridpoint_grid_pos_test.py Test suite modernization 2026-02-09 08:15:18 -05:00
issue_179_181_grid_vectors_test.py Remove redundant Grid.position alias, keep only Grid.pos, closes #308 2026-04-09 22:18:30 -04:00
issue_180_timer_orphan_test.py Organize test suite: add README, move loose tests to proper directories 2026-01-21 21:34:22 -05:00
issue_180_timer_stopped_test.py Organize test suite: add README, move loose tests to proper directories 2026-01-21 21:34:22 -05:00
issue_182_caption_size_test.py Organize test suite: add README, move loose tests to proper directories 2026-01-21 21:34:22 -05:00
issue_183_parent_quirks_test.py tests for the last few issues (these test scripts should work with recent APIs, while the rest of the test suite needs an overhaul) 2026-01-08 10:31:21 -05:00
issue_184_189_module_test.py Organize test suite: add README, move loose tests to proper directories 2026-01-21 21:34:22 -05:00
issue_185_188_bounds_test.py Organize test suite: add README, move loose tests to proper directories 2026-01-21 21:34:22 -05:00
issue_190_layer_docs_test.py Test suite modernization 2026-02-09 08:15:18 -05:00
issue_212_to_217_test.py Add input validation and fix Entity repr 2026-01-16 19:18:27 -05:00
issue_223_clip_children_test.py Fix UIFrame RenderTexture positioning and toggling issues 2026-01-22 22:54:50 -05:00
issue_251_callback_gc_test.py Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
issue_251_timer_gc_test.py Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
issue_253_spatial_hash_test.py Phase 1: Safety & performance foundation for Grid/Entity overhaul 2026-03-15 21:48:24 -04:00
issue_254_tilelayer_texture_test.py Phase 1: Safety & performance foundation for Grid/Entity overhaul 2026-03-15 21:48:24 -04:00
issue_256_animation_spatial_hash_test.py Update spatial hash in animation setProperty for entity position, closes #256 2026-04-10 01:40:29 -04:00
issue_258_gridstate_resize_test.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_264_gridpoint_dangle_test.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_266_entity_lifecycle_test.py Remove entity self-reference cycle 2026-03-07 23:22:58 -05:00
issue_266_subclass_identity_test.py Preserve Python subclass identity for entities in grids (reopens #266) 2026-03-09 00:24:26 -04:00
issue_267_275_refcount_test.py Add regression tests for GridPointState dangle and refcount leaks, refs #287 2026-04-10 02:06:10 -04:00
issue_268_vector_null_test.py Add regression tests for vector NULL safety and uniform owner validity, closes #287 2026-04-10 02:57:32 -04:00
issue_270_277_parent_grid_dangle_test.py Null parent_grid pointers in GridData destructor, closes #270, closes #271, closes #277 2026-04-10 01:34:33 -04:00
issue_272_uniform_owner_test.py Add regression tests for vector NULL safety and uniform owner validity, closes #287 2026-04-10 02:57:32 -04:00
issue_273_die_during_iteration_test.py Add regression test for entity.die() during iteration, refs #273 2026-04-10 01:45:23 -04:00
issue_274_set_grid_spatial_hash_test.py Add missing spatial_hash.insert() in set_grid() grid transfer, closes #274 2026-04-10 01:45:18 -04:00
issue_288_291_dirty_flags_test.py Add missing markDirty()/markCompositeDirty() to all Python property setters 2026-04-10 01:01:41 -04:00
issue_292_fov_dedup_test.py Phase 1: Safety & performance foundation for Grid/Entity overhaul 2026-03-15 21:48:24 -04:00
issue_294_perspective_map_test.py Replace UIEntity gridstate with DiscreteMap perspective_map; closes #294 2026-04-17 23:04:27 -04:00
issue_295_cell_pos_test.py Phase 2: Entity data model extensions for behavior system 2026-03-15 22:05:06 -04:00
issue_307_color_eq_test.py Fix audit type count (44->46) and add regression test for Color __eq__, refs #307 2026-04-09 21:45:12 -04:00
issue_309_caption_numeric_setter_test.py Clamp Caption numeric setters to prevent UBSan float->uint UB 2026-04-16 23:31:58 -04:00
issue_310_compute_fov_enum_test.py Route Grid.compute_fov algorithm through PyFOV::from_arg 2026-04-16 23:32:09 -04:00
issue_311_dijkstra_bounds_test.py Bounds-check DijkstraMap coordinates at the Python boundary 2026-04-16 23:32:19 -04:00
issue_313_entity_grid_data_test.py refactor(grid): GridData is a map, not a widget -- delete UIGrid's drawable half 2026-07-13 07:14:43 -04:00
issue_315_path_provider_test.py Refactor EntityBehavior SEEK/FLEE to use PathProvider strategy; refs #315 2026-04-18 09:19:05 -04:00
issue_316_sparse_perspective_test.py Windowed perspective writeback in UIEntity::updateVisibility; closes #316 2026-06-21 09:40:05 -04:00
issue_317_scroll_position_test.py Fix verify-pass code bugs #317/#318/#319 2026-06-21 10:12:41 -04:00
issue_318_gridview_texture_test.py Fix verify-pass code bugs #317/#318/#319 2026-06-21 10:12:41 -04:00
issue_319_visible_entities_radius_none_test.py Fix verify-pass code bugs #317/#318/#319 2026-06-21 10:12:41 -04:00
issue_320_caption_positional_font_test.py Add read-only Caption.font getter; addresses #320 2026-06-21 12:18:53 -04:00
issue_322_terrain_enum_bad_utf8_test.py Check Python C-API returns in terrain_enum builders; closes #322 2026-06-26 23:52:38 -04:00
issue_323_pitch_shift_nonfinite_test.py Reject non-finite floats before int casts; closes #323, closes #324, closes #325 2026-06-26 23:53:02 -04:00
issue_324_hsl_shift_nonfinite_test.py Reject non-finite floats before int casts; closes #323, closes #324, closes #325 2026-06-26 23:53:02 -04:00
issue_325_vector_int_nonfinite_test.py Reject non-finite floats before int casts; closes #323, closes #324, closes #325 2026-06-26 23:53:02 -04:00
issue_329_entity_index_perf_test.py Add tests pinning EntityCollection semantics and O(1) indexing (#329) 2026-07-02 20:13:18 -04:00
issue_332_soa_storage_test.py refactor(grid): GridData SoA -- dense uint8 planes for walkable/transparent; closes #332 2026-07-11 01:43:30 -04:00
issue_334_discretemap_buffer_test.py feat(discretemap): buffer protocol for zero-copy numpy views; closes #334 2026-07-11 01:00:53 -04:00
issue_335_layer_edit_test.py feat(grid): numpy buffer views for ColorLayer/TileLayer via layer.edit(); closes #335 2026-07-11 02:04:42 -04:00
issue_338_drawable_footprint_test.py perf(grid): lazily allocate rotationTexture; footprint measurement; addresses #338 2026-07-11 01:31:18 -04:00
issue_341_metrics_counters_test.py fix(metrics): report counters Python can actually observe 2026-07-14 06:37:33 -04:00
issue_342_animation_float_fastpath_test.py perf(animation): scalar-float fast path skipping variant visits; closes #342 2026-07-10 22:59:28 -04:00
issue_344_key_enum_cache_test.py perf(input): cache Key/InputState enum members per event; closes #344 2026-07-10 22:59:47 -04:00
issue_348_gridview_wrapper_identity_test.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
issue_350_step_frame_parity_test.py fix(engine): headless step() is a full simulation frame; --exec can no longer hang 2026-07-14 06:37:52 -04:00
issue_351_gridview_early_out_test.py perf(grid): clean-state render early-out for UIGridView; closes #351 2026-07-11 00:55:32 -04:00
issue_351_step_movement_render_test.py fix(grid): turn manager invalidates view render cache on entity move; addresses #351 2026-07-11 10:15:36 -04:00
issue_355_camera_rotation_hit_test.py fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
issue_355_grid_cell_dispatch_test.py fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
issue_355_grid_input_test.py fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
issue_355_gridview_position_sync_test.py fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
issue_355_perspective_overlay_test.py fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
issue_356_module_attrs_discoverable_test.py fix(bindings): make dynamic module attributes dir()-discoverable 2026-07-13 23:47:04 -04:00
issue_357_find_in_grid_test.py fix(grid): find()/findAll() descend into grids again 2026-07-12 18:02:04 -04:00
issue_359_shared_griddata_test.py refactor(grid): GridData is a map, not a widget -- delete UIGrid's drawable half 2026-07-13 07:14:43 -04:00
issue_361_griddata_is_not_a_widget_test.py refactor(grid): GridData is a map, not a widget -- delete UIGrid's drawable half 2026-07-13 07:14:43 -04:00
issue_363_hover_exit_on_window_leave_test.py fix(input): dispatch hover-exit when the cursor leaves the window 2026-07-12 21:12:58 -04:00
issue_364_grid_children_on_view_test.py fix(bindings): route every drawable wrapper through PythonObjectCache 2026-07-13 23:37:01 -04:00
issue_365_stale_clicked_cell_test.py fix(grid): never dispatch a stale cell from UIGridView::click_at 2026-07-12 20:26:59 -04:00
issue_366_collection_repr_grid_test.py fix(grid): UICollection.repr names Grids again instead of "UIDrawable" 2026-07-12 20:26:32 -04:00
issue_367_hover_exit_on_focus_loss_test.py fix(render): content invalidation propagates again; clear hover on focus loss 2026-07-13 07:40:20 -04:00
issue_368_cache_subtree_content_dirty_test.py fix(render): content invalidation propagates again; clear hover on focus loss 2026-07-13 07:40:20 -04:00
issue_369_parent_identity_test.py fix(bindings): route every drawable wrapper through PythonObjectCache 2026-07-13 23:37:01 -04:00
issue_373_subclass_identity_survives_gc_test.py fix(bindings): pin a subclassed drawable's wrapper while a collection owns it 2026-07-14 07:53:42 -04:00
issue_377_collection_parent_link_test.py fix(ui): maintain the parent link in every collection mutator 2026-07-14 07:53:21 -04:00
issue_callback_refcount_test.py Simplify on_enter/on_exit callbacks to position-only signature 2026-01-28 17:36:02 -05:00
parent_none_removal_test.py Add regression tests for Frame.children mutation and parent=None removal 2026-04-01 11:27:47 -04:00
recursive_animation_callback_segfault.py test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -04:00
subclass_callback_segfault_test.py Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
test_type_preservation_solution.py Test suite modernization 2026-02-09 08:15:18 -05:00