Skip to content

Fix exception-by-pointer leak; add missing test coverage - #32

Merged
dmccoystephenson merged 1 commit into
masterfrom
feature/fix-exception-leak-and-test-coverage
Aug 3, 2026
Merged

Fix exception-by-pointer leak; add missing test coverage#32
dmccoystephenson merged 1 commit into
masterfrom
feature/fix-exception-leak-and-test-coverage

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

  • `Grid::getLocationByCoordinates`, `Grid::getLocation`, `Environment::getFirstEntity`, and `Environment::getEntity` were throwing `new std::runtime_error(...)` (a heap-allocated pointer) instead of the exception object itself; the corresponding catch site in `Environment::moveEntityInDirection` caught by pointer, so the allocated exception was never deleted. All four throw sites now throw by value, and the catch site now catches `const std::runtime_error&`.
  • Direct test coverage was added for previously-untested public methods identified in Missing test coverage for several public methods #28: `Environment::setName`, `Environment::printInfo`, `Environment::addEntityToLocation`, `Grid::setSize`, `Grid::addLocation`, `Grid::getLocationByCoordinates`, `Location::getEntities()`, and `Location::getNumEntities()`.
  • `Grid::removeLocation` was intentionally left without new test coverage in this PR — writing a direct test surfaced a pre-existing iterator-invalidation bug in that method (reliable undefined behavior, confirmed both in a plain build and under AddressSanitizer, not a clean assertion failure). That defect is filed separately as Grid::removeLocation invalidates its iterator after erase, causing undefined behavior #31 with a proposed fix, so a regression test can land alongside the fix rather than in this coverage-focused change. A comment was left in `tests.cpp` at the point where that test would otherwise live, and Missing test coverage for several public methods #28 was commented with this explanation.

Test plan

  • `make` builds cleanly
  • All 34 registered tests in `tests_executable` pass (exit 0)
  • Regression for the leak fix confirmed empirically: with the fix reverted (`git stash`), a debug build compiled with `-fsanitize=address` and `ASAN_OPTIONS=detect_leaks=1` reports leaks originating from the four throw sites; with the fix restored, the same build and test run reports no leaks
  • All newly-added tests pass; `Grid::removeLocation` intentionally not exercised by a new test (see above)

Closes #29
Closes #28

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

Grid::getLocationByCoordinates, Grid::getLocation, Environment::getFirstEntity,
and Environment::getEntity threw new std::runtime_error(...) (heap pointer),
leaking the exception object on every "not found" path. Now throw by value and
catch by const reference at the one catch site in
Environment::moveEntityInDirection.

Also adds direct test coverage for several previously-untested public methods:
Environment::setName/printInfo/addEntityToLocation, Grid::setSize/addLocation/
getLocationByCoordinates, and Location::getEntities/getNumEntities.

Grid::removeLocation is deliberately left without a new test: writing one
surfaced a pre-existing iterator-invalidation bug (reliable UB, not a clean
assertion failure), filed separately as #31.

Closes #29
Closes #28

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review rubric:

  • Scope: PASS — every changed line is necessary for Exception-by-pointer pattern leaks the heap-allocated exception object #29 (throw-by-value fix, 4 sites + 1 catch) or Missing test coverage for several public methods #28 (new test coverage); no unrelated formatting/renames.
  • Tests-new: PASS — every method added in this PR's scope (Environment::setName/printInfo/addEntityToLocation, Grid::setSize/addLocation/getLocationByCoordinates, Location::getEntities/getNumEntities) has a dedicated assert-based test.
  • Tests-fix (empirical): PASS — the fix was stashed (git stash push -- src/environment.cpp src/grid.cpp), a debug build with -fsanitize=address + ASAN_OPTIONS=detect_leaks=1 was run, and LeakSanitizer reported 2124 bytes leaked across 72 allocations originating from the four throw sites (confirmed FAIL). The stash was popped and the same ASan build/run reported zero leaks (confirmed PASS).
  • Sibling structure: PASS — new test functions follow the existing void test<Behavior>() / assert() / "Test N - ..." + "--- Success" pattern used by every neighboring test in tests.cpp.
  • Sibling renames: N/A — no identifier renames in this PR.
  • Docs: PASS — README's class table lists only Entity/Environment/Grid/Location with unchanged descriptions; no class or public surface changed, so no drift introduced.
  • Issue resolution: PASS for Exception-by-pointer pattern leaks the heap-allocated exception object #29 (all 4 throw sites + catch site changed). PARTIAL-but-documented for Missing test coverage for several public methods #28: 8 of 9 named methods got direct tests; Grid::removeLocation was deliberately skipped after its test reliably reproduced undefined behavior (confirmed under a plain build and under ASan) rather than a clean assertion failure — filed as Grid::removeLocation invalidates its iterator after erase, causing undefined behavior #31, with a comment left in tests.cpp and a comment posted on Missing test coverage for several public methods #28 explaining the gap.
  • Local test suite: PASS — make builds cleanly; all 34 registered tests print --- Success in order; process exits 0.

Repo-specific:

  • Header/definition parity: N/A — no new methods or signature changes; only exception-throwing statements and a catch clause changed body-internally.
  • Include guards: N/A — no new header files.
  • Test registration: PASS — all 8 new test*() functions are called from main(); verified against main()'s call list.

Summary: one item is a deliberate partial per the "characterization, not change" rule — Grid::removeLocation's iterator-invalidation bug (#31) surfaced while writing its test, and the test-expansion policy says not to fix production code discovered incidentally in a coverage cycle. No other findings.

This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exception-by-pointer pattern leaks the heap-allocated exception object Missing test coverage for several public methods

1 participant