package controllogic import ( "math" "testing" ) func TestEvalExpr(t *testing.T) { resolve := func(ds, name string) float64 { switch { case ds == "stub" && name == "x": return 10 case ds == "local" && name == "y": return 3 case ds == "sys" && name == "dt": return 0.5 } return math.NaN() } cases := []struct { expr string want float64 }{ {"1 + 2 * 3", 7}, {"(1 + 2) * 3", 9}, {"10 % 3", 1}, {"{stub:x} + y", 13}, {"{stub:x} > 5 ? 1 : 0", 1}, {"min(4, 2, 8)", 2}, {"max(4, 2, 8)", 8}, {"abs(-5)", 5}, {"sqrt(16)", 4}, {"2 < 3 && 3 < 4", 1}, {"!0", 1}, {"-{stub:x}", -10}, {"{sys:dt} * 2", 1}, {"true + false", 1}, } for _, c := range cases { got := EvalExpr(c.expr, resolve) if math.Abs(got-c.want) > 1e-9 { t.Errorf("EvalExpr(%q) = %v, want %v", c.expr, got, c.want) } } } func TestEvalExprErrors(t *testing.T) { r := func(ds, name string) float64 { return 0 } for _, bad := range []string{"1 +", "(1", "1 2", "{unterminated"} { if v := EvalExpr(bad, r); !math.IsNaN(v) { t.Errorf("EvalExpr(%q) = %v, want NaN", bad, v) } } } func TestCollectRefs(t *testing.T) { refs := CollectRefs("{stub:x} + y + {epics:MY:PV} * z") got := map[string]bool{} for _, r := range refs { got[r.DS+":"+r.Name] = true } for _, want := range []string{"stub:x", "local:y", "epics:MY:PV", "local:z"} { if !got[want] { t.Errorf("CollectRefs missing %q (got %v)", want, got) } } } func TestCheckExpr(t *testing.T) { if msg := CheckExpr("1 + 2"); msg != "" { t.Errorf("CheckExpr valid returned %q", msg) } if msg := CheckExpr("1 +"); msg == "" { t.Errorf("CheckExpr invalid returned empty") } if msg := CheckExpr(""); msg != "" { t.Errorf("CheckExpr empty returned %q", msg) } } func TestEpicsRefSplitFirstColon(t *testing.T) { refs := CollectRefs("{epics:SR:BPM:01:X}") if len(refs) != 1 || refs[0].DS != "epics" || refs[0].Name != "SR:BPM:01:X" { t.Errorf("got %+v, want epics / SR:BPM:01:X", refs) } }