McRogueFace/tests
John McCardle 43a6cc376b fix(engine): UTF-8 filesystem encoding, --run-forever in --help, honest step() docs
Three documentation-adjacent defects, found while auditing what the July bugfix
batch obliged the docs to say.

#378 -- init_python_with_config(), the init path main.cpp actually uses, did no
pre-initialization at all. PyPreConfig.utf8_mode was never enabled, so the
filesystem encoding fell back to ASCII while sys.getdefaultencoding() and the
locale both reported UTF-8. open() with no explicit encoding= therefore could not
read a UTF-8 file:

    open("notes.py").read()
    # UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2

In any normal CPython 3, open() defaults to UTF-8. This broke any script reading a
data file, a save file, or its own source. init_python() -- the other init path --
had always set utf8_mode = 1; only the live path was missing it.

This also corrects the long-standing folklore that "--exec scripts must be
ASCII-only". They need not be, and never did: the C++ side reads the file and hands
the bytes to Python, which parses them as UTF-8 per PEP 3120. The folklore was
pointing at open(), via harnesses that read scripts themselves.

--run-forever (#350) parsed but was absent from print_help(), which lists every
other McRogueFace flag. The only place a user could learn it existed was the
runtime error printed after their script had already failed. CLI flags are not in
the API manifest, so nothing flagged the omission.

mcrfpy.step()'s docstring still read "Advance simulation time" -- accurate until
30abb0b made step() a full simulation frame (scene update, Python Scene.update(),
timers, animations, transition completion, frame metrics, currentFrame++). It now
says so, says why render and input are deliberately excluded, and states the
sys.exit() requirement that --run-forever exists to waive. The most-changed
semantic in the batch had the least-changed documentation.

closes #378
2026-07-14 18:21:19 -04:00
..
api Add Game-to-API Bridge for external client integration 2026-01-29 23:08:26 -05:00
benchmarks feat(bench): Gauntlet OOM safety guards + The Crucible wall-clock benchmark; closes #353 closes #354 2026-07-11 10:28:13 -04:00
cookbook Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
demo fix(demos): unrot tests/demo and gate it in CI 2026-07-14 06:46:03 -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 Fold Tier C surface into existing fuzz targets; closes #312 2026-06-21 16:45:03 -04:00
geometry_demo Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
integration test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -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 fix(engine): UTF-8 filesystem encoding, --run-forever in --help, honest step() docs 2026-07-14 18:21:19 -04:00
snapshots fix(bindings): make dynamic module attributes dir()-discoverable 2026-07-13 23:47:04 -04:00
unit test(suite): unrot 82 tests that were passing without ever running 2026-07-14 07:29:23 -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 fix(demos): unrot tests/demo and gate it in CI 2026-07-14 06:46:03 -04: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)