McRogueFace/tests
John McCardle 59e722166a Phase 5.2: performance benchmark suite for grid/entity/FOV/pathfinding
Adds 6 benchmark scripts in tests/benchmarks/ covering all 5 scenarios from
Kanboard #37, plus a shared baseline helper:

  grid_step_bench.py        100 ent / 100x100 grid / 1000 grid.step() rounds
                            mix of IDLE/NOISE8/SEEK/FLEE behaviors
  fov_opt_bench.py          100 ent / 1000x1000 grid; entity.update_visibility()
                            (with DiscreteMap perspective writeback) vs bare
                            grid.compute_fov() (no writeback) across FOV
                            algorithms BASIC/SHADOW/SYMMETRIC_SHADOWCAST and
                            radii 8/16/32
  spatial_hash_bench.py     entities_in_radius() at radii (1,5,10,50) x
                            entity counts (100,1k,10k); compares against
                            naive O(n) baseline with hit-count validation
  pathfinding_bench.py      A* across grid sizes/densities/heuristics/weights,
                            plus with-vs-without `collide=` collision-label
                            comparison (0/10/100 blockers on 100x100)
  gridview_render_bench.py  1/2/4 GridViews on shared grid; uses
                            automation.screenshot() to force real renders in
                            headless mode (mcrfpy.step alone is render-stubbed)
  dijkstra_bench.py         single-root, multi-root, mask, invert, descent
  _baseline.py              writes baseline JSON to baseline/phase5_2/

All scripts emit JSON to stdout and write a baseline copy under
tests/benchmarks/baseline/phase5_2/ for regression comparison. All run
headless; pure time.perf_counter() timing for compute benches, screenshot
wall-time for the render bench (start/end_benchmark would only capture the
no-op headless game loop, so direct timing is used).

Notable findings captured in baselines:
- spatial hash: 5x to >300x speedup over naive O(n), hits validated identical
- update_visibility: ~25-37 ms/entity perspective writeback overhead on
  1000x1000 grid (full-grid demote+promote loop in UIEntity::updateVisibility)
  dominates over the actual TCOD FOV cost (~3-24 ms). Worth a follow-up issue
  for sparse perspective updating.
- gridview render: per-view cost scales near-linearly down (~78ms total for
  1, 2, or 4 views) -- the multi-view system shares state efficiently.

Refs Kanboard #37.
2026-04-18 06:45:40 -04:00
..
api Add Game-to-API Bridge for external client integration 2026-01-29 23:08:26 -05:00
benchmarks Phase 5.2: performance benchmark suite for grid/entity/FOV/pathfinding 2026-04-18 06:45:40 -04:00
cookbook Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
demo Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
docs Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
fixtures Test suite modernization 2026-02-09 08:15:18 -05:00
fuzz Merge W9: fuzz_pathfinding_behavior target 2026-04-10 11:20:29 -04:00
geometry_demo Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
integration Add Phase 5.1 end-to-end scenario test for Grid entity behaviors 2026-04-18 05:44:06 -04:00
notes Timer overhaul: update tests 2026-01-03 22:44:53 -05:00
procgen_interactive Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
regression Replace UIEntity gridstate with DiscreteMap perspective_map; closes #294 2026-04-17 23:04:27 -04:00
unit Replace UIEntity gridstate with DiscreteMap perspective_map; closes #294 2026-04-17 23:04:27 -04:00
vllm_demo update tests: new scene API 2026-01-03 10:59:52 -05:00
all_inputs.py Test suite modernization 2026-02-09 08:15:18 -05:00
conftest.py Add API verification test suite and documentation 2026-01-15 04:05:32 +00:00
debug_viewport.py Test suite modernization 2026-02-09 08:15:18 -05:00
gui.py Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
KNOWN_ISSUES.md Refactor 11 more tests to mcrfpy.step() pattern 2026-01-14 03:09:47 +00:00
procgen_cave2_visualization.py Test suite modernization 2026-02-09 08:15:18 -05:00
procgen_cave_visualization.py Test suite modernization 2026-02-09 08:15:18 -05:00
pytest.ini Test suite modernization: pytest wrapper and runner fixes 2026-01-14 01:54:31 +00:00
README.md Organize test suite: add README, move loose tests to proper directories 2026-01-21 21:34:22 -05:00
run_procgen_interactive.py Test suite modernization 2026-02-09 08:15:18 -05:00
run_tests.py CI for memory safety - updates 2026-03-07 22:33:01 -05:00
shader_poc_test.py Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
shader_toggle_test.py Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
test_mcrogueface.py Test suite modernization: pytest wrapper and runner fixes 2026-01-14 01:54:31 +00:00
wiki_api_verify.py Test suite modernization 2026-02-09 08:15:18 -05:00
wiki_phase_d2_verify.py Test suite modernization 2026-02-09 08:15:18 -05:00
wiki_phase_d3_verify.py Test suite modernization 2026-02-09 08:15:18 -05:00
wiki_phase_d_verify.py Test suite modernization 2026-02-09 08:15:18 -05:00
wiki_snippets_verify.py Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00

