| |
| """ |
| OpenEnv Compliance Test Script |
| Tests all requirements for HF Space deployment: |
| 1. Reset endpoint availability |
| 2. Step/state endpoints |
| 3. All tasks enumerable |
| 4. All graders produce scores in [0, 1] |
| 5. Reward in valid range |
| """ |
|
|
| import sys |
| import json |
| from server.my_env_environment import QuantumCircuitEnvironment |
| from models import ActionType, GateType, QuantumAction |
|
|
| def test_reset_endpoint(): |
| """Test reset endpoint with all tasks.""" |
| print("=" * 60) |
| print("TEST 1: Reset Endpoint") |
| print("=" * 60) |
| |
| env = QuantumCircuitEnvironment(seed=42) |
| tasks = ["easy", "medium", "hard", "efficient", "noisy", "budget", "approx"] |
| |
| for task_id in tasks: |
| try: |
| obs = env.reset(config={"task_id": task_id}) |
| assert obs is not None, f"Reset returned None for task {task_id}" |
| assert obs.task_id == task_id, f"Task ID mismatch: {obs.task_id} != {task_id}" |
| assert 0.0 <= obs.score <= 1.0, f"Score out of range: {obs.score}" |
| print(f" ✓ Task '{task_id}': reset OK, score={obs.score:.4f}") |
| except Exception as e: |
| print(f" ✗ Task '{task_id}': FAILED - {e}") |
| return False |
| |
| print(" ✓ All tasks reset successfully\n") |
| return True |
|
|
| def test_step_endpoint(): |
| """Test step endpoint.""" |
| print("=" * 60) |
| print("TEST 2: Step Endpoint") |
| print("=" * 60) |
| |
| env = QuantumCircuitEnvironment(seed=42) |
| obs = env.reset(config={"task_id": "easy"}) |
| |
| |
| action = QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0]) |
| obs = env.step(action) |
| assert obs is not None, "Step returned None" |
| assert hasattr(obs, 'reward'), "Observation missing reward" |
| assert hasattr(obs, 'done'), "Observation missing done" |
| print(f" ✓ ADD action: reward={obs.reward:.4f}, done={obs.done}") |
| |
| |
| action = QuantumAction(action_type=ActionType.REMOVE) |
| obs = env.step(action) |
| print(f" ✓ REMOVE action: reward={obs.reward:.4f}, done={obs.done}") |
| |
| |
| action = QuantumAction(action_type=ActionType.STOP) |
| obs = env.step(action) |
| assert obs.done == True, "STOP should set done=True" |
| print(f" ✓ STOP action: reward={obs.reward:.4f}, done={obs.done}\n") |
| |
| return True |
|
|
| def test_state_endpoint(): |
| """Test state property.""" |
| print("=" * 60) |
| print("TEST 3: State Endpoint") |
| print("=" * 60) |
| |
| env = QuantumCircuitEnvironment(seed=42) |
| obs = env.reset(config={"task_id": "easy"}) |
| |
| state = env.state |
| assert state is not None, "State returned None" |
| assert hasattr(state, 'episode_id'), "State missing episode_id" |
| assert hasattr(state, 'step_count'), "State missing step_count" |
| assert hasattr(state, 'circuit_gates'), "State missing circuit_gates" |
| print(f" ✓ State accessible: episode_id={state.episode_id[:8]}..., steps={state.step_count}") |
| print(f" ✓ State fields: {list(state.__dict__.keys())}\n") |
| |
| return True |
|
|
| def test_all_tasks_with_graders(): |
| """Test all tasks and verify grader scores.""" |
| print("=" * 60) |
| print("TEST 4: All Tasks + Graders (3+ tasks)") |
| print("=" * 60) |
| |
| env = QuantumCircuitEnvironment(seed=42) |
| tasks = ["easy", "medium", "hard", "efficient", "noisy", "budget", "approx"] |
| |
| print(f" Total tasks: {len(tasks)}") |
| assert len(tasks) >= 3, f"Need at least 3 tasks, found {len(tasks)}" |
| |
| for task_id in tasks: |
| obs = env.reset(config={"task_id": task_id}) |
| |
| |
| assert 0.0 <= obs.score <= 1.0, f"Score out of range: {obs.score}" |
| assert 0.0 <= obs.fidelity <= 1.0, f"Fidelity out of range: {obs.fidelity}" |
| |
| |
| action = QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[0]) |
| obs = env.step(action) |
| |
| |
| assert 0.0 <= obs.score <= 1.0, f"Score out of range after step: {obs.score}" |
| assert 0.0 <= obs.fidelity <= 1.0, f"Fidelity out of range: {obs.fidelity}" |
| |
| |
| if obs.metadata: |
| for key, value in obs.metadata.items(): |
| if key.endswith('_score') and isinstance(value, (int, float)): |
| assert 0.0 <= value <= 1.0, f"{key} out of range: {value}" |
| |
| print(f" ✓ Task '{task_id}': fid={obs.fidelity:.4f}, score={obs.score:.4f}, reward={obs.reward:.4f}") |
| |
| print(f" ✓ All {len(tasks)} tasks passed grader validation\n") |
| return True |
|
|
| def test_reward_range(): |
| """Test that rewards are in valid range.""" |
| print("=" * 60) |
| print("TEST 5: Reward Range") |
| print("=" * 60) |
| |
| env = QuantumCircuitEnvironment(seed=42) |
| obs = env.reset(config={"task_id": "easy"}) |
| |
| rewards = [] |
| for i in range(10): |
| if obs.done: |
| break |
| action = QuantumAction(action_type=ActionType.ADD, gate=GateType.H, qubits=[i % 2]) |
| obs = env.step(action) |
| rewards.append(obs.reward) |
| |
| for i, r in enumerate(rewards): |
| |
| assert -2.0 <= r <= 2.0, f"Reward {i} out of reasonable range: {r}" |
| print(f" Step {i+1}: reward={r:+.4f}") |
| |
| print(f" ✓ All rewards in valid range\n") |
| return True |
|
|
| def test_openenv_yaml(): |
| """Test openenv.yaml compliance.""" |
| print("=" * 60) |
| print("TEST 6: openenv.yaml Compliance") |
| print("=" * 60) |
| |
| import yaml |
| with open("openenv.yaml", "r") as f: |
| config = yaml.safe_load(f) |
| |
| required_fields = ["spec_version", "name", "type", "runtime", "app", "port", "tasks"] |
| for field in required_fields: |
| assert field in config, f"Missing required field: {field}" |
| print(f" ✓ {field}: {config[field]}") |
| |
| |
| assert len(config["tasks"]) >= 3, f"Need at least 3 tasks in openenv.yaml" |
| print(f" ✓ Tasks defined: {len(config['tasks'])}") |
| |
| |
| assert "action_schema" in config, "Missing action_schema" |
| assert "observation_schema" in config, "Missing observation_schema" |
| print(f" ✓ Schemas defined\n") |
| |
| return True |
|
|
| def main(): |
| """Run all compliance tests.""" |
| print("\n" + "=" * 60) |
| print("OPENENV COMPLIANCE TEST SUITE") |
| print("=" * 60 + "\n") |
| |
| tests = [ |
| ("Reset Endpoint", test_reset_endpoint), |
| ("Step Endpoint", test_step_endpoint), |
| ("State Endpoint", test_state_endpoint), |
| ("Tasks + Graders", test_all_tasks_with_graders), |
| ("Reward Range", test_reward_range), |
| ("openenv.yaml", test_openenv_yaml), |
| ] |
| |
| results = [] |
| for name, test_func in tests: |
| try: |
| result = test_func() |
| results.append((name, result)) |
| except Exception as e: |
| print(f" ✗ {name} FAILED: {e}\n") |
| results.append((name, False)) |
| |
| |
| print("=" * 60) |
| print("SUMMARY") |
| print("=" * 60) |
| passed = sum(1 for _, r in results if r) |
| total = len(results) |
| |
| for name, result in results: |
| status = "✓ PASS" if result else "✗ FAIL" |
| print(f" {status}: {name}") |
| |
| print(f"\nTotal: {passed}/{total} tests passed") |
| |
| if passed == total: |
| print("\n🎉 ALL COMPLIANCE TESTS PASSED!") |
| return 0 |
| else: |
| print(f"\n❌ {total - passed} test(s) failed") |
| return 1 |
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|