Spaces:
Running
Running
| """Phase A2 — sandbox kernel (SPEC_OPTION_A.md §4.1, §10). | |
| Offline: a valid generator executes and (via repair) passes the metric gate; | |
| disallowed imports, timeouts, runtime errors, and invalid output all raise | |
| KernelError. | |
| """ | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| import pytest | |
| ROOT = Path(__file__).resolve().parent.parent | |
| if str(ROOT) not in sys.path: | |
| sys.path.insert(0, str(ROOT)) | |
| from geometry import kernel, metrics, repair # noqa: E402 | |
| GEN_OCT = ''' | |
| import numpy as np | |
| def build(p): | |
| v = [[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]] | |
| f = [[4,0,2,"f"],[4,2,1,"f"],[4,1,3,"f"],[4,3,0,"f"], | |
| [5,2,0,"f"],[5,1,2,"f"],[5,3,1,"f"],[5,0,3,"f"]] | |
| return v, f | |
| ''' | |
| GEN_PARAM_PRISM = ''' | |
| import numpy as np, math | |
| def build(p): | |
| n = int(p.get("sides", 5)) | |
| ang = [2*math.pi*i/n for i in range(n)] | |
| bot = [[math.cos(a), -1.0, math.sin(a)] for a in ang] | |
| top = [[math.cos(a), 1.0, math.sin(a)] for a in ang] | |
| v = bot + top | |
| f = [] | |
| for i in range(n): | |
| j = (i+1) % n | |
| f.append([i, j, n+j, "side"]); f.append([i, n+j, n+i, "side"]) | |
| return v, f | |
| ''' | |
| def test_valid_generator_runs(): | |
| V, F = kernel.run(GEN_OCT) | |
| assert V.shape == (6, 3) | |
| assert len(F) == 8 and all(len(f) == 4 for f in F) | |
| def test_generator_plus_repair_passes_gate(): | |
| V, F = kernel.run(GEN_OCT) | |
| v, f = repair.repair(V, F, "regular_solid") | |
| ok, fails = metrics.passes(v, f, "regular_solid") | |
| assert ok, fails | |
| def test_params_reach_generator(): | |
| V, F = kernel.run(GEN_PARAM_PRISM, params={"sides": 7}) | |
| assert V.shape == (14, 3) # 7 bottom + 7 top | |
| def test_disallowed_import_rejected(): | |
| code = "import os\ndef build(p):\n return [[0,0,0]], [[0,0,0,'x']]\n" | |
| with pytest.raises(kernel.KernelError) as e: | |
| kernel.run(code) | |
| assert "disallowed import" in str(e.value) | |
| def test_timeout_rejected(): | |
| code = "def build(p):\n x=0\n while True:\n x+=1\n return [],[]\n" | |
| with pytest.raises(kernel.KernelError) as e: | |
| kernel.run(code, timeout=1) | |
| assert "timeout" in str(e.value) | |
| def test_runtime_error_rejected(): | |
| code = "def build(p):\n return 1/0\n" | |
| with pytest.raises(kernel.KernelError): | |
| kernel.run(code) | |
| def test_non_finite_rejected(): | |
| code = ("import numpy as np\n" | |
| "def build(p):\n" | |
| " return [[float('nan'),0,0],[1,0,0],[0,1,0]], [[0,1,2,'f']]\n") | |
| with pytest.raises(kernel.KernelError): | |
| kernel.run(code) | |
| def test_out_of_range_face_rejected(): | |
| code = "def build(p):\n return [[0,0,0],[1,0,0],[0,1,0]], [[0,1,9,'f']]\n" | |
| with pytest.raises(kernel.KernelError): | |
| kernel.run(code) | |
| def test_missing_entry_rejected(): | |
| code = "def other(p):\n return [],[]\n" | |
| with pytest.raises(kernel.KernelError): | |
| kernel.run(code) | |
| def test_syntax_error_rejected(): | |
| with pytest.raises(kernel.KernelError): | |
| kernel.run("def build(p)\n return [],[]\n") | |