package lang import "testing" // TestSecondPairDryRun is the §5 "second pair" dry-run for the source-script seam (П2): the echo detector // must be REACHABLE for every source the engine handles — a source with no declared script silently sleeps // the echo detector (the en/ko blind spots §5 named). Every 2nd-pair source declares a script here. func TestSecondPairDryRun(t *testing.T) { for _, tc := range []struct { lang string isCJK bool }{ {"zh", true}, {"ja", true}, {"ko", true}, {"en", false}, {"ru", false}, } { if got := LangScripts(tc.lang); len(got) == 0 { t.Errorf("LangScripts(%q) is empty — the echo detector would sleep silently for this source (a §5 blind spot)", tc.lang) } if got := IsCJKScriptLang(tc.lang); got != tc.isCJK { t.Errorf("IsCJKScriptLang(%q) = %v, want %v", tc.lang, got, tc.isCJK) } } // ko (Hangul) and en (Latin) are the two the OLD hard-coded Han+kana detector was blind to. if len(LangScripts("ko")) == 0 || len(LangScripts("en")) == 0 { t.Fatal("ko/en must resolve scripts — these are the fixed blind spots") } } // TestSeriesMorphologyUnchanged pins that moving head-finality into data (fix-pack §б) reproduces the prior // Go-constant behaviour: every dense source co-batches head-final, every alphabetic source has the channel off. func TestSeriesMorphologyUnchanged(t *testing.T) { for _, tc := range []struct { lang string enabled, headFinal bool }{ {"zh", true, true}, {"ja", true, true}, {"ko", true, true}, {"en", false, false}, {"ru", false, false}, {"unknown", false, false}, } { en, hf := SeriesMorphology(tc.lang) if en != tc.enabled || hf != tc.headFinal { t.Errorf("SeriesMorphology(%q) = (%v,%v), want (%v,%v)", tc.lang, en, hf, tc.enabled, tc.headFinal) } } // The real embedded data must declare han head-final, or zh would silently flip. if !SeriesHeadFinal("han") { t.Fatal("data/script-series.txt must declare han head-final") } } // TestSeriesHeadFinalIsDataDriven is the fix-pack §б generality proof: a dense script's head direction comes // from DATA, so declaring a dense NON-head-final script is a data row, not a Go edit. Driven through the pure // parser with a synthetic table so it needs no real embedded script. func TestSeriesHeadFinalIsDataDriven(t *testing.T) { m, err := parseScriptHeadFinal([]byte( "# synthetic\nhead_position\ttengwar\tinitial\nhead_position\tcirth\tfinal\n")) if err != nil { t.Fatalf("valid table must parse: %v", err) } if m["tengwar"] { t.Fatal("a script declared `initial` must resolve head-INITIAL from data with no Go edit") } if !m["cirth"] { t.Fatal("a script declared `final` must resolve head-final") } // An unlisted script defaults to head-final (the CJK modifier-head norm) through the exported resolver. if !SeriesHeadFinal("no-such-script-xyz") { t.Fatal("an unlisted script must default to head-final") } // Corrupt data is loud, never silent. if _, err := parseScriptHeadFinal([]byte("head_position\than\tsideways\n")); err == nil { t.Fatal("an invalid head position must be a parse error") } if _, err := parseScriptHeadFinal([]byte("wrong_cat\than\tfinal\n")); err == nil { t.Fatal("an unknown category must be a parse error") } }