1
2
3
4
5 package plural
6
7 import (
8 "fmt"
9 "reflect"
10 "strconv"
11 "strings"
12 "testing"
13
14 "golang.org/x/text/language"
15 )
16
17 func TestGetIntApprox(t *testing.T) {
18 const big = 1234567890
19 testCases := []struct {
20 digits string
21 start int
22 end int
23 nMod int
24 want int
25 }{
26 {"123", 0, 1, 1, 1},
27 {"123", 0, 2, 1, big},
28 {"123", 0, 2, 2, 12},
29 {"123", 3, 4, 2, 0},
30 {"12345", 3, 4, 2, 4},
31 {"40", 0, 1, 2, 4},
32 {"1", 0, 7, 2, big},
33
34 {"123", 0, 5, 2, big},
35 {"123", 0, 5, 3, big},
36 {"123", 0, 5, 4, big},
37 {"123", 0, 5, 5, 12300},
38 {"123", 0, 5, 6, 12300},
39 {"123", 0, 5, 7, 12300},
40
41
42
43 {"123", 0, 3, 3, 123},
44 {"1234", 0, 3, 3, 123},
45 {"1", 0, 6, 8, 100000},
46
47
48 {"123", 3, 3, 3, 0},
49 {"1234", 3, 4, 3, 4},
50 {"1234", 3, 5, 3, 40},
51 {"1", 6, 8, 8, 0},
52 }
53 for _, tc := range testCases {
54 t.Run(fmt.Sprintf("%s:%d:%d/%d", tc.digits, tc.start, tc.end, tc.nMod), func(t *testing.T) {
55 got := getIntApprox(mkDigits(tc.digits), tc.start, tc.end, tc.nMod, big)
56 if got != tc.want {
57 t.Errorf("got %d; want %d", got, tc.want)
58 }
59 })
60 }
61 }
62
63 func mkDigits(s string) []byte {
64 b := []byte(s)
65 for i := range b {
66 b[i] -= '0'
67 }
68 return b
69 }
70
71 func TestValidForms(t *testing.T) {
72 testCases := []struct {
73 tag language.Tag
74 want []Form
75 }{
76 {language.AmericanEnglish, []Form{Other, One}},
77 {language.Portuguese, []Form{Other, One}},
78 {language.Latvian, []Form{Other, Zero, One}},
79 {language.Arabic, []Form{Other, Zero, One, Two, Few, Many}},
80 {language.Russian, []Form{Other, One, Few, Many}},
81 }
82 for _, tc := range testCases {
83 got := validForms(cardinal, tc.tag)
84 if !reflect.DeepEqual(got, tc.want) {
85 t.Errorf("validForms(%v): got %v; want %v", tc.tag, got, tc.want)
86 }
87 }
88 }
89
90 func TestOrdinal(t *testing.T) {
91 testPlurals(t, Ordinal, ordinalTests)
92 }
93
94 func TestCardinal(t *testing.T) {
95 testPlurals(t, Cardinal, cardinalTests)
96 }
97
98 func testPlurals(t *testing.T, p *Rules, testCases []pluralTest) {
99 for _, tc := range testCases {
100 for _, loc := range strings.Split(tc.locales, " ") {
101 tag := language.MustParse(loc)
102
103 for _, s := range tc.integer {
104 a := strings.Split(s, "~")
105 from := parseUint(t, a[0])
106 to := from
107 if len(a) > 1 {
108 to = parseUint(t, a[1])
109 }
110 for n := from; n <= to; n++ {
111 t.Run(fmt.Sprintf("%s/int(%d)", loc, n), func(t *testing.T) {
112 if f := p.matchComponents(tag, n, 0, 0); f != Form(tc.form) {
113 t.Errorf("matchComponents: got %v; want %v", f, Form(tc.form))
114 }
115 digits := []byte(fmt.Sprint(n))
116 for i := range digits {
117 digits[i] -= '0'
118 }
119 if f := p.MatchDigits(tag, digits, len(digits), 0); f != Form(tc.form) {
120 t.Errorf("MatchDigits: got %v; want %v", f, Form(tc.form))
121 }
122 })
123 }
124 }
125
126 for _, s := range tc.decimal {
127 a := strings.Split(s, "~")
128 from, scale := parseFixedPoint(t, a[0])
129 to := from
130 if len(a) > 1 {
131 var toScale int
132 if to, toScale = parseFixedPoint(t, a[1]); toScale != scale {
133 t.Fatalf("%s:%s: non-matching scales %d versus %d", loc, s, scale, toScale)
134 }
135 }
136 m := 1
137 for i := 0; i < scale; i++ {
138 m *= 10
139 }
140 for n := from; n <= to; n++ {
141 num := fmt.Sprintf("%[1]d.%0[3]*[2]d", n/m, n%m, scale)
142 name := fmt.Sprintf("%s:dec(%s)", loc, num)
143 t.Run(name, func(t *testing.T) {
144 ff := n % m
145 tt := ff
146 w := scale
147 for tt > 0 && tt%10 == 0 {
148 w--
149 tt /= 10
150 }
151 if f := p.MatchPlural(tag, n/m, scale, w, ff, tt); f != Form(tc.form) {
152 t.Errorf("MatchPlural: got %v; want %v", f, Form(tc.form))
153 }
154 if f := p.matchComponents(tag, n/m, n%m, scale); f != Form(tc.form) {
155 t.Errorf("matchComponents: got %v; want %v", f, Form(tc.form))
156 }
157 exp := strings.IndexByte(num, '.')
158 digits := []byte(strings.Replace(num, ".", "", 1))
159 for i := range digits {
160 digits[i] -= '0'
161 }
162 if f := p.MatchDigits(tag, digits, exp, scale); f != Form(tc.form) {
163 t.Errorf("MatchDigits: got %v; want %v", f, Form(tc.form))
164 }
165 })
166 }
167 }
168 }
169 }
170 }
171
172 func parseUint(t *testing.T, s string) int {
173 val, err := strconv.ParseUint(s, 10, 32)
174 if err != nil {
175 t.Fatal(err)
176 }
177 return int(val)
178 }
179
180 func parseFixedPoint(t *testing.T, s string) (val, scale int) {
181 p := strings.Index(s, ".")
182 s = strings.Replace(s, ".", "", 1)
183 v, err := strconv.ParseUint(s, 10, 32)
184 if err != nil {
185 t.Fatal(err)
186 }
187 return int(v), len(s) - p
188 }
189
190 func BenchmarkPluralSimpleCases(b *testing.B) {
191 p := Cardinal
192 en := tagToID(language.English)
193 zh := tagToID(language.Chinese)
194 for i := 0; i < b.N; i++ {
195 matchPlural(p, en, 0, 0, 0)
196 matchPlural(p, en, 1, 0, 0)
197 matchPlural(p, en, 2, 12, 3)
198 matchPlural(p, zh, 0, 0, 0)
199 matchPlural(p, zh, 1, 0, 0)
200 matchPlural(p, zh, 2, 12, 3)
201 }
202 }
203
204 func BenchmarkPluralComplexCases(b *testing.B) {
205 p := Cardinal
206 ar := tagToID(language.Arabic)
207 lv := tagToID(language.Latvian)
208 for i := 0; i < b.N; i++ {
209 matchPlural(p, lv, 0, 19, 2)
210 matchPlural(p, lv, 11, 0, 3)
211 matchPlural(p, lv, 100, 123, 4)
212 matchPlural(p, ar, 0, 0, 0)
213 matchPlural(p, ar, 110, 0, 0)
214 matchPlural(p, ar, 99, 99, 2)
215 }
216 }
217
View as plain text