textmachine/backend/internal/langscreen/langscreen_test.go

106 lines
4.7 KiB
Go

package langscreen
import (
"strings"
"testing"
"unicode"
)
func cyr() []*unicode.RangeTable { return []*unicode.RangeTable{unicode.Cyrillic} }
// pad repeats s until it clears the evidence floor, so a case can be about the SHARE without also being
// about the length.
func pad(s string, letters int) string {
var b strings.Builder
for n := 0; n < letters; n += len([]rune(s)) {
b.WriteString(s)
}
return b.String()
}
func TestTheScreenAnswersTheClosedSet(t *testing.T) {
for _, tc := range []struct {
name string
text string
want Verdict
}{
{"target prose", pad("текстанаязыке", 400), OnTarget},
{"a foreign completion", pad("englishtextinstead", 400), OffTarget},
{"the source echoed back", pad("源文本汉字", 400), OffTarget},
{"too short to judge", "коротко", Abstain},
} {
t.Run(tc.name, func(t *testing.T) {
if got := Screen(tc.text, cyr()).Verdict; got != tc.want {
t.Fatalf("verdict %q, want %q", got, tc.want)
}
})
}
}
// TestATargetWithNoDataIsNeverJudged is the layer-7 discipline and the review question of the whole
// project in one assertion: a pair the repository does not carry must be INERT, not condemned. The screen
// abstains on an empty script set however foreign the text looks, so adding a pair is a data row and never
// an edit here.
func TestATargetWithNoDataIsNeverJudged(t *testing.T) {
r := Screen(pad("englishtextinstead", 400), nil)
if r.Verdict != Abstain {
t.Fatalf("a target that declares no script must not be judged: %+v", r)
}
if r.Share != 0 {
t.Fatalf("an abstention must carry no score to be misread as one: %+v", r)
}
}
// TestTheEvidenceFloorIsAFloorAndNotAVerdict pins the abstain boundary on both sides. Below it the screen
// says nothing — never «off target», which on a chapter heading would be a flag on a healthy unit — and one
// letter above it the same text is judged normally.
func TestTheEvidenceFloorIsAFloorAndNotAVerdict(t *testing.T) {
foreign := strings.Repeat("a", MinLetters-1)
if got := Screen(foreign, cyr()); got.Verdict != Abstain || got.Letters != MinLetters-1 {
t.Fatalf("under the floor the screen must abstain and say how little it saw: %+v", got)
}
if got := Screen(strings.Repeat("a", MinLetters), cyr()); got.Verdict != OffTarget {
t.Fatalf("at the floor the screen must judge: %+v", got)
}
// Non-letters carry no evidence and must not lift a text over the floor: a table of numbers is not
// 200 letters of anything.
padded := strings.Repeat("a", MinLetters-1) + strings.Repeat("1234567890 ", 100)
if got := Screen(padded, cyr()); got.Verdict != Abstain {
t.Fatalf("digits and spacing must not count as evidence: %+v", got)
}
}
// TestTheThresholdSitsInTheMeasuredGap holds the constant against the corpus it was placed from (backlog
// row 113): every off-target completion on the stand scores 0.0000 and the worst healthy one 0.9767. Both
// sides are asserted, because a threshold is only a threshold if BOTH are on the right side of it.
func TestTheThresholdSitsInTheMeasuredGap(t *testing.T) {
const worstHealthyMeasured, bestOffTargetMeasured = 0.9767, 0.0000
if OnTargetFloor >= worstHealthyMeasured {
t.Errorf("the floor %.4f is at or above the worst HEALTHY completion measured (%.4f): it would flag clean prose", OnTargetFloor, worstHealthyMeasured)
}
if OnTargetFloor <= bestOffTargetMeasured {
t.Errorf("the floor %.4f is at or below the best OFF-TARGET completion measured (%.4f): it would catch nothing", OnTargetFloor, bestOffTargetMeasured)
}
}
// TestTheScoreIsOverLettersOnly is the measurement rule made explicit. Sharing punctuation, digits and
// whitespace across languages is exactly why they are excluded: counted, a heavily formatted reply and a
// dense one would be judged on different scales.
func TestTheScoreIsOverLettersOnly(t *testing.T) {
letters := pad("текст", 400)
plain := Screen(letters, cyr())
noisy := Screen(strings.Join(strings.Split(letters, ""), " — 42, "), cyr())
if plain.Share != noisy.Share || plain.Letters != noisy.Letters {
t.Fatalf("formatting changed the score: %+v vs %+v", plain, noisy)
}
}
// TestTheSupersetLimitIsRealAndNamed asserts the limitation the package doccomment declares, because a
// limit that is only written down is a limit somebody will later be surprised by. A target whose declared
// scripts CONTAIN another language's — ja declares han — cannot tell that language's output apart here.
func TestTheSupersetLimitIsRealAndNamed(t *testing.T) {
ja := []*unicode.RangeTable{unicode.Han, unicode.Hiragana, unicode.Katakana}
if got := Screen(pad("源文本汉字", 400), ja).Verdict; got != OnTarget {
t.Fatalf("the doccomment says a purely Chinese output scores 1.000 for a ja target; it now says %q — if the rule gained a datum, say so there", got)
}
}