122 lines
4.5 KiB
Go
122 lines
4.5 KiB
Go
package lang
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
"unicode"
|
||
)
|
||
|
||
// reader_test.go: the reader words are DATA of the target language, read outside the pack loader and
|
||
// outside Pack.Version() — a wording edit must never re-bill a book.
|
||
|
||
const sampleReader = `# ru
|
||
hole.pending ⚠ Не переведено ({chapter}.{unit}).
|
||
hole.withheld ⚠ Не выпущено.
|
||
hole.incomplete ⚠ Недостаёт {dropped}.
|
||
hole.stale ⚠ Устарело ({chapter}.{unit}).
|
||
hole.ghost ⚠ Глава {chapter}: вне нарезки {ghost}.
|
||
notice.holes ⚠ Пропусков {holes} из {total}.
|
||
notice.ghost ⚠ Вне нарезки {ghost}.
|
||
`
|
||
|
||
func writeReader(t *testing.T, root, tgt, body string) {
|
||
t.Helper()
|
||
if err := os.MkdirAll(filepath.Join(root, tgt), 0o755); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := os.WriteFile(filepath.Join(root, tgt, readerFile), []byte(body), 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
|
||
func TestReaderWordsLoadAndFill(t *testing.T) {
|
||
root := t.TempDir()
|
||
writeReader(t, root, "ru", sampleReader)
|
||
w, present, err := LoadReaderWords(root, "ru")
|
||
if err != nil || !present {
|
||
t.Fatalf("present=%v err=%v", present, err)
|
||
}
|
||
if got := FillReaderTemplate(w.HolePending, "chapter", "3", "unit", "0"); got != "⚠ Не переведено (3.0)." {
|
||
t.Errorf("filled = %q", got)
|
||
}
|
||
if got := FillReaderTemplate(w.NoticeHoles, "holes", "2", "total", "37"); got != "⚠ Пропусков 2 из 37." {
|
||
t.Errorf("filled = %q", got)
|
||
}
|
||
// A placeholder the caller did not pass stays visible rather than vanishing.
|
||
if got := FillReaderTemplate("{x} {y}", "x", "1"); got != "1 {y}" {
|
||
t.Errorf("unfilled placeholder must stay: %q", got)
|
||
}
|
||
}
|
||
|
||
func TestReaderWordsAbsentIsTheNonVerbalForm(t *testing.T) {
|
||
for _, root := range []string{"", t.TempDir()} {
|
||
w, present, err := LoadReaderWords(root, "ru")
|
||
if err != nil || present {
|
||
t.Fatalf("root %q: present=%v err=%v", root, present, err)
|
||
}
|
||
if w != DefaultReaderWords() {
|
||
t.Fatalf("root %q: absent file must yield the defaults", root)
|
||
}
|
||
}
|
||
// The non-verbal form contains no letter of any language: a symbol and the numbers only.
|
||
d := DefaultReaderWords()
|
||
for name, tmpl := range map[string]string{"pending": d.HolePending, "withheld": d.HoleWithheld, "incomplete": d.HoleIncomplete,
|
||
"stale": d.HoleStale, "ghost": d.HoleGhost, "notice": d.NoticeHoles, "notice-ghost": d.NoticeGhost} {
|
||
bare := tmpl
|
||
for _, ph := range []string{"{chapter}", "{unit}", "{dropped}", "{ghost}", "{holes}", "{total}"} {
|
||
bare = strings.ReplaceAll(bare, ph, "")
|
||
}
|
||
for _, r := range bare {
|
||
if unicode.IsLetter(r) {
|
||
t.Errorf("default %s template %q carries a letter — that is a word the engine decided", name, tmpl)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestReaderWordsFailLoud(t *testing.T) {
|
||
cases := map[string]string{
|
||
"unknown key": sampleReader + "hole.other\tx\n",
|
||
"missing key": strings.Replace(sampleReader, "notice.ghost\t⚠ Вне нарезки {ghost}.\n", "", 1),
|
||
"duplicate key": sampleReader + "hole.pending\tагаин\n",
|
||
"empty value": strings.Replace(sampleReader, "⚠ Не выпущено.", "", 1),
|
||
"no tab": strings.Replace(sampleReader, "hole.withheld\t", "hole.withheld ", 1),
|
||
}
|
||
for name, body := range cases {
|
||
root := t.TempDir()
|
||
writeReader(t, root, "ru", body)
|
||
if _, _, err := LoadReaderWords(root, "ru"); err == nil {
|
||
t.Errorf("%s: must fail loud", name)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestReaderWordsDoNotMoveThePackVersion is the money pin: reader.txt beside the pack changes NOTHING
|
||
// the snapshot folds. (The stand proof is `current_snapshot` of the minirun export before and after.)
|
||
func TestReaderWordsDoNotMoveThePackVersion(t *testing.T) {
|
||
root := t.TempDir()
|
||
writeSyntheticPack(t, root, "zh", "ru")
|
||
before, err := Load(root, "zh", "ru")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
writeReader(t, root, "ru", sampleReader)
|
||
after, err := Load(root, "zh", "ru")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if before.Version() != after.Version() {
|
||
t.Fatalf("adding <root>/ru/reader.txt moved Pack.Version() %s → %s — a wording edit would re-bill every book of the pair", before.Version(), after.Version())
|
||
}
|
||
// And the real shipped file loads.
|
||
w, present, err := LoadReaderWords(filepath.Join("..", "..", "configs", "langpacks"), "ru")
|
||
if err != nil || !present {
|
||
t.Fatalf("configs/langpacks/ru/reader.txt: present=%v err=%v", present, err)
|
||
}
|
||
if !strings.Contains(w.HoleIncomplete, "{dropped}") || !strings.Contains(w.NoticeHoles, "{total}") {
|
||
t.Errorf("shipped ru words lost a placeholder: %+v", w)
|
||
}
|
||
}
|