test: harden entity recycling with stress and edge-case tests#20
test: harden entity recycling with stress and edge-case tests#20gauvainw wants to merge 1 commit into
Conversation
Add stress tests for entity pool (1M create/destroy cycles, generation increment validation, batch create/destroy) and entity encoding limits (max index, max generation). Add world-level recycling tests: stress create/destroy with components, stale reference access after multiple recycles, clean component state on recycled entities, batch recycling with query consistency checks. All tests pass with -race enabled. Closes #13
There was a problem hiding this comment.
Pull request overview
Adds stress and edge-case test coverage for the ECS entity recycling (generation-based) mechanism at both the entity pool and world levels, aligned with the recycling hardening work in internal/ecs/.
Changes:
- Add high-iteration stress tests for entity pool alloc/free recycling, generation increments, stale references, free-list order, and uint32 boundary encoding.
- Add world-level recycling tests validating stale entity access, component cleanup on recycle, and query consistency through destroy/recreate cycles.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| internal/ecs/entity_test.go | Adds entity pool stress and edge-case tests (recycling, generations, free-list order, boundary encoding). |
| internal/ecs/world_test.go | Adds world-level recycling stress/stale access/component cleanup/query consistency tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const cycles = 1_000_000 | ||
| for i := range cycles { | ||
| e := pool.Alloc() | ||
| if !pool.IsAlive(e) { | ||
| t.Fatalf("cycle %d: freshly allocated entity should be alive", i) | ||
| } | ||
| if !pool.Free(e) { | ||
| t.Fatalf("cycle %d: free of alive entity should succeed", i) | ||
| } | ||
| if pool.IsAlive(e) { | ||
| t.Fatalf("cycle %d: freed entity should be dead", i) | ||
| } | ||
| } |
There was a problem hiding this comment.
TestEntityPool_StressCreateDestroy runs 1,000,000 alloc/free cycles unconditionally. In CI this repo runs go test -race for all packages, and this kind of stress loop can significantly slow or time out the pipeline. Consider gating the high iteration count behind testing.Short() (skip or reduce cycles), or lowering the default cycles and making the full stress run opt-in (e.g., via a build tag).
| t.Fatalf("cycle %d: destroyed entity should be dead", i) | ||
| } | ||
|
|
||
| // Get on dead entity must return zero value and false. |
There was a problem hiding this comment.
This comment says “Get on dead entity must return zero value and false”, but Get[T] actually returns nil, false when the entity is not alive. Updating the comment to match the API behavior will avoid confusion for future readers.
| // Get on dead entity must return zero value and false. | |
| // Get on dead entity must return nil and false. |
| if e.Generation() != uint32(i) { | ||
| t.Fatalf("cycle %d: expected generation %d, got %d", i, i, e.Generation()) | ||
| } | ||
| pool.Free(e) |
There was a problem hiding this comment.
pool.Free(e) return value is ignored here. While the next iteration will likely fail if Free doesn’t succeed, asserting the return value directly makes the test failure clearer and pins down the exact cycle where freeing broke.
| pool.Free(e) | |
| if !pool.Free(e) { | |
| t.Fatalf("cycle %d: free of entity should succeed", i) | |
| } |
What changed and why
Stress tests and edge-case tests for the entity recycling (generation-based) system.
New entity pool tests (entity_test.go)
New world-level recycling tests (world_test.go)
All tests pass with
-raceenabled.How to test
Related issue
Closes #13