Files
Experiment_ReinforcementLea…/Training/tests/test_lander_cli_env.py

104 lines
3.0 KiB
Python

"""Unit tests for LanderCliEnv."""
from pathlib import Path
import numpy as np
import pytest
from gymnasium.utils.env_checker import check_env
# Add parent dir to sys.path so we can import lander_cli_env
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from lander_cli_env import LanderCliEnv
REPO_ROOT = Path(__file__).resolve().parents[2]
CLI_BINARY = REPO_ROOT / "publish" / "GameCli" / "GameCli"
@pytest.fixture(scope="module")
def cli_binary_exists():
if not CLI_BINARY.exists():
pytest.skip(f"CLI binary not found at {CLI_BINARY} — run `dotnet publish GameCli -c Release -o publish/GameCli`")
return CLI_BINARY
def test_env_opens_and_closes(cli_binary_exists):
env = LanderCliEnv(cli_binary=str(cli_binary_exists))
env.close() # should not raise
def test_observation_space_is_seven_dim(cli_binary_exists):
env = LanderCliEnv(cli_binary=str(cli_binary_exists))
try:
assert env.observation_space.shape == (7,)
finally:
env.close()
def test_action_space_is_discrete_four(cli_binary_exists):
env = LanderCliEnv(cli_binary=str(cli_binary_exists))
try:
assert env.action_space.n == 4
finally:
env.close()
def test_reset_returns_seven_dim_observation(cli_binary_exists):
env = LanderCliEnv(cli_binary=str(cli_binary_exists))
try:
obs, info = env.reset(seed=42)
assert isinstance(obs, np.ndarray)
assert obs.shape == (7,)
assert obs.dtype == np.float32
finally:
env.close()
def test_step_advances_and_returns_five_tuple(cli_binary_exists):
env = LanderCliEnv(cli_binary=str(cli_binary_exists))
try:
env.reset(seed=0)
obs, reward, terminated, truncated, info = env.step(0) # noop
assert obs.shape == (7,)
assert isinstance(reward, float)
assert terminated is False # never terminates
assert isinstance(truncated, bool)
finally:
env.close()
def test_reset_randomizes_target_across_episodes(cli_binary_exists):
env = LanderCliEnv(cli_binary=str(cli_binary_exists))
try:
# Two resets with different seeds should generally give different targets.
env.reset(seed=1)
target1 = env._target
env.reset(seed=2)
target2 = env._target
assert target1 != target2
finally:
env.close()
def test_truncation_at_max_steps(cli_binary_exists):
env = LanderCliEnv(cli_binary=str(cli_binary_exists), max_episode_steps=5)
try:
env.reset(seed=0)
truncated = False
for _ in range(5):
_, _, _, truncated, _ = env.step(0)
assert truncated is True
finally:
env.close()
def test_check_env_passes(cli_binary_exists):
"""Gymnasium's own env sanity checker — spaces, dtypes, reset/step contract."""
env = LanderCliEnv(cli_binary=str(cli_binary_exists), max_episode_steps=50)
try:
# skip_render_check because we don't implement rendering
check_env(env.unwrapped, skip_render_check=True)
finally:
env.close()