106 lines
4.7 KiB
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)
|
|
}
|
|
}
|