github.com/liquid-dev/text@v0.3.3-liquid/language/language_test.go (about)

     1  // Copyright 2013 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 language
     6  
     7  import (
     8  	"reflect"
     9  	"testing"
    10  )
    11  
    12  func TestTagSize(t *testing.T) {
    13  	id := Tag{}
    14  	typ := reflect.TypeOf(id)
    15  	if typ.Size() > 24 {
    16  		t.Errorf("size of Tag was %d; want 24", typ.Size())
    17  	}
    18  }
    19  
    20  func TestIsRoot(t *testing.T) {
    21  	loc := Tag{}
    22  	if !loc.IsRoot() {
    23  		t.Errorf("unspecified should be root.")
    24  	}
    25  	for i, tt := range parseTests() {
    26  		loc, _ := Parse(tt.in)
    27  		undef := tt.lang == "und" && tt.script == "" && tt.region == "" && tt.ext == ""
    28  		if loc.IsRoot() != undef {
    29  			t.Errorf("%d: was %v; want %v", i, loc.IsRoot(), undef)
    30  		}
    31  	}
    32  }
    33  
    34  func TestEquality(t *testing.T) {
    35  	for i, tt := range parseTests() {
    36  		s := tt.in
    37  		tag := Make(s)
    38  		t1 := Make(tag.String())
    39  		if tag != t1 {
    40  			t.Errorf("%d:%s: equality test 1 failed\n got: %#v\nwant: %#v)", i, s, t1, tag)
    41  		}
    42  		t2, _ := Compose(tag)
    43  		if tag != t2 {
    44  			t.Errorf("%d:%s: equality test 2 failed\n got: %#v\nwant: %#v", i, s, t2, tag)
    45  		}
    46  	}
    47  }
    48  
    49  func TestString(t *testing.T) {
    50  	tests := []string{
    51  		"no-u-rg-dkzzzz",
    52  	}
    53  	for i, s := range tests {
    54  		tag := Make(s)
    55  		if tag.String() != s {
    56  			t.Errorf("%d:%s: got %s: want %s (%#v)", i, s, tag.String(), s, tag)
    57  		}
    58  	}
    59  }
    60  
    61  func TestMarshal(t *testing.T) {
    62  	testCases := []string{
    63  		// TODO: these values will change with each CLDR update. This issue
    64  		// will be solved if we decide to fix the indexes.
    65  		"und",
    66  		"ca-ES-valencia",
    67  		"ca-ES-valencia-u-va-posix",
    68  		"ca-ES-valencia-u-co-phonebk",
    69  		"ca-ES-valencia-u-co-phonebk-va-posix",
    70  		"x-klingon",
    71  		"en-US",
    72  		"en-US-u-va-posix",
    73  		"en",
    74  		"en-u-co-phonebk",
    75  		"en-001",
    76  		"sh",
    77  
    78  		"en-GB-u-rg-uszzzz",
    79  		"en-GB-u-rg-uszzzz-va-posix",
    80  		"en-GB-u-co-phonebk-rg-uszzzz",
    81  		// Invalid tags should also roundtrip.
    82  		"en-GB-u-co-phonebk-rg-uszz",
    83  	}
    84  	for _, tc := range testCases {
    85  		var tag Tag
    86  		err := tag.UnmarshalText([]byte(tc))
    87  		if err != nil {
    88  			t.Errorf("UnmarshalText(%q): unexpected error: %v", tc, err)
    89  		}
    90  		b, err := tag.MarshalText()
    91  		if err != nil {
    92  			t.Errorf("MarshalText(%q): unexpected error: %v", tc, err)
    93  		}
    94  		if got := string(b); got != tc {
    95  			t.Errorf("%s: got %q; want %q", tc, got, tc)
    96  		}
    97  	}
    98  }
    99  
   100  func TestBase(t *testing.T) {
   101  	tests := []struct {
   102  		loc, lang string
   103  		conf      Confidence
   104  	}{
   105  		{"und", "en", Low},
   106  		{"x-abc", "und", No},
   107  		{"en", "en", Exact},
   108  		{"und-Cyrl", "ru", High},
   109  		// If a region is not included, the official language should be English.
   110  		{"und-US", "en", High},
   111  		// TODO: not-explicitly listed scripts should probably be und, No
   112  		// Modify addTags to return info on how the match was derived.
   113  		// {"und-Aghb", "und", No},
   114  	}
   115  	for i, tt := range tests {
   116  		loc, _ := Parse(tt.loc)
   117  		lang, conf := loc.Base()
   118  		if lang.String() != tt.lang {
   119  			t.Errorf("%d: language was %s; want %s", i, lang, tt.lang)
   120  		}
   121  		if conf != tt.conf {
   122  			t.Errorf("%d: confidence was %d; want %d", i, conf, tt.conf)
   123  		}
   124  	}
   125  }
   126  
   127  func TestParseBase(t *testing.T) {
   128  	tests := []struct {
   129  		in  string
   130  		out string
   131  		ok  bool
   132  	}{
   133  		{"en", "en", true},
   134  		{"EN", "en", true},
   135  		{"nld", "nl", true},
   136  		{"dut", "dut", true},  // bibliographic
   137  		{"aaj", "und", false}, // unknown
   138  		{"qaa", "qaa", true},
   139  		{"a", "und", false},
   140  		{"", "und", false},
   141  		{"aaaa", "und", false},
   142  	}
   143  	for i, tt := range tests {
   144  		x, err := ParseBase(tt.in)
   145  		if x.String() != tt.out || err == nil != tt.ok {
   146  			t.Errorf("%d:%s: was %s, %v; want %s, %v", i, tt.in, x, err == nil, tt.out, tt.ok)
   147  		}
   148  		if y, _, _ := Raw.Make(tt.out).Raw(); x != y {
   149  			t.Errorf("%d:%s: tag was %s; want %s", i, tt.in, x, y)
   150  		}
   151  	}
   152  }
   153  
   154  func TestScript(t *testing.T) {
   155  	tests := []struct {
   156  		loc, scr string
   157  		conf     Confidence
   158  	}{
   159  		{"und", "Latn", Low},
   160  		{"en-Latn", "Latn", Exact},
   161  		{"en", "Latn", High},
   162  		{"sr", "Cyrl", Low},
   163  		{"kk", "Cyrl", High},
   164  		{"kk-CN", "Arab", Low},
   165  		{"cmn", "Hans", Low},
   166  		{"ru", "Cyrl", High},
   167  		{"ru-RU", "Cyrl", High},
   168  		{"yue", "Hant", Low},
   169  		{"x-abc", "Zzzz", Low},
   170  		{"und-zyyy", "Zyyy", Exact},
   171  	}
   172  	for i, tt := range tests {
   173  		loc, _ := Parse(tt.loc)
   174  		sc, conf := loc.Script()
   175  		if sc.String() != tt.scr {
   176  			t.Errorf("%d:%s: script was %s; want %s", i, tt.loc, sc, tt.scr)
   177  		}
   178  		if conf != tt.conf {
   179  			t.Errorf("%d:%s: confidence was %d; want %d", i, tt.loc, conf, tt.conf)
   180  		}
   181  	}
   182  }
   183  
   184  func TestParseScript(t *testing.T) {
   185  	tests := []struct {
   186  		in  string
   187  		out string
   188  		ok  bool
   189  	}{
   190  		{"Latn", "Latn", true},
   191  		{"zzzz", "Zzzz", true},
   192  		{"zyyy", "Zyyy", true},
   193  		{"Latm", "Zzzz", false},
   194  		{"Zzz", "Zzzz", false},
   195  		{"", "Zzzz", false},
   196  		{"Zzzxx", "Zzzz", false},
   197  	}
   198  	for i, tt := range tests {
   199  		x, err := ParseScript(tt.in)
   200  		if x.String() != tt.out || err == nil != tt.ok {
   201  			t.Errorf("%d:%s: was %s, %v; want %s, %v", i, tt.in, x, err == nil, tt.out, tt.ok)
   202  		}
   203  		if err == nil {
   204  			if _, y, _ := Raw.Make("und-" + tt.out).Raw(); x != y {
   205  				t.Errorf("%d:%s: tag was %s; want %s", i, tt.in, x, y)
   206  			}
   207  		}
   208  	}
   209  }
   210  
   211  func TestRegion(t *testing.T) {
   212  	tests := []struct {
   213  		loc, reg string
   214  		conf     Confidence
   215  	}{
   216  		{"und", "US", Low},
   217  		{"en", "US", Low},
   218  		{"zh-Hant", "TW", Low},
   219  		{"en-US", "US", Exact},
   220  		{"cmn", "CN", Low},
   221  		{"ru", "RU", Low},
   222  		{"yue", "HK", Low},
   223  		{"x-abc", "ZZ", Low},
   224  	}
   225  	for i, tt := range tests {
   226  		loc, _ := Raw.Parse(tt.loc)
   227  		reg, conf := loc.Region()
   228  		if reg.String() != tt.reg {
   229  			t.Errorf("%d:%s: region was %s; want %s", i, tt.loc, reg, tt.reg)
   230  		}
   231  		if conf != tt.conf {
   232  			t.Errorf("%d:%s: confidence was %d; want %d", i, tt.loc, conf, tt.conf)
   233  		}
   234  	}
   235  }
   236  
   237  func TestEncodeM49(t *testing.T) {
   238  	tests := []struct {
   239  		m49  int
   240  		code string
   241  		ok   bool
   242  	}{
   243  		{1, "001", true},
   244  		{840, "US", true},
   245  		{899, "ZZ", false},
   246  	}
   247  	for i, tt := range tests {
   248  		if r, err := EncodeM49(tt.m49); r.String() != tt.code || err == nil != tt.ok {
   249  			t.Errorf("%d:%d: was %s, %v; want %s, %v", i, tt.m49, r, err == nil, tt.code, tt.ok)
   250  		}
   251  	}
   252  	for i := 1; i <= 1000; i++ {
   253  		if r, err := EncodeM49(i); err == nil && r.M49() == 0 {
   254  			t.Errorf("%d has no error, but maps to undefined region", i)
   255  		}
   256  	}
   257  }
   258  
   259  func TestParseRegion(t *testing.T) {
   260  	tests := []struct {
   261  		in  string
   262  		out string
   263  		ok  bool
   264  	}{
   265  		{"001", "001", true},
   266  		{"840", "US", true},
   267  		{"899", "ZZ", false},
   268  		{"USA", "US", true},
   269  		{"US", "US", true},
   270  		{"BC", "ZZ", false},
   271  		{"C", "ZZ", false},
   272  		{"CCCC", "ZZ", false},
   273  		{"01", "ZZ", false},
   274  	}
   275  	for i, tt := range tests {
   276  		r, err := ParseRegion(tt.in)
   277  		if r.String() != tt.out || err == nil != tt.ok {
   278  			t.Errorf("%d:%s: was %s, %v; want %s, %v", i, tt.in, r, err == nil, tt.out, tt.ok)
   279  		}
   280  		if err == nil {
   281  			if _, _, y := Raw.Make("und-" + tt.out).Raw(); r != y {
   282  				t.Errorf("%d:%s: tag was %s; want %s", i, tt.in, r, y)
   283  			}
   284  		}
   285  	}
   286  }
   287  
   288  func TestIsCountry(t *testing.T) {
   289  	tests := []struct {
   290  		reg     string
   291  		country bool
   292  	}{
   293  		{"US", true},
   294  		{"001", false},
   295  		{"958", false},
   296  		{"419", false},
   297  		{"203", true},
   298  		{"020", true},
   299  		{"900", false},
   300  		{"999", false},
   301  		{"QO", false},
   302  		{"EU", false},
   303  		{"AA", false},
   304  		{"XK", true},
   305  	}
   306  	for i, tt := range tests {
   307  		r, _ := ParseRegion(tt.reg)
   308  		if r.IsCountry() != tt.country {
   309  			t.Errorf("%d: IsCountry(%s) was %v; want %v", i, tt.reg, r.IsCountry(), tt.country)
   310  		}
   311  	}
   312  }
   313  
   314  func TestIsGroup(t *testing.T) {
   315  	tests := []struct {
   316  		reg   string
   317  		group bool
   318  	}{
   319  		{"US", false},
   320  		{"001", true},
   321  		{"958", false},
   322  		{"419", true},
   323  		{"203", false},
   324  		{"020", false},
   325  		{"900", false},
   326  		{"999", false},
   327  		{"QO", true},
   328  		{"EU", true},
   329  		{"AA", false},
   330  		{"XK", false},
   331  	}
   332  	for i, tt := range tests {
   333  		r, _ := ParseRegion(tt.reg)
   334  		if r.IsGroup() != tt.group {
   335  			t.Errorf("%d: IsGroup(%s) was %v; want %v", i, tt.reg, r.IsGroup(), tt.group)
   336  		}
   337  	}
   338  }
   339  
   340  func TestContains(t *testing.T) {
   341  	tests := []struct {
   342  		enclosing, contained string
   343  		contains             bool
   344  	}{
   345  		// A region contains itself.
   346  		{"US", "US", true},
   347  		{"001", "001", true},
   348  
   349  		// Direct containment.
   350  		{"001", "002", true},
   351  		{"039", "XK", true},
   352  		{"150", "XK", true},
   353  		{"EU", "AT", true},
   354  		{"QO", "AQ", true},
   355  
   356  		// Indirect containemnt.
   357  		{"001", "US", true},
   358  		{"001", "419", true},
   359  		{"001", "013", true},
   360  
   361  		// No containment.
   362  		{"US", "001", false},
   363  		{"155", "EU", false},
   364  	}
   365  	for i, tt := range tests {
   366  		r := MustParseRegion(tt.enclosing)
   367  		con := MustParseRegion(tt.contained)
   368  		if got := r.Contains(con); got != tt.contains {
   369  			t.Errorf("%d: %s.Contains(%s) was %v; want %v", i, tt.enclosing, tt.contained, got, tt.contains)
   370  		}
   371  	}
   372  }
   373  
   374  func TestRegionCanonicalize(t *testing.T) {
   375  	for i, tt := range []struct{ in, out string }{
   376  		{"UK", "GB"},
   377  		{"TP", "TL"},
   378  		{"QU", "EU"},
   379  		{"SU", "SU"},
   380  		{"VD", "VN"},
   381  		{"DD", "DE"},
   382  	} {
   383  		r := MustParseRegion(tt.in)
   384  		want := MustParseRegion(tt.out)
   385  		if got := r.Canonicalize(); got != want {
   386  			t.Errorf("%d: got %v; want %v", i, got, want)
   387  		}
   388  	}
   389  }
   390  
   391  func TestRegionTLD(t *testing.T) {
   392  	for _, tt := range []struct {
   393  		in, out string
   394  		ok      bool
   395  	}{
   396  		{"EH", "EH", true},
   397  		{"FR", "FR", true},
   398  		{"TL", "TL", true},
   399  
   400  		// In ccTLD before in ISO.
   401  		{"GG", "GG", true},
   402  
   403  		// Non-standard assignment of ccTLD to ISO code.
   404  		{"GB", "UK", true},
   405  
   406  		// Exceptionally reserved in ISO and valid ccTLD.
   407  		{"UK", "UK", true},
   408  		{"AC", "AC", true},
   409  		{"EU", "EU", true},
   410  		{"SU", "SU", true},
   411  
   412  		// Exceptionally reserved in ISO and invalid ccTLD.
   413  		{"CP", "ZZ", false},
   414  		{"DG", "ZZ", false},
   415  		{"EA", "ZZ", false},
   416  		{"FX", "ZZ", false},
   417  		{"IC", "ZZ", false},
   418  		{"TA", "ZZ", false},
   419  
   420  		// Transitionally reserved in ISO (e.g. deprecated) but valid ccTLD as
   421  		// it is still being phased out.
   422  		{"AN", "AN", true},
   423  		{"TP", "TP", true},
   424  
   425  		// Transitionally reserved in ISO (e.g. deprecated) and invalid ccTLD.
   426  		// Defined in package language as it has a mapping in CLDR.
   427  		{"BU", "ZZ", false},
   428  		{"CS", "ZZ", false},
   429  		{"NT", "ZZ", false},
   430  		{"YU", "ZZ", false},
   431  		{"ZR", "ZZ", false},
   432  		// Not defined in package: SF.
   433  
   434  		// Indeterminately reserved in ISO.
   435  		// Defined in package language as it has a legacy mapping in CLDR.
   436  		{"DY", "ZZ", false},
   437  		{"RH", "ZZ", false},
   438  		{"VD", "ZZ", false},
   439  		// Not defined in package: EW, FL, JA, LF, PI, RA, RB, RC, RI, RL, RM,
   440  		// RN, RP, WG, WL, WV, and YV.
   441  
   442  		// Not assigned in ISO, but legacy definitions in CLDR.
   443  		{"DD", "ZZ", false},
   444  		{"YD", "ZZ", false},
   445  
   446  		// Normal mappings but somewhat special status in ccTLD.
   447  		{"BL", "BL", true},
   448  		{"MF", "MF", true},
   449  		{"BV", "BV", true},
   450  		{"SJ", "SJ", true},
   451  
   452  		// Have values when normalized, but not as is.
   453  		{"QU", "ZZ", false},
   454  
   455  		// ISO Private Use.
   456  		{"AA", "ZZ", false},
   457  		{"QM", "ZZ", false},
   458  		{"QO", "ZZ", false},
   459  		{"XA", "ZZ", false},
   460  		{"XK", "ZZ", false}, // Sometimes used for Kosovo, but invalid ccTLD.
   461  	} {
   462  		if tt.in == "" {
   463  			continue
   464  		}
   465  
   466  		r := MustParseRegion(tt.in)
   467  		var want Region
   468  		if tt.out != "ZZ" {
   469  			want = MustParseRegion(tt.out)
   470  		}
   471  		tld, err := r.TLD()
   472  		if got := err == nil; got != tt.ok {
   473  			t.Errorf("error(%v): got %v; want %v", r, got, tt.ok)
   474  		}
   475  		if tld != want {
   476  			t.Errorf("TLD(%v): got %v; want %v", r, tld, want)
   477  		}
   478  	}
   479  }
   480  
   481  func TestCanonicalize(t *testing.T) {
   482  	// TODO: do a full test using CLDR data in a separate regression test.
   483  	tests := []struct {
   484  		in, out string
   485  		option  CanonType
   486  	}{
   487  		{"en-Latn", "en", SuppressScript},
   488  		{"sr-Cyrl", "sr-Cyrl", SuppressScript},
   489  		{"sh", "sr-Latn", Legacy},
   490  		{"sh-HR", "sr-Latn-HR", Legacy},
   491  		{"sh-Cyrl-HR", "sr-Cyrl-HR", Legacy},
   492  		{"tl", "fil", Legacy},
   493  		{"no", "no", Legacy},
   494  		{"no", "nb", Legacy | CLDR},
   495  		{"cmn", "cmn", Legacy},
   496  		{"cmn", "zh", Macro},
   497  		{"cmn-u-co-stroke", "zh-u-co-stroke", Macro},
   498  		{"yue", "yue", Macro},
   499  		{"nb", "no", Macro},
   500  		{"nb", "nb", Macro | CLDR},
   501  		{"no", "no", Macro},
   502  		{"no", "no", Macro | CLDR},
   503  		{"iw", "he", DeprecatedBase},
   504  		{"iw", "he", Deprecated | CLDR},
   505  		{"mo", "ro-MD", Deprecated}, // Adopted by CLDR as of version 25.
   506  		{"alb", "sq", Legacy},       // bibliographic
   507  		{"dut", "nl", Legacy},       // bibliographic
   508  		// As of CLDR 25, mo is no longer considered a legacy mapping.
   509  		{"mo", "mo", Legacy | CLDR},
   510  		{"und-AN", "und-AN", Deprecated},
   511  		{"und-YD", "und-YE", DeprecatedRegion},
   512  		{"und-YD", "und-YD", DeprecatedBase},
   513  		{"und-Qaai", "und-Zinh", DeprecatedScript},
   514  		{"und-Qaai", "und-Qaai", DeprecatedBase},
   515  		{"drh", "mn", All}, // drh -> khk -> mn
   516  
   517  		{"en-GB-u-rg-uszzzz", "en-GB-u-rg-uszzzz", Raw},
   518  		{"en-GB-u-rg-USZZZZ", "en-GB-u-rg-uszzzz", Raw},
   519  		// TODO: use different exact values for language and regional tag?
   520  		{"en-GB-u-rg-uszzzz-va-posix", "en-GB-u-rg-uszzzz-va-posix", Raw},
   521  		{"en-GB-u-rg-uszzzz-co-phonebk", "en-GB-u-co-phonebk-rg-uszzzz", Raw},
   522  		// Invalid region specifications are left as is.
   523  		{"en-GB-u-rg-usz", "en-GB-u-rg-usz", Raw},
   524  		{"en-GB-u-rg-usz-va-posix", "en-GB-u-rg-usz-va-posix", Raw},
   525  		{"en-GB-u-rg-usz-co-phonebk", "en-GB-u-co-phonebk-rg-usz", Raw},
   526  	}
   527  	for i, tt := range tests {
   528  		in, _ := Raw.Parse(tt.in)
   529  		in, _ = tt.option.Canonicalize(in)
   530  		if in.String() != tt.out {
   531  			t.Errorf("%d:%s: was %s; want %s", i, tt.in, in.String(), tt.out)
   532  		}
   533  	}
   534  	// Test idempotence.
   535  	for _, base := range Supported.BaseLanguages() {
   536  		tag, _ := Raw.Compose(base)
   537  		got, _ := All.Canonicalize(tag)
   538  		want, _ := All.Canonicalize(got)
   539  		if got != want {
   540  			t.Errorf("idem(%s): got %s; want %s", tag, got, want)
   541  		}
   542  	}
   543  }
   544  
   545  func TestTypeForKey(t *testing.T) {
   546  	tests := []struct{ key, in, out string }{
   547  		{"co", "en", ""},
   548  		{"co", "en-u-abc", ""},
   549  		{"co", "en-u-co-phonebk", "phonebk"},
   550  		{"co", "en-u-co-phonebk-cu-aud", "phonebk"},
   551  		{"co", "x-foo-u-co-phonebk", ""},
   552  		{"va", "en-US-u-va-posix", "posix"},
   553  		{"rg", "en-u-rg-gbzzzz", "gbzzzz"},
   554  		{"nu", "en-u-co-phonebk-nu-arabic", "arabic"},
   555  		{"kc", "cmn-u-co-stroke", ""},
   556  	}
   557  	for _, tt := range tests {
   558  		if v := Make(tt.in).TypeForKey(tt.key); v != tt.out {
   559  			t.Errorf("%q[%q]: was %q; want %q", tt.in, tt.key, v, tt.out)
   560  		}
   561  	}
   562  }
   563  
   564  func TestParent(t *testing.T) {
   565  	tests := []struct{ in, out string }{
   566  		// Strip variants and extensions first
   567  		{"de-u-co-phonebk", "de"},
   568  		{"de-1994", "de"},
   569  		{"de-Latn-1994", "de"}, // remove superfluous script.
   570  
   571  		// Ensure the canonical Tag for an entry is in the chain for base-script
   572  		// pairs.
   573  		{"zh-Hans", "zh"},
   574  
   575  		// Skip the script if it is the maximized version. CLDR files for the
   576  		// skipped tag are always empty.
   577  		{"zh-Hans-TW", "zh"},
   578  		{"zh-Hans-CN", "zh"},
   579  
   580  		// Insert the script if the maximized script is not the same as the
   581  		// maximized script of the base language.
   582  		{"zh-TW", "zh-Hant"},
   583  		{"zh-HK", "zh-Hant"},
   584  		{"zh-Hant-TW", "zh-Hant"},
   585  		{"zh-Hant-HK", "zh-Hant"},
   586  
   587  		// Non-default script skips to und.
   588  		// CLDR
   589  		{"az-Cyrl", "und"},
   590  		{"bs-Cyrl", "und"},
   591  		{"en-Dsrt", "und"},
   592  		{"ha-Arab", "und"},
   593  		{"mn-Mong", "und"},
   594  		{"pa-Arab", "und"},
   595  		{"shi-Latn", "und"},
   596  		{"sr-Latn", "und"},
   597  		{"uz-Arab", "und"},
   598  		{"uz-Cyrl", "und"},
   599  		{"vai-Latn", "und"},
   600  		{"zh-Hant", "und"},
   601  		// extra
   602  		{"nl-Cyrl", "und"},
   603  
   604  		// World english inherits from en-001.
   605  		{"en-150", "en-001"},
   606  		{"en-AU", "en-001"},
   607  		{"en-BE", "en-001"},
   608  		{"en-GG", "en-001"},
   609  		{"en-GI", "en-001"},
   610  		{"en-HK", "en-001"},
   611  		{"en-IE", "en-001"},
   612  		{"en-IM", "en-001"},
   613  		{"en-IN", "en-001"},
   614  		{"en-JE", "en-001"},
   615  		{"en-MT", "en-001"},
   616  		{"en-NZ", "en-001"},
   617  		{"en-PK", "en-001"},
   618  		{"en-SG", "en-001"},
   619  
   620  		// Spanish in Latin-American countries have es-419 as parent.
   621  		{"es-AR", "es-419"},
   622  		{"es-BO", "es-419"},
   623  		{"es-CL", "es-419"},
   624  		{"es-CO", "es-419"},
   625  		{"es-CR", "es-419"},
   626  		{"es-CU", "es-419"},
   627  		{"es-DO", "es-419"},
   628  		{"es-EC", "es-419"},
   629  		{"es-GT", "es-419"},
   630  		{"es-HN", "es-419"},
   631  		{"es-MX", "es-419"},
   632  		{"es-NI", "es-419"},
   633  		{"es-PA", "es-419"},
   634  		{"es-PE", "es-419"},
   635  		{"es-PR", "es-419"},
   636  		{"es-PY", "es-419"},
   637  		{"es-SV", "es-419"},
   638  		{"es-US", "es-419"},
   639  		{"es-UY", "es-419"},
   640  		{"es-VE", "es-419"},
   641  		// exceptions (according to CLDR)
   642  		{"es-CW", "es"},
   643  
   644  		// Inherit from pt-PT, instead of pt for these countries.
   645  		{"pt-AO", "pt-PT"},
   646  		{"pt-CV", "pt-PT"},
   647  		{"pt-GW", "pt-PT"},
   648  		{"pt-MO", "pt-PT"},
   649  		{"pt-MZ", "pt-PT"},
   650  		{"pt-ST", "pt-PT"},
   651  		{"pt-TL", "pt-PT"},
   652  
   653  		{"en-GB-u-co-phonebk-rg-uszzzz", "en-GB"},
   654  		{"en-GB-u-rg-uszzzz", "en-GB"},
   655  		{"en-US-u-va-posix", "en-US"},
   656  
   657  		// Difference between language and regional tag.
   658  		{"ca-ES-valencia", "ca-ES"},
   659  		{"ca-ES-valencia-u-rg-ptzzzz", "ca-ES"},
   660  		{"en-US-u-va-variant", "en-US"},
   661  		{"en-u-va-variant", "en"},
   662  		{"en-u-rg-gbzzzz", "en"},
   663  		{"en-US-u-rg-gbzzzz", "en-US"},
   664  		{"nl-US-u-rg-gbzzzz", "nl-US"},
   665  	}
   666  	for _, tt := range tests {
   667  		tag := Raw.MustParse(tt.in)
   668  		if p := Raw.MustParse(tt.out); p != tag.Parent() {
   669  			t.Errorf("%s: was %v; want %v", tt.in, tag.Parent(), p)
   670  		}
   671  	}
   672  }
   673  
   674  var (
   675  	// Tags without error that don't need to be changed.
   676  	benchBasic = []string{
   677  		"en",
   678  		"en-Latn",
   679  		"en-GB",
   680  		"za",
   681  		"zh-Hant",
   682  		"zh",
   683  		"zh-HK",
   684  		"ar-MK",
   685  		"en-CA",
   686  		"fr-CA",
   687  		"fr-CH",
   688  		"fr",
   689  		"lv",
   690  		"he-IT",
   691  		"tlh",
   692  		"ja",
   693  		"ja-Jpan",
   694  		"ja-Jpan-JP",
   695  		"de-1996",
   696  		"de-CH",
   697  		"sr",
   698  		"sr-Latn",
   699  	}
   700  	// Tags with extensions, not changes required.
   701  	benchExt = []string{
   702  		"x-a-b-c-d",
   703  		"x-aa-bbbb-cccccccc-d",
   704  		"en-x_cc-b-bbb-a-aaa",
   705  		"en-c_cc-b-bbb-a-aaa-x-x",
   706  		"en-u-co-phonebk",
   707  		"en-Cyrl-u-co-phonebk",
   708  		"en-US-u-co-phonebk-cu-xau",
   709  		"en-nedix-u-co-phonebk",
   710  		"en-t-t0-abcd",
   711  		"en-t-nl-latn",
   712  		"en-t-t0-abcd-x-a",
   713  	}
   714  	// Change, but not memory allocation required.
   715  	benchSimpleChange = []string{
   716  		"EN",
   717  		"i-klingon",
   718  		"en-latn",
   719  		"zh-cmn-Hans-CN",
   720  		"iw-NL",
   721  	}
   722  	// Change and memory allocation required.
   723  	benchChangeAlloc = []string{
   724  		"en-c_cc-b-bbb-a-aaa",
   725  		"en-u-cu-xua-co-phonebk",
   726  		"en-u-cu-xua-co-phonebk-a-cd",
   727  		"en-u-def-abc-cu-xua-co-phonebk",
   728  		"en-t-en-Cyrl-NL-1994",
   729  		"en-t-en-Cyrl-NL-1994-t0-abc-def",
   730  	}
   731  	// Tags that result in errors.
   732  	benchErr = []string{
   733  		// IllFormed
   734  		"x_A.-B-C_D",
   735  		"en-u-cu-co-phonebk",
   736  		"en-u-cu-xau-co",
   737  		"en-t-nl-abcd",
   738  		// Invalid
   739  		"xx",
   740  		"nl-Uuuu",
   741  		"nl-QB",
   742  	}
   743  	benchChange = append(benchSimpleChange, benchChangeAlloc...)
   744  	benchAll    = append(append(append(benchBasic, benchExt...), benchChange...), benchErr...)
   745  )
   746  
   747  func doParse(b *testing.B, tag []string) {
   748  	for i := 0; i < b.N; i++ {
   749  		// Use the modulo instead of looping over all tags so that we get a somewhat
   750  		// meaningful ns/op.
   751  		Parse(tag[i%len(tag)])
   752  	}
   753  }
   754  
   755  func BenchmarkParse(b *testing.B) {
   756  	doParse(b, benchAll)
   757  }
   758  
   759  func BenchmarkParseBasic(b *testing.B) {
   760  	doParse(b, benchBasic)
   761  }
   762  
   763  func BenchmarkParseError(b *testing.B) {
   764  	doParse(b, benchErr)
   765  }
   766  
   767  func BenchmarkParseSimpleChange(b *testing.B) {
   768  	doParse(b, benchSimpleChange)
   769  }
   770  
   771  func BenchmarkParseChangeAlloc(b *testing.B) {
   772  	doParse(b, benchChangeAlloc)
   773  }