1
2
3
4
5 package cldrtree
6
7 import (
8 "bytes"
9 "flag"
10 "log"
11 "math/rand"
12 "os"
13 "path/filepath"
14 "reflect"
15 "regexp"
16 "strconv"
17 "strings"
18 "testing"
19
20 "golang.org/x/text/internal/gen"
21 "golang.org/x/text/internal/language/compact"
22 "golang.org/x/text/language"
23 "golang.org/x/text/unicode/cldr"
24 )
25
26 var genOutput = flag.Bool("gen", false, "generate output files")
27
28 func TestAliasRegexp(t *testing.T) {
29 testCases := []struct {
30 alias string
31 want []string
32 }{{
33 alias: "miscPatterns[@numberSystem='latn']",
34 want: []string{
35 "miscPatterns[@numberSystem='latn']",
36 "miscPatterns",
37 "[@numberSystem='latn']",
38 "numberSystem",
39 "latn",
40 },
41 }, {
42 alias: `calendar[@type='greg-foo']/days/`,
43 want: []string{
44 "calendar[@type='greg-foo']",
45 "calendar",
46 "[@type='greg-foo']",
47 "type",
48 "greg-foo",
49 },
50 }, {
51 alias: "eraAbbr",
52 want: []string{
53 "eraAbbr",
54 "eraAbbr",
55 "",
56 "",
57 "",
58 },
59 }, {
60
61 alias: `../calendar[@type='gregorian']/days/`,
62 }}
63 for _, tc := range testCases {
64 t.Run(tc.alias, func(t *testing.T) {
65 got := aliasRe.FindStringSubmatch(tc.alias)
66 if !reflect.DeepEqual(got, tc.want) {
67 t.Errorf("got %v; want %v", got, tc.want)
68 }
69 })
70 }
71 }
72
73 func TestBuild(t *testing.T) {
74 tree1, _ := loadTestdata(t, "test1")
75 tree2, _ := loadTestdata(t, "test2")
76
77
78 const (
79 calendar = iota
80 field
81 )
82 const (
83 month = iota
84 era
85 filler
86 cyclicNameSet
87 )
88 const (
89 abbreviated = iota
90 narrow
91 wide
92 )
93
94 testCases := []struct {
95 desc string
96 tree *Tree
97 locale string
98 path []uint16
99 isFeature bool
100 result string
101 }{{
102 desc: "und/chinese month format wide m1",
103 tree: tree1,
104 locale: "und",
105 path: path(calendar, 0, month, 0, wide, 1),
106 result: "cM01",
107 }, {
108 desc: "und/chinese month format wide m12",
109 tree: tree1,
110 locale: "und",
111 path: path(calendar, 0, month, 0, wide, 12),
112 result: "cM12",
113 }, {
114 desc: "und/non-existing value",
115 tree: tree1,
116 locale: "und",
117 path: path(calendar, 0, month, 0, wide, 13),
118 result: "",
119 }, {
120 desc: "und/dangi:chinese month format wide",
121 tree: tree1,
122 locale: "und",
123 path: path(calendar, 1, month, 0, wide, 1),
124 result: "cM01",
125 }, {
126 desc: "und/chinese month format abbreviated:wide",
127 tree: tree1,
128 locale: "und",
129 path: path(calendar, 0, month, 0, abbreviated, 1),
130 result: "cM01",
131 }, {
132 desc: "und/chinese month format narrow:wide",
133 tree: tree1,
134 locale: "und",
135 path: path(calendar, 0, month, 0, narrow, 1),
136 result: "cM01",
137 }, {
138 desc: "und/gregorian month format wide",
139 tree: tree1,
140 locale: "und",
141 path: path(calendar, 2, month, 0, wide, 2),
142 result: "gM02",
143 }, {
144 desc: "und/gregorian month format:stand-alone narrow",
145 tree: tree1,
146 locale: "und",
147 path: path(calendar, 2, month, 0, narrow, 1),
148 result: "1",
149 }, {
150 desc: "und/gregorian month stand-alone:format abbreviated",
151 tree: tree1,
152 locale: "und",
153 path: path(calendar, 2, month, 1, abbreviated, 1),
154 result: "gM01",
155 }, {
156 desc: "und/gregorian month stand-alone:format wide ",
157 tree: tree1,
158 locale: "und",
159 path: path(calendar, 2, month, 1, abbreviated, 1),
160 result: "gM01",
161 }, {
162 desc: "und/dangi:chinese month format narrow:wide ",
163 tree: tree1,
164 locale: "und",
165 path: path(calendar, 1, month, 0, narrow, 4),
166 result: "cM04",
167 }, {
168 desc: "und/field era displayname 0",
169 tree: tree2,
170 locale: "und",
171 path: path(field, 0, 0, 0),
172 result: "Era",
173 }, {
174 desc: "en/field era displayname 0",
175 tree: tree2,
176 locale: "en",
177 path: path(field, 0, 0, 0),
178 result: "era",
179 }, {
180 desc: "und/calendar hebrew format wide 7-leap",
181 tree: tree2,
182 locale: "und",
183 path: path(calendar, 7, month, 0, wide, 0),
184 result: "Adar II",
185 }, {
186 desc: "en-GB:en-001:en:und/calendar hebrew format wide 7-leap",
187 tree: tree2,
188 locale: "en-GB",
189 path: path(calendar, 7, month, 0, wide, 0),
190 result: "Adar II",
191 }, {
192 desc: "und/buddhist month format wide 11",
193 tree: tree2,
194 locale: "und",
195 path: path(calendar, 0, month, 0, wide, 12),
196 result: "genWideM12",
197 }, {
198 desc: "en-GB/gregorian month stand-alone narrow 2",
199 tree: tree2,
200 locale: "en-GB",
201 path: path(calendar, 6, month, 1, narrow, 3),
202 result: "gbNarrowM3",
203 }, {
204 desc: "en-GB/gregorian month format narrow 3/missing in en-GB",
205 tree: tree2,
206 locale: "en-GB",
207 path: path(calendar, 6, month, 0, narrow, 4),
208 result: "enNarrowM4",
209 }, {
210 desc: "en-GB/gregorian month format narrow 3/missing in en and en-GB",
211 tree: tree2,
212 locale: "en-GB",
213 path: path(calendar, 6, month, 0, narrow, 7),
214 result: "gregNarrowM7",
215 }, {
216 desc: "en-GB/gregorian month format narrow 3/missing in en and en-GB",
217 tree: tree2,
218 locale: "en-GB",
219 path: path(calendar, 6, month, 0, narrow, 7),
220 result: "gregNarrowM7",
221 }, {
222 desc: "en-GB/gregorian era narrow",
223 tree: tree2,
224 locale: "en-GB",
225 path: path(calendar, 6, era, abbreviated, 0, 1),
226 isFeature: true,
227 result: "AD",
228 }, {
229 desc: "en-GB/gregorian era narrow",
230 tree: tree2,
231 locale: "en-GB",
232 path: path(calendar, 6, era, narrow, 0, 0),
233 isFeature: true,
234 result: "BC",
235 }, {
236 desc: "en-GB/gregorian era narrow",
237 tree: tree2,
238 locale: "en-GB",
239 path: path(calendar, 6, era, wide, 1, 0),
240 isFeature: true,
241 result: "Before Common Era",
242 }, {
243 desc: "en-GB/dangi:chinese cyclicName, months, format, narrow:abbreviated 2",
244 tree: tree2,
245 locale: "en-GB",
246 path: path(calendar, 1, cyclicNameSet, 3, 0, 1, 2),
247 isFeature: true,
248 result: "year2",
249 }, {
250 desc: "en-GB/field era-narrow ",
251 tree: tree2,
252 locale: "en-GB",
253 path: path(field, 2, 0, 0),
254 result: "era",
255 }, {
256 desc: "en-GB/field month-narrow relativeTime future one",
257 tree: tree2,
258 locale: "en-GB",
259 path: path(field, 5, 2, 0, 1),
260 isFeature: true,
261 result: "001NarrowFutMOne",
262 }, {
263
264 desc: "en-GB/field month-short relativeTime past one:other",
265 tree: tree2,
266 locale: "en-GB",
267 path: path(field, 4, 2, 1, 1),
268 isFeature: true,
269 result: "001ShortPastMOther",
270 }, {
271 desc: "en-GB/field month relativeTime future two:other",
272 tree: tree2,
273 locale: "en-GB",
274 path: path(field, 3, 2, 0, 2),
275 isFeature: true,
276 result: "enFutMOther",
277 }}
278
279 for _, tc := range testCases {
280 t.Run(tc.desc, func(t *testing.T) {
281 tag, _ := compact.RegionalID(compact.Tag(language.MustParse(tc.locale)))
282 s := tc.tree.lookup(tag, tc.isFeature, tc.path...)
283 if s != tc.result {
284 t.Errorf("got %q; want %q", s, tc.result)
285 }
286 })
287 }
288 }
289
290 func path(e ...uint16) []uint16 { return e }
291
292 func TestGen(t *testing.T) {
293 testCases := []string{"test1", "test2"}
294 for _, tc := range testCases {
295 t.Run(tc, func(t *testing.T) {
296 _, got := loadTestdata(t, tc)
297
298
299 re := regexp.MustCompile("// Size: [0-9]*")
300 got = re.ReplaceAllLiteral(got, []byte("// Size: xxxx"))
301 re = regexp.MustCompile("// Total table size [0-9]*")
302 got = re.ReplaceAllLiteral(got, []byte("// Total table size: xxxx"))
303
304 file := filepath.Join("testdata", tc, "output.go")
305 if *genOutput {
306 os.WriteFile(file, got, 0700)
307 t.SkipNow()
308 }
309
310 b, err := os.ReadFile(file)
311 if err != nil {
312 t.Fatalf("failed to open file: %v", err)
313 }
314 if want := string(b); string(got) != want {
315 t.Log(string(got))
316 t.Errorf("files differ")
317 }
318 })
319 }
320 }
321
322 func loadTestdata(t *testing.T, test string) (tree *Tree, file []byte) {
323 b := New("test")
324
325 var d cldr.Decoder
326
327 data, err := d.DecodePath(filepath.Join("testdata", test))
328 if err != nil {
329 t.Fatalf("error decoding testdata: %v", err)
330 }
331
332 context := Enum("context")
333 widthMap := func(s string) string {
334
335 if r, ok := map[string]string{
336 "eraAbbr": "abbreviated",
337 "eraNarrow": "narrow",
338 "eraNames": "wide",
339 }[s]; ok {
340 s = r
341 }
342 return "w" + strings.Title(s)
343 }
344 width := EnumFunc("width", widthMap, "abbreviated", "narrow", "wide")
345 month := Enum("month", "leap7")
346 relative := EnumFunc("relative", func(s string) string {
347 x, err := strconv.ParseInt(s, 10, 8)
348 if err != nil {
349 log.Fatal("Invalid number:", err)
350 }
351 return []string{
352 "before1",
353 "current",
354 "after1",
355 }[x+1]
356 })
357 cycleType := EnumFunc("cycleType", func(s string) string {
358 return "cyc" + strings.Title(s)
359 })
360 r := rand.New(rand.NewSource(0))
361
362 for _, loc := range data.Locales() {
363 ldml := data.RawLDML(loc)
364 x := b.Locale(language.Make(loc))
365
366 if x := x.Index(ldml.Dates.Calendars); x != nil {
367 for _, cal := range ldml.Dates.Calendars.Calendar {
368 x := x.IndexFromType(cal)
369 if x := x.Index(cal.Months); x != nil {
370 for _, mc := range cal.Months.MonthContext {
371 x := x.IndexFromType(mc, context)
372 for _, mw := range mc.MonthWidth {
373 x := x.IndexFromType(mw, width)
374 for _, m := range mw.Month {
375 x.SetValue(m.Yeartype+m.Type, m, month)
376 }
377 }
378 }
379 }
380 if x := x.Index(cal.CyclicNameSets); x != nil {
381 for _, cns := range cal.CyclicNameSets.CyclicNameSet {
382 x := x.IndexFromType(cns, cycleType)
383 for _, cc := range cns.CyclicNameContext {
384 x := x.IndexFromType(cc, context)
385 for _, cw := range cc.CyclicNameWidth {
386 x := x.IndexFromType(cw, width)
387 for _, c := range cw.CyclicName {
388 x.SetValue(c.Type, c)
389 }
390 }
391 }
392 }
393 }
394 if x := x.Index(cal.Eras); x != nil {
395 opts := []Option{width, SharedType()}
396 if x := x.Index(cal.Eras.EraNames, opts...); x != nil {
397 for _, e := range cal.Eras.EraNames.Era {
398 x.IndexFromAlt(e).SetValue(e.Type, e)
399 }
400 }
401 if x := x.Index(cal.Eras.EraAbbr, opts...); x != nil {
402 for _, e := range cal.Eras.EraAbbr.Era {
403 x.IndexFromAlt(e).SetValue(e.Type, e)
404 }
405 }
406 if x := x.Index(cal.Eras.EraNarrow, opts...); x != nil {
407 for _, e := range cal.Eras.EraNarrow.Era {
408 x.IndexFromAlt(e).SetValue(e.Type, e)
409 }
410 }
411 }
412 {
413
414 f := x.IndexWithName("filler")
415 b := make([]byte, maxStrlen)
416 opt := &options{parent: x}
417 r.Read(b)
418 f.setValue("0", string(b), opt)
419 }
420 }
421 }
422 if x := x.Index(ldml.Dates.Fields); x != nil {
423 for _, f := range ldml.Dates.Fields.Field {
424 x := x.IndexFromType(f)
425 for _, d := range f.DisplayName {
426 x.Index(d).SetValue("", d)
427 }
428 for _, r := range f.Relative {
429 x.Index(r).SetValue(r.Type, r, relative)
430 }
431 for _, rt := range f.RelativeTime {
432 x := x.Index(rt).IndexFromType(rt)
433 for _, p := range rt.RelativeTimePattern {
434 x.SetValue(p.Count, p)
435 }
436 }
437 for _, rp := range f.RelativePeriod {
438 x.Index(rp).SetValue("", rp)
439 }
440 }
441 }
442 }
443
444 tree, err = build(b)
445 if err != nil {
446 t.Fatal("error building tree:", err)
447 }
448 w := gen.NewCodeWriter()
449 generate(b, tree, w)
450 generateTestData(b, w)
451 buf := &bytes.Buffer{}
452 if _, err = w.WriteGo(buf, "test", ""); err != nil {
453 t.Log(buf.String())
454 t.Fatal("error generating code:", err)
455 }
456 return tree, buf.Bytes()
457 }
458
View as plain text