// Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cldrtree import ( "bytes" "flag" "io/ioutil" "log" "math/rand" "path/filepath" "reflect" "regexp" "strconv" "strings" "testing" "golang.org/x/text/internal/gen" "golang.org/x/text/language" "golang.org/x/text/unicode/cldr" ) var genOutput = flag.Bool("gen", false, "generate output files") func TestAliasRegexp(t *testing.T) { testCases := []struct { alias string want []string }{{ alias: "miscPatterns[@numberSystem='latn']", want: []string{ "miscPatterns[@numberSystem='latn']", "miscPatterns", "[@numberSystem='latn']", "numberSystem", "latn", }, }, { alias: `calendar[@type='greg-foo']/days/`, want: []string{ "calendar[@type='greg-foo']", "calendar", "[@type='greg-foo']", "type", "greg-foo", }, }, { alias: "eraAbbr", want: []string{ "eraAbbr", "eraAbbr", "", "", "", }, }, { // match must be anchored at beginning. alias: `../calendar[@type='gregorian']/days/`, }} for _, tc := range testCases { t.Run(tc.alias, func(t *testing.T) { got := aliasRe.FindStringSubmatch(tc.alias) if !reflect.DeepEqual(got, tc.want) { t.Errorf("got %v; want %v", got, tc.want) } }) } } func TestBuild(t *testing.T) { tree1, _ := loadTestdata(t, "test1") tree2, _ := loadTestdata(t, "test2") // Constants for second test test const ( calendar = iota field ) const ( month = iota era filler cyclicNameSet ) const ( abbreviated = iota narrow wide ) testCases := []struct { desc string tree *Tree locale string path []uint16 isFeature bool result string }{{ desc: "und/chinese month format wide m1", tree: tree1, locale: "und", path: path(calendar, 0, month, 0, wide, 1), result: "cM01", }, { desc: "und/chinese month format wide m12", tree: tree1, locale: "und", path: path(calendar, 0, month, 0, wide, 12), result: "cM12", }, { desc: "und/non-existing value", tree: tree1, locale: "und", path: path(calendar, 0, month, 0, wide, 13), result: "", }, { desc: "und/dangi:chinese month format wide", tree: tree1, locale: "und", path: path(calendar, 1, month, 0, wide, 1), result: "cM01", }, { desc: "und/chinese month format abbreviated:wide", tree: tree1, locale: "und", path: path(calendar, 0, month, 0, abbreviated, 1), result: "cM01", }, { desc: "und/chinese month format narrow:wide", tree: tree1, locale: "und", path: path(calendar, 0, month, 0, narrow, 1), result: "cM01", }, { desc: "und/gregorian month format wide", tree: tree1, locale: "und", path: path(calendar, 2, month, 0, wide, 2), result: "gM02", }, { desc: "und/gregorian month format:stand-alone narrow", tree: tree1, locale: "und", path: path(calendar, 2, month, 0, narrow, 1), result: "1", }, { desc: "und/gregorian month stand-alone:format abbreviated", tree: tree1, locale: "und", path: path(calendar, 2, month, 1, abbreviated, 1), result: "gM01", }, { desc: "und/gregorian month stand-alone:format wide ", tree: tree1, locale: "und", path: path(calendar, 2, month, 1, abbreviated, 1), result: "gM01", }, { desc: "und/dangi:chinese month format narrow:wide ", tree: tree1, locale: "und", path: path(calendar, 1, month, 0, narrow, 4), result: "cM04", }, { desc: "und/field era displayname 0", tree: tree2, locale: "und", path: path(field, 0, 0, 0), result: "Era", }, { desc: "en/field era displayname 0", tree: tree2, locale: "en", path: path(field, 0, 0, 0), result: "era", }, { desc: "und/calendar hebrew format wide 7-leap", tree: tree2, locale: "und", path: path(calendar, 7, month, 0, wide, 0), result: "Adar II", }, { desc: "en-GB:en-001:en:und/calendar hebrew format wide 7-leap", tree: tree2, locale: "en-GB", path: path(calendar, 7, month, 0, wide, 0), result: "Adar II", }, { desc: "und/buddhist month format wide 11", tree: tree2, locale: "und", path: path(calendar, 0, month, 0, wide, 12), result: "genWideM12", }, { desc: "en-GB/gregorian month stand-alone narrow 2", tree: tree2, locale: "en-GB", path: path(calendar, 6, month, 1, narrow, 3), result: "gbNarrowM3", }, { desc: "en-GB/gregorian month format narrow 3/missing in en-GB", tree: tree2, locale: "en-GB", path: path(calendar, 6, month, 0, narrow, 4), result: "enNarrowM4", }, { desc: "en-GB/gregorian month format narrow 3/missing in en and en-GB", tree: tree2, locale: "en-GB", path: path(calendar, 6, month, 0, narrow, 7), result: "gregNarrowM7", }, { desc: "en-GB/gregorian month format narrow 3/missing in en and en-GB", tree: tree2, locale: "en-GB", path: path(calendar, 6, month, 0, narrow, 7), result: "gregNarrowM7", }, { desc: "en-GB/gregorian era narrow", tree: tree2, locale: "en-GB", path: path(calendar, 6, era, abbreviated, 0, 1), isFeature: true, result: "AD", }, { desc: "en-GB/gregorian era narrow", tree: tree2, locale: "en-GB", path: path(calendar, 6, era, narrow, 0, 0), isFeature: true, result: "BC", }, { desc: "en-GB/gregorian era narrow", tree: tree2, locale: "en-GB", path: path(calendar, 6, era, wide, 1, 0), isFeature: true, result: "Before Common Era", }, { desc: "en-GB/dangi:chinese cyclicName, months, format, narrow:abbreviated 2", tree: tree2, locale: "en-GB", path: path(calendar, 1, cyclicNameSet, 3, 0, 1, 2), isFeature: true, result: "year2", }, { desc: "en-GB/field era-narrow ", tree: tree2, locale: "en-GB", path: path(field, 2, 0, 0), result: "era", }, { desc: "en-GB/field month-narrow relativeTime future one", tree: tree2, locale: "en-GB", path: path(field, 5, 2, 0, 1), isFeature: true, result: "001NarrowFutMOne", }, { // Don't fall back to the one of "en". desc: "en-GB/field month-short relativeTime past one:other", tree: tree2, locale: "en-GB", path: path(field, 4, 2, 1, 1), isFeature: true, result: "001ShortPastMOther", }, { desc: "en-GB/field month relativeTime future two:other", tree: tree2, locale: "en-GB", path: path(field, 3, 2, 0, 2), isFeature: true, result: "enFutMOther", }} for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { tag, _ := language.CompactIndex(language.MustParse(tc.locale)) s := tc.tree.lookup(tag, tc.isFeature, tc.path...) if s != tc.result { t.Errorf("got %q; want %q", s, tc.result) } }) } } func path(e ...uint16) []uint16 { return e } func TestGen(t *testing.T) { testCases := []string{"test1", "test2"} for _, tc := range testCases { t.Run(tc, func(t *testing.T) { _, got := loadTestdata(t, tc) // Remove sizes that may vary per architecture. re := regexp.MustCompile("// Size: [0-9]*") got = re.ReplaceAllLiteral(got, []byte("// Size: xxxx")) re = regexp.MustCompile("// Total table size [0-9]*") got = re.ReplaceAllLiteral(got, []byte("// Total table size: xxxx")) file := filepath.Join("testdata", tc, "output.go") if *genOutput { ioutil.WriteFile(file, got, 0700) t.SkipNow() } b, err := ioutil.ReadFile(file) if err != nil { t.Fatalf("failed to open file: %v", err) } if want := string(b); string(got) != want { t.Log(string(got)) t.Errorf("files differ") } }) } } func loadTestdata(t *testing.T, test string) (tree *Tree, file []byte) { b := New("test") var d cldr.Decoder data, err := d.DecodePath(filepath.Join("testdata", test)) if err != nil { t.Fatalf("error decoding testdata: %v", err) } context := Enum("context") widthMap := func(s string) string { // Align era with width values. if r, ok := map[string]string{ "eraAbbr": "abbreviated", "eraNarrow": "narrow", "eraNames": "wide", }[s]; ok { s = r } return "w" + strings.Title(s) } width := EnumFunc("width", widthMap, "abbreviated", "narrow", "wide") month := Enum("month", "leap7") relative := EnumFunc("relative", func(s string) string { x, err := strconv.ParseInt(s, 10, 8) if err != nil { log.Fatal("Invalid number:", err) } return []string{ "before1", "current", "after1", }[x+1] }) cycleType := EnumFunc("cycleType", func(s string) string { return "cyc" + strings.Title(s) }) r := rand.New(rand.NewSource(0)) for _, loc := range data.Locales() { ldml := data.RawLDML(loc) x := b.Locale(language.Make(loc)) if x := x.Index(ldml.Dates.Calendars); x != nil { for _, cal := range ldml.Dates.Calendars.Calendar { x := x.IndexFromType(cal) if x := x.Index(cal.Months); x != nil { for _, mc := range cal.Months.MonthContext { x := x.IndexFromType(mc, context) for _, mw := range mc.MonthWidth { x := x.IndexFromType(mw, width) for _, m := range mw.Month { x.SetValue(m.Yeartype+m.Type, m, month) } } } } if x := x.Index(cal.CyclicNameSets); x != nil { for _, cns := range cal.CyclicNameSets.CyclicNameSet { x := x.IndexFromType(cns, cycleType) for _, cc := range cns.CyclicNameContext { x := x.IndexFromType(cc, context) for _, cw := range cc.CyclicNameWidth { x := x.IndexFromType(cw, width) for _, c := range cw.CyclicName { x.SetValue(c.Type, c) } } } } } if x := x.Index(cal.Eras); x != nil { opts := []Option{width, SharedType()} if x := x.Index(cal.Eras.EraNames, opts...); x != nil { for _, e := range cal.Eras.EraNames.Era { x.IndexFromAlt(e).SetValue(e.Type, e) } } if x := x.Index(cal.Eras.EraAbbr, opts...); x != nil { for _, e := range cal.Eras.EraAbbr.Era { x.IndexFromAlt(e).SetValue(e.Type, e) } } if x := x.Index(cal.Eras.EraNarrow, opts...); x != nil { for _, e := range cal.Eras.EraNarrow.Era { x.IndexFromAlt(e).SetValue(e.Type, e) } } } { // Ensure having more than 2 buckets. f := x.IndexWithName("filler") b := make([]byte, maxStrlen) opt := &options{parent: x} r.Read(b) f.setValue("0", string(b), opt) } } } if x := x.Index(ldml.Dates.Fields); x != nil { for _, f := range ldml.Dates.Fields.Field { x := x.IndexFromType(f) for _, d := range f.DisplayName { x.Index(d).SetValue("", d) } for _, r := range f.Relative { x.Index(r).SetValue(r.Type, r, relative) } for _, rt := range f.RelativeTime { x := x.Index(rt).IndexFromType(rt) for _, p := range rt.RelativeTimePattern { x.SetValue(p.Count, p) } } for _, rp := range f.RelativePeriod { x.Index(rp).SetValue("", rp) } } } } tree, err = build(b) if err != nil { t.Fatal("error building tree:", err) } w := gen.NewCodeWriter() generate(b, tree, w) generateTestData(b, w) buf := &bytes.Buffer{} if _, err = w.WriteGo(buf, "test"); err != nil { t.Log(buf.String()) t.Fatal("error generating code:", err) } return tree, buf.Bytes() }