e6923490fd
Weekday names lived in four files outside internal/lexicon and each copy was short in a different direction. The habit map had the prepositional plural of Sunday and no dative of Wednesday. The plan refusal had the accusative of Wednesday and not the prepositional. cmd/mavend matched the stem. Weekdays hands out the seven nominatives whole, because every Russian case lemmatises to one of them and the case is morph's question. WeekdayEnglish is the half that has to be data: the vendored dictionary is Russian and leaves mondays as it found it. Cardinals gain the dative and instrumental of one to four. A spoken hour declines and five upward has one oblique form for the genitive, dative and prepositional, so pyati was already whole while dvum was missing and k dvum chasam is an hour he says.
203 lines
7.6 KiB
Go
203 lines
7.6 KiB
Go
package lexicon
|
|
|
|
import "testing"
|
|
|
|
// TestClosedSetsAreComplete — the point of the package. A closed class can be
|
|
// finished, so the test names the members that were MISSING from the inline Go
|
|
// lists this package replaced (Vikunja #525) and every one of them has to be
|
|
// there. Add to this list when a form turns up unhandled.
|
|
func TestClosedSetsAreComplete(t *testing.T) {
|
|
inter := map[string]bool{}
|
|
for _, w := range Interrogatives() {
|
|
inter[w] = true
|
|
}
|
|
// Instrumental and prepositional cases of что and кто, and the declined
|
|
// какой. The old list had что/чего and nothing else, so "чем ты занята" and
|
|
// "в каком часу" carried no interrogative at all.
|
|
for _, w := range []string{"чем", "чём", "чему", "кем", "ком", "каком", "какими", "насколько"} {
|
|
if !inter[w] {
|
|
t.Errorf("interrogatives is missing %q", w)
|
|
}
|
|
}
|
|
|
|
for _, tc := range []struct {
|
|
word string
|
|
want int
|
|
}{
|
|
{"ноль", 0}, {"одна", 1}, {"две", 2}, {"одиннадцать", 11},
|
|
{"пятнадцать", 15}, {"двадцать", 20}, {"сорок", 40}, {"девяносто", 90},
|
|
{"сто", 100}, {"twelve", 12},
|
|
} {
|
|
got, ok := Cardinal(tc.word)
|
|
if !ok || got != tc.want {
|
|
t.Errorf("Cardinal(%q) = %d, %v; want %d, true", tc.word, got, ok, tc.want)
|
|
}
|
|
}
|
|
if _, ok := Cardinal("бэкап"); ok {
|
|
t.Error("Cardinal must not answer for a word that is not a number")
|
|
}
|
|
|
|
// A spoken hour declines, and one to four decline further than the rest:
|
|
// "к двум часам" and "к трём" are hours, and only the dative says so (V-581).
|
|
for _, tc := range []struct {
|
|
word string
|
|
want int
|
|
}{
|
|
{"одному", 1}, {"двум", 2}, {"двумя", 2}, {"трём", 3}, {"трем", 3},
|
|
{"четырём", 4}, {"четырем", 4}, {"пяти", 5}, {"семи", 7},
|
|
} {
|
|
if got, ok := Cardinal(tc.word); !ok || got != tc.want {
|
|
t.Errorf("Cardinal(%q) = %d, %v; want %d, true", tc.word, got, ok, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestWeekdaysAreOneList — the second copy of a closed class is the bug (V-581).
|
|
// Weekdays lived in four files outside this one, so the list is handed out whole
|
|
// and the English forms, which the Russian dictionary cannot lemmatise, are here.
|
|
func TestWeekdaysAreOneList(t *testing.T) {
|
|
days := Weekdays()
|
|
if len(days) != 7 || days[0] != "воскресенье" || days[1] != "понедельник" {
|
|
t.Fatalf("Weekdays() = %v; want the seven, Sunday first", days)
|
|
}
|
|
for i, name := range days {
|
|
if Weekday(i) != name {
|
|
t.Errorf("Weekdays()[%d] = %q, but Weekday(%d) = %q", i, name, i, Weekday(i))
|
|
}
|
|
}
|
|
for _, tc := range []struct {
|
|
word string
|
|
want int
|
|
}{{"sunday", 0}, {"monday", 1}, {"mondays", 1}, {"Friday", 5}, {"saturdays", 6}} {
|
|
if got, ok := WeekdayEnglish(tc.word); !ok || got != tc.want {
|
|
t.Errorf("WeekdayEnglish(%q) = %d, %v; want %d, true", tc.word, got, ok, tc.want)
|
|
}
|
|
}
|
|
if _, ok := WeekdayEnglish("понедельник"); ok {
|
|
t.Error("WeekdayEnglish answered for a Russian word; that side is morph's")
|
|
}
|
|
}
|
|
|
|
// TestDayOffsetHasNoOrderingTrap — the defect a lookup removes. The callers this
|
|
// replaced used strings.Contains in a switch, so "послезавтра" had to be tested
|
|
// before "завтра" by hand or the day after tomorrow read as tomorrow.
|
|
func TestDayOffsetHasNoOrderingTrap(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
text string
|
|
want int
|
|
ok bool
|
|
}{
|
|
{"послезавтра", 2, true},
|
|
{"завтра", 1, true},
|
|
{"сегодня", 0, true},
|
|
{"вчера", -1, true},
|
|
{"позавчера", -2, true},
|
|
{"встреча послезавтра в 14:30", 2, true},
|
|
{"Tomorrow at 09:00", 1, true},
|
|
{"не сегодня, а послезавтра", 2, true},
|
|
{"в четверг", 0, false},
|
|
{"завтраком", 0, false},
|
|
} {
|
|
got, ok := DayOffsetIn(tc.text)
|
|
if ok != tc.ok || (ok && got != tc.want) {
|
|
t.Errorf("DayOffsetIn(%q) = %d, %v; want %d, %v", tc.text, got, ok, tc.want, tc.ok)
|
|
}
|
|
}
|
|
// "сегодня" is offset 0, which is also the zero value, so the second return
|
|
// is the only thing that separates a hit from a miss.
|
|
if n, ok := DayOffset("сегодня"); n != 0 || !ok {
|
|
t.Errorf("DayOffset(сегодня) = %d, %v; want 0, true", n, ok)
|
|
}
|
|
}
|
|
|
|
// TestIndexedSetsLineUpWithTheirCallers — weekdays start at Sunday because Go's
|
|
// time.Weekday does, and months are 1-indexed so a month number needs no
|
|
// arithmetic. Off-by-one here is a wrong date spoken out loud.
|
|
func TestIndexedSetsLineUpWithTheirCallers(t *testing.T) {
|
|
if got := Weekday(0); got != "воскресенье" {
|
|
t.Errorf("Weekday(0) = %q, want воскресенье", got)
|
|
}
|
|
if got := Weekday(1); got != "понедельник" {
|
|
t.Errorf("Weekday(1) = %q, want понедельник", got)
|
|
}
|
|
if got := MonthGenitive(1); got != "января" {
|
|
t.Errorf("MonthGenitive(1) = %q, want января", got)
|
|
}
|
|
if got := MonthGenitive(12); got != "декабря" {
|
|
t.Errorf("MonthGenitive(12) = %q, want декабря", got)
|
|
}
|
|
if got := MonthGenitive(0); got != "" {
|
|
t.Errorf("MonthGenitive(0) = %q, want the empty slot", got)
|
|
}
|
|
if got := HourSpoken(23); got != "двадцать три" {
|
|
t.Errorf("HourSpoken(23) = %q, want двадцать три", got)
|
|
}
|
|
for _, i := range []int{-1, 7, 13, 24} {
|
|
if got := Weekday(i); i >= 7 && got != "" {
|
|
t.Errorf("Weekday(%d) = %q, want empty", i, got)
|
|
}
|
|
}
|
|
if got := HourSpoken(24); got != "" {
|
|
t.Errorf("HourSpoken(24) = %q, want empty", got)
|
|
}
|
|
}
|
|
|
|
// TestCallerCannotEditTheLexicon — the sets are handed out as copies. A caller
|
|
// that sorted the slice it was given would otherwise reorder weekdays for
|
|
// everybody.
|
|
func TestCallerCannotEditTheLexicon(t *testing.T) {
|
|
first := Interrogatives()
|
|
first[0] = "мутировало"
|
|
if again := Interrogatives(); again[0] == "мутировало" {
|
|
t.Fatal("the lexicon handed out its own backing array")
|
|
}
|
|
}
|
|
|
|
// The positions carry gender and oblique forms, because "второй пункт" and
|
|
// "закрепи вторым" name one position (Vikunja #516). "last" is a position and not
|
|
// a count, so it is -1 rather than a large number.
|
|
func TestOrdinalsSpanGenderAndCase(t *testing.T) {
|
|
for _, w := range []string{"второй", "вторая", "второе", "вторым", "второго", "second"} {
|
|
n, ok := Ordinal(w)
|
|
if !ok || n != 2 {
|
|
t.Errorf("Ordinal(%q) = %d, %v; want 2, true", w, n, ok)
|
|
}
|
|
}
|
|
for _, w := range []string{"последний", "последнюю", "last"} {
|
|
if n, ok := Ordinal(w); !ok || n != -1 {
|
|
t.Errorf("Ordinal(%q) = %d, %v; want -1, true", w, n, ok)
|
|
}
|
|
}
|
|
// A weekday shares a stem with a position and is not one.
|
|
if n, ok := Ordinal("вторник"); ok {
|
|
t.Errorf("Ordinal(\"вторник\") = %d; a weekday is not a position", n)
|
|
}
|
|
}
|
|
|
|
// Earliest wins, not map order: the same sentence must answer the same way twice.
|
|
func TestOrdinalInTakesTheFirstPosition(t *testing.T) {
|
|
for i := 0; i < 50; i++ {
|
|
n, ok := OrdinalIn("отметь первый и второй пункт")
|
|
if !ok || n != 1 {
|
|
t.Fatalf("run %d: OrdinalIn = %d, %v; want 1, true", i, n, ok)
|
|
}
|
|
}
|
|
if _, ok := OrdinalIn("отметь пункт"); ok {
|
|
t.Error("a sentence with no position reported one")
|
|
}
|
|
}
|
|
|
|
// Ordinals is the escape hatch for the cases the file does not list, so it must
|
|
// hand out every entry and hand out the same order twice.
|
|
func TestOrdinalsListIsCompleteAndStable(t *testing.T) {
|
|
a, b := Ordinals(), Ordinals()
|
|
if len(a) != len(ru.Sets["ordinals"].Values) {
|
|
t.Errorf("Ordinals returned %d of %d entries", len(a), len(ru.Sets["ordinals"].Values))
|
|
}
|
|
for i := range a {
|
|
if a[i] != b[i] {
|
|
t.Fatalf("Ordinals order is not stable at %d: %v vs %v", i, a[i], b[i])
|
|
}
|
|
}
|
|
}
|