Spaces:
Running
Running
| """ | |
| Property-based tests using Hypothesis. | |
| Fuzzes the game engine with random action sequences to detect crashes and invariant violations. | |
| """ | |
| from hypothesis import HealthCheck, given, settings | |
| from hypothesis import strategies as st | |
| from engine.game.game_state import GameState | |
| from engine.tests.framework.state_validators import StateValidator | |
| # Strategy for generating a list of actions (0-800 covers most cases) | |
| # We can refine this to be smarter, but random integers is a good "dumb fuzzer" | |
| action_seq_strategy = st.lists(st.integers(min_value=0, max_value=800), min_size=1, max_size=50) | |
| def test_fuzz_random_actions_no_crash(data, actions): | |
| """ | |
| Fuzz test: execute random valid actions. | |
| If this crashes, it means the engine accepts an action it claimed was legal | |
| but failed to handle it. | |
| """ | |
| # Create a fresh game state | |
| gs = GameState() | |
| # Simple deck setup using loaded data | |
| member_db, live_db = data | |
| available_members = list(member_db.keys()) | |
| if not available_members: | |
| return # Can't test without cards | |
| import random | |
| # Seed random for determinism within hypothesis loop? | |
| # Hypothesis handles its own randomness. We just need a valid start state. | |
| rng = random.Random(42) | |
| for p in gs.players: | |
| p.main_deck = [rng.choice(available_members) for _ in range(20)] | |
| p.hand = [rng.choice(available_members) for _ in range(5)] | |
| p.hand_added_turn = [0] * len(p.hand) | |
| p.energy_zone = [2000] * 5 # Give some energy to allow play | |
| # Check initial validity | |
| StateValidator.assert_valid(gs) | |
| for action in actions: | |
| legal_mask = gs.get_legal_actions() | |
| # Check boundary | |
| if action < 0 or action >= len(legal_mask): | |
| continue | |
| if legal_mask[action]: | |
| try: | |
| # Execute legal action | |
| # Note: step() returns NEW state (copy) | |
| gs = gs.step(action) | |
| # Check invariants | |
| StateValidator.assert_valid(gs) | |
| except Exception as e: | |
| # Crash during LEGAL action is a fail | |
| print(f"CRASH Action {action} in phase {gs.phase}") | |
| print(f"Pending choices: {gs.pending_choices}") | |
| raise e | |