0d7194ef89
CompareBenchmark now also compares the engine's numeric dimensions (mm gaps, mm/s speeds) against the professional's GT measures: parses distance tokens from both sides (German thousands/decimal aware), reports matched / gt_only (gaps) / engine_only + an agreement %. Surfaces as result.distances on the existing benchmark endpoint. Deterministic, no LLM. On the GT-derived seed sessions it mainly guards DRIFT; its real value is new sessions. Real-GT test pins that the engine covers the Bremse (250 mm/s, 250/850 mm) and Kistenhub (25/120 mm, 150/75 mm/s) headline dimensions. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
89 lines
2.6 KiB
Go
89 lines
2.6 KiB
Go
package iace
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
)
|
|
|
|
func TestExtractDistanceTokens_Normalisation(t *testing.T) {
|
|
toks := extractDistanceTokens([]string{
|
|
"Abstand >= 25 mm und max. 250 mm/s",
|
|
"Hand-Speed 1.600 mm/s", // German thousands → 1600
|
|
"Querschnitt 2,5 mm", // German decimal → 2.5
|
|
"850mm ohne Leerzeichen",
|
|
})
|
|
got := map[string]bool{}
|
|
for _, tk := range toks {
|
|
got[tk.Unit+":"+strconv.FormatFloat(tk.Value, 'f', 1, 64)] = true
|
|
}
|
|
for _, want := range []string{"mm:25.0", "mm/s:250.0", "mm/s:1600.0", "mm:2.5", "mm:850.0"} {
|
|
if !got[want] {
|
|
t.Errorf("expected token %s, got %+v", want, toks)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCompareDistances_MatchesAndGaps(t *testing.T) {
|
|
gt := []string{"Abstand >= 25 mm", "max. 250 mm/s", "min. 850 mm", "<= 150 mm/s"}
|
|
eng := []string{"Spalt 25 mm Fingerschutz", "Teach 250 mm/s", "850 mm Tunnel"}
|
|
cmp := CompareDistances(gt, eng)
|
|
if cmp.GTCount != 4 || cmp.MatchedCount != 3 {
|
|
t.Fatalf("expected 3/4 matched, got %d/%d", cmp.MatchedCount, cmp.GTCount)
|
|
}
|
|
if len(cmp.GTOnly) != 1 || cmp.GTOnly[0].Value != 150 {
|
|
t.Errorf("expected 150 mm/s as the gap, got %+v", cmp.GTOnly)
|
|
}
|
|
}
|
|
|
|
// Real GT sessions: the engine library must cover the professional's headline
|
|
// dimensions (the engine measures were authored from these sessions).
|
|
func TestCompareSessionDistances_RealGT(t *testing.T) {
|
|
var engTexts []string
|
|
for _, m := range GetProtectiveMeasureLibrary() {
|
|
engTexts = append(engTexts, m.Name, m.Description)
|
|
}
|
|
|
|
cases := []struct {
|
|
file string
|
|
must []DistanceToken
|
|
}{
|
|
{"testdata/ground_truth_bremse.json", []DistanceToken{
|
|
{Value: 250, Unit: "mm/s"}, {Value: 250, Unit: "mm"}, {Value: 850, Unit: "mm"},
|
|
}},
|
|
{"testdata/ground_truth_kistenhub.json", []DistanceToken{
|
|
{Value: 25, Unit: "mm"}, {Value: 120, Unit: "mm"},
|
|
{Value: 150, Unit: "mm/s"}, {Value: 75, Unit: "mm/s"}, // filled by M603/M605
|
|
}},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
raw, err := os.ReadFile(tc.file)
|
|
if err != nil {
|
|
t.Fatalf("read %s: %v", tc.file, err)
|
|
}
|
|
var gt GroundTruth
|
|
if err := json.Unmarshal(raw, >); err != nil {
|
|
t.Fatalf("parse %s: %v", tc.file, err)
|
|
}
|
|
var gtTexts []string
|
|
for _, e := range gt.Entries {
|
|
gtTexts = append(gtTexts, e.Measures...)
|
|
}
|
|
cmp := CompareDistances(gtTexts, engTexts)
|
|
for _, want := range tc.must {
|
|
matched := false
|
|
for _, m := range cmp.Matched {
|
|
if m.Unit == want.Unit && m.Value == want.Value {
|
|
matched = true
|
|
break
|
|
}
|
|
}
|
|
if !matched {
|
|
t.Errorf("%s: engine should cover %.0f %s but it is a gap", tc.file, want.Value, want.Unit)
|
|
}
|
|
}
|
|
}
|
|
}
|