File size: 2,994 Bytes
089a893
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
"""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")