McRogueFace Test Suite

Automated tests for the McRogueFace game engine.

Directory Structure

tests/
├── unit/           # Unit tests for individual components (155+ tests)
├── integration/    # Integration tests for system interactions
├── regression/     # Bug regression tests (issue_XX_*.py)
├── benchmarks/     # Performance benchmarks
├── demo/           # Interactive demo system
│   ├── demo_main.py    # Demo runner
│   └── screens/        # Per-feature demo screens
├── conftest.py     # Pytest configuration and fixtures
├── pytest.ini      # Pytest settings
├── run_tests.py    # Standalone test runner
└── KNOWN_ISSUES.md # Test status and mcrfpy.step() documentation

Running Tests

# Run all tests
pytest tests/ -v

# Run specific directory
pytest tests/unit/ -v

# Run tests matching a pattern
pytest tests/ -k "bsp" -v

# Quick run with short timeout (some timeouts expected)
pytest tests/ -q --mcrf-timeout=5

# Full run with longer timeout
pytest tests/ -q --mcrf-timeout=30

# Stop on first failure
pytest tests/ -x

With run_tests.py

# Run all tests
python3 tests/run_tests.py

# Run specific category
python3 tests/run_tests.py unit
python3 tests/run_tests.py regression

# Verbose output
python3 tests/run_tests.py -v

# Quiet (no checksums)
python3 tests/run_tests.py -q

# Custom timeout
python3 tests/run_tests.py --timeout=30

Running individual tests

cd build
./mcrogueface --headless --exec ../tests/unit/some_test.py

Writing Tests

Test Pattern: Synchronous with mcrfpy.step()

Recommended: Use mcrfpy.step(t) to advance simulation time synchronously.

import mcrfpy
import sys

# Setup
scene = mcrfpy.Scene("test")
scene.activate()

# Create UI elements
frame = mcrfpy.Frame(pos=(100, 100), size=(50, 50))
scene.children.append(frame)

# Start animation
frame.animate("x", 500.0, 1.0, mcrfpy.Easing.LINEAR)

# Advance simulation synchronously
mcrfpy.step(1.5)

# Verify results
if abs(frame.x - 500.0) < 0.1:
    print("PASS")
    sys.exit(0)
else:
    print(f"FAIL: frame.x = {frame.x}, expected 500.0")
    sys.exit(1)

Test Pattern: Timer-based (legacy)

For tests that need multiple timer callbacks or complex timing:

import mcrfpy
import sys

def run_test(runtime):
    # Test code here
    print("PASS")
    sys.exit(0)

scene = mcrfpy.Scene("test")
scene.activate()

# Timer fires after 100ms
timer = mcrfpy.Timer("test", run_test, 100)
# Script ends, game loop runs timer

Test Output

  • Print PASS and sys.exit(0) for success
  • Print FAIL with details and sys.exit(1) for failure
  • Tests that timeout are marked as failures

Test Categories

Unit Tests (unit/)

Test individual components in isolation:

  • *_test.py - Standard component tests
  • api_*_test.py - Python API tests
  • automation_*_test.py - Automation module tests

Regression Tests (regression/)

Tests for specific bug fixes, named by issue number:

  • issue_XX_description_test.py

Integration Tests (integration/)

Tests for system interactions and complex scenarios.

Benchmarks (benchmarks/)

Performance measurement tests.

Demo (demo/)

Interactive demonstrations of features. Run with:

cd build
./mcrogueface ../tests/demo/demo_main.py

Tips

  • Read tests as examples: Tests show correct API usage
  • Use mcrfpy.step(): Avoids timeout issues, makes tests deterministic
  • Check KNOWN_ISSUES.md: Documents expected failures and workarounds
  • Screenshots: Use mcrfpy.automation.screenshot("name.png") after mcrfpy.step()

See Also

  • KNOWN_ISSUES.md - Current test status and the mcrfpy.step() pattern
  • conftest.py - Pytest fixtures and custom collector
  • demo/screens/ - Feature demonstrations (good API examples)