""" End-to-End and Regression Test Suite for MathSolver Pipeline. Verifies: 1. DSL Parser + Geometry Engine + Geometry Validator integration. 2. Canonical 3D solids (Pyramid, Cube, Prism, Cone) coordinate solving & invariants. 3. GeometryStatus propagation (VALID, DEGRADED, FAILED). 4. Machine-readable structured error feedback for LLM repair loops. 5. Evaluation framework components and metrics calculation. """ from __future__ import annotations import pytest from eval.benchmark import BenchmarkDataset, BenchmarkSample from eval.metrics import compute_cer, compute_wer, latex_match from eval.runner import EvalRunner from solver.dsl_parser import DSLParser from solver.engine import GeometryEngine from solver.validator import GeometryStatus, GeometryValidator, StructuredError, ValidationResult def test_metric_calculations(): """Verify CER, WER, and LaTeX matching functions.""" # CER assert compute_cer("SA = 6", "SA = 6") == 0.0 assert compute_cer("SA = 6", "SA = 8") == pytest.approx(1 / 6) assert compute_cer("", "") == 0.0 # WER assert compute_wer("Cho hình vuông ABCD", "Cho hình vuông ABCD") == 0.0 assert compute_wer("Cho hình vuông ABCD", "Cho hình chữ nhật ABCD") == pytest.approx(2 / 4) # LaTeX matching assert latex_match("\\frac{1}{3} \\cdot S \\cdot h", "\\frac{1}{3} * S * h") assert latex_match("S_{ABCD}", "S_{ABCD}") def test_validator_structured_feedback(): """Verify that ValidationResult properly generates structured error feedback.""" err = StructuredError( error_type="constraint_violation", constraint="Length constraint violated", expected="AB = 4", actual="AB = 5", instruction="Correct the DSL length values.", ) res = ValidationResult( is_valid=False, errors=["Length constraint violated: |AB| expected 4.00, got 5.00"], status=GeometryStatus.FAILED, structured_errors=[err], ) fb = res.to_structured_feedback() assert fb["status"] == "failed" assert fb["error_count"] == 1 assert len(fb["details"]) == 1 assert fb["details"][0]["error_type"] == "constraint_violation" assert fb["details"][0]["constraint"] == "Length constraint violated" def test_regression_pyramid_solving_and_validation(): """Tests S.ABCD square pyramid DSL parse -> engine solve -> validator pass.""" dsl = """ PYRAMID(S_ABCD) SQUARE(ABCD) LENGTH(AB, 4) LENGTH(SA, 6) PERPENDICULAR_PLANE(SA, ABCD) """ parser = DSLParser() points, constraints, is_3d = parser.parse(dsl) assert is_3d is True assert len(points) >= 5 engine = GeometryEngine() result = engine.solve(points, constraints, is_3d) assert result is not None coords = result.get("coordinates", {}) assert len(coords) >= 5 assert "S" in coords and "A" in coords validator = GeometryValidator() val_res = validator.validate(result, constraints, is_3d) assert val_res.is_valid is True assert val_res.status == GeometryStatus.VALID def test_regression_cube_solving_and_validation(): """Tests Cube ABCD.A1B1C1D1 DSL parse -> engine solve -> validator pass.""" dsl = """ CUBE(ABCD_A1B1C1D1) LENGTH(AB, 5) """ parser = DSLParser() points, constraints, is_3d = parser.parse(dsl) assert is_3d is True engine = GeometryEngine() result = engine.solve(points, constraints, is_3d) assert result is not None coords = result.get("coordinates", {}) assert len(coords) >= 8 validator = GeometryValidator() val_res = validator.validate(result, constraints, is_3d) assert val_res.is_valid is True assert val_res.status == GeometryStatus.VALID def test_regression_triangular_prism_solving_and_validation(): """Tests Right Triangular Prism ABC.A1B1C1 solving & validation.""" dsl = """ PRISM(ABC_A1B1C1) POINT(A, 0, 0, 0) POINT(B, 3, 0, 0) POINT(C, 0, 4, 0) POINT(A1, 0, 0, 6) POINT(B1, 3, 0, 6) POINT(C1, 0, 4, 6) LENGTH(AA1, 6) PERPENDICULAR_PLANE(AA1, ABC) """ parser = DSLParser() points, constraints, is_3d = parser.parse(dsl) assert is_3d is True engine = GeometryEngine() result = engine.solve(points, constraints, is_3d) assert result is not None coords = result.get("coordinates", {}) assert len(coords) >= 6 validator = GeometryValidator() val_res = validator.validate(result, constraints, is_3d) assert val_res.is_valid is True assert val_res.status == GeometryStatus.VALID def test_regression_cone_solving(): """Tests Cone with apex S and base center O.""" dsl = "CONE(S_O, 3, 4)" parser = DSLParser() points, constraints, is_3d = parser.parse(dsl) assert is_3d is True engine = GeometryEngine() result = engine.solve(points, constraints, is_3d) assert result is not None coords = result.get("coordinates", {}) assert "S" in coords and "O" in coords def test_regression_2d_rectangle_solving_and_validation(): """Tests 2D Rectangle ABCD solving & validation.""" dsl = """ RECTANGLE(ABCD) LENGTH(AB, 6) LENGTH(BC, 8) """ parser = DSLParser() points, constraints, is_3d = parser.parse(dsl) assert is_3d is False engine = GeometryEngine() result = engine.solve(points, constraints, is_3d) assert result is not None coords = result.get("coordinates", {}) assert len(coords) == 4 validator = GeometryValidator() val_res = validator.validate(result, constraints, is_3d) assert val_res.is_valid is True assert val_res.status == GeometryStatus.VALID def test_eval_runner_on_benchmark(): """Tests EvalRunner deterministic pass over benchmark dataset.""" dataset = BenchmarkDataset.load_all_standard() assert len(dataset) >= 5 runner = EvalRunner() metrics = runner.evaluate_dsl_deterministic(dataset) assert metrics.total_samples >= 5 assert metrics.dsl_valid_rate == 1.0 assert metrics.solvability_rate == 1.0 assert metrics.validation_pass_rate == 1.0 assert metrics.degradation_rate == 0.0