...

Source file src/golang.org/x/text/internal/cldrtree/cldrtree_test.go

Documentation: golang.org/x/text/internal/cldrtree

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     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  		// match must be anchored at beginning.
    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  	// Constants for second test
    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  		// Don't fall back to the one of "en".
   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  			// Remove sizes that may vary per architecture.
   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  		// Align era with width values.
   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  					// Ensure having more than 2 buckets.
   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