OSDN Git Service

fix commands
[bytom/shuttle.git] / vendor / github.com / bytom / vendor / golang.org / x / text / language / language_test.go
diff --git a/vendor/github.com/bytom/vendor/golang.org/x/text/language/language_test.go b/vendor/github.com/bytom/vendor/golang.org/x/text/language/language_test.go
new file mode 100644 (file)
index 0000000..f7c2d88
--- /dev/null
@@ -0,0 +1,878 @@
+// Copyright 2013 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 language
+
+import (
+       "reflect"
+       "testing"
+
+       "golang.org/x/text/internal/testtext"
+)
+
+func TestTagSize(t *testing.T) {
+       id := Tag{}
+       typ := reflect.TypeOf(id)
+       if typ.Size() > 24 {
+               t.Errorf("size of Tag was %d; want 24", typ.Size())
+       }
+}
+
+func TestIsRoot(t *testing.T) {
+       loc := Tag{}
+       if !loc.IsRoot() {
+               t.Errorf("unspecified should be root.")
+       }
+       for i, tt := range parseTests() {
+               loc, _ := Parse(tt.in)
+               undef := tt.lang == "und" && tt.script == "" && tt.region == "" && tt.ext == ""
+               if loc.IsRoot() != undef {
+                       t.Errorf("%d: was %v; want %v", i, loc.IsRoot(), undef)
+               }
+       }
+}
+
+func TestEquality(t *testing.T) {
+       for i, tt := range parseTests()[48:49] {
+               s := tt.in
+               tag := Make(s)
+               t1 := Make(tag.String())
+               if tag != t1 {
+                       t.Errorf("%d:%s: equality test 1 failed\n got: %#v\nwant: %#v)", i, s, t1, tag)
+               }
+               t2, _ := Compose(tag)
+               if tag != t2 {
+                       t.Errorf("%d:%s: equality test 2 failed\n got: %#v\nwant: %#v", i, s, t2, tag)
+               }
+       }
+}
+
+func TestMakeString(t *testing.T) {
+       tests := []struct{ in, out string }{
+               {"und", "und"},
+               {"und", "und-CW"},
+               {"nl", "nl-NL"},
+               {"de-1901", "nl-1901"},
+               {"de-1901", "de-Arab-1901"},
+               {"x-a-b", "de-Arab-x-a-b"},
+               {"x-a-b", "x-a-b"},
+       }
+       for i, tt := range tests {
+               id, _ := Parse(tt.in)
+               mod, _ := Parse(tt.out)
+               id.setTagsFrom(mod)
+               for j := 0; j < 2; j++ {
+                       id.remakeString()
+                       if str := id.String(); str != tt.out {
+                               t.Errorf("%d:%d: found %s; want %s", i, j, id.String(), tt.out)
+                       }
+               }
+               // The bytes to string conversion as used in remakeString
+               // occasionally measures as more than one alloc, breaking this test.
+               // To alleviate this we set the number of runs to more than 1.
+               if n := testtext.AllocsPerRun(8, id.remakeString); n > 1 {
+                       t.Errorf("%d: # allocs got %.1f; want <= 1", i, n)
+               }
+       }
+}
+
+func TestCompactIndex(t *testing.T) {
+       tests := []struct {
+               tag   string
+               index int
+               ok    bool
+       }{
+               // TODO: these values will change with each CLDR update. This issue
+               // will be solved if we decide to fix the indexes.
+               {"und", 0, true},
+               {"ca-ES-valencia", 1, true},
+               {"ca-ES-valencia-u-va-posix", 0, false},
+               {"ca-ES-valencia-u-co-phonebk", 1, true},
+               {"ca-ES-valencia-u-co-phonebk-va-posix", 0, false},
+               {"x-klingon", 0, false},
+               {"en-US", 229, true},
+               {"en-US-u-va-posix", 2, true},
+               {"en", 133, true},
+               {"en-u-co-phonebk", 133, true},
+               {"en-001", 134, true},
+               {"sh", 0, false}, // We don't normalize.
+       }
+       for _, tt := range tests {
+               x, ok := CompactIndex(Raw.MustParse(tt.tag))
+               if x != tt.index || ok != tt.ok {
+                       t.Errorf("%s: got %d, %v; want %d %v", tt.tag, x, ok, tt.index, tt.ok)
+               }
+       }
+}
+
+func TestBase(t *testing.T) {
+       tests := []struct {
+               loc, lang string
+               conf      Confidence
+       }{
+               {"und", "en", Low},
+               {"x-abc", "und", No},
+               {"en", "en", Exact},
+               {"und-Cyrl", "ru", High},
+               // If a region is not included, the official language should be English.
+               {"und-US", "en", High},
+               // TODO: not-explicitly listed scripts should probably be und, No
+               // Modify addTags to return info on how the match was derived.
+               // {"und-Aghb", "und", No},
+       }
+       for i, tt := range tests {
+               loc, _ := Parse(tt.loc)
+               lang, conf := loc.Base()
+               if lang.String() != tt.lang {
+                       t.Errorf("%d: language was %s; want %s", i, lang, tt.lang)
+               }
+               if conf != tt.conf {
+                       t.Errorf("%d: confidence was %d; want %d", i, conf, tt.conf)
+               }
+       }
+}
+
+func TestParseBase(t *testing.T) {
+       tests := []struct {
+               in  string
+               out string
+               ok  bool
+       }{
+               {"en", "en", true},
+               {"EN", "en", true},
+               {"nld", "nl", true},
+               {"dut", "dut", true},  // bibliographic
+               {"aaj", "und", false}, // unknown
+               {"qaa", "qaa", true},
+               {"a", "und", false},
+               {"", "und", false},
+               {"aaaa", "und", false},
+       }
+       for i, tt := range tests {
+               x, err := ParseBase(tt.in)
+               if x.String() != tt.out || err == nil != tt.ok {
+                       t.Errorf("%d:%s: was %s, %v; want %s, %v", i, tt.in, x, err == nil, tt.out, tt.ok)
+               }
+               if y, _, _ := Raw.Make(tt.out).Raw(); x != y {
+                       t.Errorf("%d:%s: tag was %s; want %s", i, tt.in, x, y)
+               }
+       }
+}
+
+func TestScript(t *testing.T) {
+       tests := []struct {
+               loc, scr string
+               conf     Confidence
+       }{
+               {"und", "Latn", Low},
+               {"en-Latn", "Latn", Exact},
+               {"en", "Latn", High},
+               {"sr", "Cyrl", Low},
+               {"kk", "Cyrl", High},
+               {"kk-CN", "Arab", Low},
+               {"cmn", "Hans", Low},
+               {"ru", "Cyrl", High},
+               {"ru-RU", "Cyrl", High},
+               {"yue", "Hant", Low},
+               {"x-abc", "Zzzz", Low},
+               {"und-zyyy", "Zyyy", Exact},
+       }
+       for i, tt := range tests {
+               loc, _ := Parse(tt.loc)
+               sc, conf := loc.Script()
+               if sc.String() != tt.scr {
+                       t.Errorf("%d:%s: script was %s; want %s", i, tt.loc, sc, tt.scr)
+               }
+               if conf != tt.conf {
+                       t.Errorf("%d:%s: confidence was %d; want %d", i, tt.loc, conf, tt.conf)
+               }
+       }
+}
+
+func TestParseScript(t *testing.T) {
+       tests := []struct {
+               in  string
+               out string
+               ok  bool
+       }{
+               {"Latn", "Latn", true},
+               {"zzzz", "Zzzz", true},
+               {"zyyy", "Zyyy", true},
+               {"Latm", "Zzzz", false},
+               {"Zzz", "Zzzz", false},
+               {"", "Zzzz", false},
+               {"Zzzxx", "Zzzz", false},
+       }
+       for i, tt := range tests {
+               x, err := ParseScript(tt.in)
+               if x.String() != tt.out || err == nil != tt.ok {
+                       t.Errorf("%d:%s: was %s, %v; want %s, %v", i, tt.in, x, err == nil, tt.out, tt.ok)
+               }
+               if err == nil {
+                       if _, y, _ := Raw.Make("und-" + tt.out).Raw(); x != y {
+                               t.Errorf("%d:%s: tag was %s; want %s", i, tt.in, x, y)
+                       }
+               }
+       }
+}
+
+func TestRegion(t *testing.T) {
+       tests := []struct {
+               loc, reg string
+               conf     Confidence
+       }{
+               {"und", "US", Low},
+               {"en", "US", Low},
+               {"zh-Hant", "TW", Low},
+               {"en-US", "US", Exact},
+               {"cmn", "CN", Low},
+               {"ru", "RU", Low},
+               {"yue", "HK", Low},
+               {"x-abc", "ZZ", Low},
+       }
+       for i, tt := range tests {
+               loc, _ := Raw.Parse(tt.loc)
+               reg, conf := loc.Region()
+               if reg.String() != tt.reg {
+                       t.Errorf("%d:%s: region was %s; want %s", i, tt.loc, reg, tt.reg)
+               }
+               if conf != tt.conf {
+                       t.Errorf("%d:%s: confidence was %d; want %d", i, tt.loc, conf, tt.conf)
+               }
+       }
+}
+
+func TestEncodeM49(t *testing.T) {
+       tests := []struct {
+               m49  int
+               code string
+               ok   bool
+       }{
+               {1, "001", true},
+               {840, "US", true},
+               {899, "ZZ", false},
+       }
+       for i, tt := range tests {
+               if r, err := EncodeM49(tt.m49); r.String() != tt.code || err == nil != tt.ok {
+                       t.Errorf("%d:%d: was %s, %v; want %s, %v", i, tt.m49, r, err == nil, tt.code, tt.ok)
+               }
+       }
+       for i := 1; i <= 1000; i++ {
+               if r, err := EncodeM49(i); err == nil && r.M49() == 0 {
+                       t.Errorf("%d has no error, but maps to undefined region", i)
+               }
+       }
+}
+
+func TestParseRegion(t *testing.T) {
+       tests := []struct {
+               in  string
+               out string
+               ok  bool
+       }{
+               {"001", "001", true},
+               {"840", "US", true},
+               {"899", "ZZ", false},
+               {"USA", "US", true},
+               {"US", "US", true},
+               {"BC", "ZZ", false},
+               {"C", "ZZ", false},
+               {"CCCC", "ZZ", false},
+               {"01", "ZZ", false},
+       }
+       for i, tt := range tests {
+               r, err := ParseRegion(tt.in)
+               if r.String() != tt.out || err == nil != tt.ok {
+                       t.Errorf("%d:%s: was %s, %v; want %s, %v", i, tt.in, r, err == nil, tt.out, tt.ok)
+               }
+               if err == nil {
+                       if _, _, y := Raw.Make("und-" + tt.out).Raw(); r != y {
+                               t.Errorf("%d:%s: tag was %s; want %s", i, tt.in, r, y)
+                       }
+               }
+       }
+}
+
+func TestIsCountry(t *testing.T) {
+       tests := []struct {
+               reg     string
+               country bool
+       }{
+               {"US", true},
+               {"001", false},
+               {"958", false},
+               {"419", false},
+               {"203", true},
+               {"020", true},
+               {"900", false},
+               {"999", false},
+               {"QO", false},
+               {"EU", false},
+               {"AA", false},
+               {"XK", true},
+       }
+       for i, tt := range tests {
+               reg, _ := getRegionID([]byte(tt.reg))
+               r := Region{reg}
+               if r.IsCountry() != tt.country {
+                       t.Errorf("%d: IsCountry(%s) was %v; want %v", i, tt.reg, r.IsCountry(), tt.country)
+               }
+       }
+}
+
+func TestIsGroup(t *testing.T) {
+       tests := []struct {
+               reg   string
+               group bool
+       }{
+               {"US", false},
+               {"001", true},
+               {"958", false},
+               {"419", true},
+               {"203", false},
+               {"020", false},
+               {"900", false},
+               {"999", false},
+               {"QO", true},
+               {"EU", true},
+               {"AA", false},
+               {"XK", false},
+       }
+       for i, tt := range tests {
+               reg, _ := getRegionID([]byte(tt.reg))
+               r := Region{reg}
+               if r.IsGroup() != tt.group {
+                       t.Errorf("%d: IsGroup(%s) was %v; want %v", i, tt.reg, r.IsGroup(), tt.group)
+               }
+       }
+}
+
+func TestContains(t *testing.T) {
+       tests := []struct {
+               enclosing, contained string
+               contains             bool
+       }{
+               // A region contains itself.
+               {"US", "US", true},
+               {"001", "001", true},
+
+               // Direct containment.
+               {"001", "002", true},
+               {"039", "XK", true},
+               {"150", "XK", true},
+               {"EU", "AT", true},
+               {"QO", "AQ", true},
+
+               // Indirect containemnt.
+               {"001", "US", true},
+               {"001", "419", true},
+               {"001", "013", true},
+
+               // No containment.
+               {"US", "001", false},
+               {"155", "EU", false},
+       }
+       for i, tt := range tests {
+               enc, _ := getRegionID([]byte(tt.enclosing))
+               con, _ := getRegionID([]byte(tt.contained))
+               r := Region{enc}
+               if got := r.Contains(Region{con}); got != tt.contains {
+                       t.Errorf("%d: %s.Contains(%s) was %v; want %v", i, tt.enclosing, tt.contained, got, tt.contains)
+               }
+       }
+}
+
+func TestRegionCanonicalize(t *testing.T) {
+       for i, tt := range []struct{ in, out string }{
+               {"UK", "GB"},
+               {"TP", "TL"},
+               {"QU", "EU"},
+               {"SU", "SU"},
+               {"VD", "VN"},
+               {"DD", "DE"},
+       } {
+               r := MustParseRegion(tt.in)
+               want := MustParseRegion(tt.out)
+               if got := r.Canonicalize(); got != want {
+                       t.Errorf("%d: got %v; want %v", i, got, want)
+               }
+       }
+}
+
+func TestRegionTLD(t *testing.T) {
+       for _, tt := range []struct {
+               in, out string
+               ok      bool
+       }{
+               {"EH", "EH", true},
+               {"FR", "FR", true},
+               {"TL", "TL", true},
+
+               // In ccTLD before in ISO.
+               {"GG", "GG", true},
+
+               // Non-standard assignment of ccTLD to ISO code.
+               {"GB", "UK", true},
+
+               // Exceptionally reserved in ISO and valid ccTLD.
+               {"UK", "UK", true},
+               {"AC", "AC", true},
+               {"EU", "EU", true},
+               {"SU", "SU", true},
+
+               // Exceptionally reserved in ISO and invalid ccTLD.
+               {"CP", "ZZ", false},
+               {"DG", "ZZ", false},
+               {"EA", "ZZ", false},
+               {"FX", "ZZ", false},
+               {"IC", "ZZ", false},
+               {"TA", "ZZ", false},
+
+               // Transitionally reserved in ISO (e.g. deprecated) but valid ccTLD as
+               // it is still being phased out.
+               {"AN", "AN", true},
+               {"TP", "TP", true},
+
+               // Transitionally reserved in ISO (e.g. deprecated) and invalid ccTLD.
+               // Defined in package language as it has a mapping in CLDR.
+               {"BU", "ZZ", false},
+               {"CS", "ZZ", false},
+               {"NT", "ZZ", false},
+               {"YU", "ZZ", false},
+               {"ZR", "ZZ", false},
+               // Not defined in package: SF.
+
+               // Indeterminately reserved in ISO.
+               // Defined in package language as it has a legacy mapping in CLDR.
+               {"DY", "ZZ", false},
+               {"RH", "ZZ", false},
+               {"VD", "ZZ", false},
+               // Not defined in package: EW, FL, JA, LF, PI, RA, RB, RC, RI, RL, RM,
+               // RN, RP, WG, WL, WV, and YV.
+
+               // Not assigned in ISO, but legacy definitions in CLDR.
+               {"DD", "ZZ", false},
+               {"YD", "ZZ", false},
+
+               // Normal mappings but somewhat special status in ccTLD.
+               {"BL", "BL", true},
+               {"MF", "MF", true},
+               {"BV", "BV", true},
+               {"SJ", "SJ", true},
+
+               // Have values when normalized, but not as is.
+               {"QU", "ZZ", false},
+
+               // ISO Private Use.
+               {"AA", "ZZ", false},
+               {"QM", "ZZ", false},
+               {"QO", "ZZ", false},
+               {"XA", "ZZ", false},
+               {"XK", "ZZ", false}, // Sometimes used for Kosovo, but invalid ccTLD.
+       } {
+               if tt.in == "" {
+                       continue
+               }
+
+               r := MustParseRegion(tt.in)
+               var want Region
+               if tt.out != "ZZ" {
+                       want = MustParseRegion(tt.out)
+               }
+               tld, err := r.TLD()
+               if got := err == nil; got != tt.ok {
+                       t.Errorf("error(%v): got %v; want %v", r, got, tt.ok)
+               }
+               if tld != want {
+                       t.Errorf("TLD(%v): got %v; want %v", r, tld, want)
+               }
+       }
+}
+
+func TestCanonicalize(t *testing.T) {
+       // TODO: do a full test using CLDR data in a separate regression test.
+       tests := []struct {
+               in, out string
+               option  CanonType
+       }{
+               {"en-Latn", "en", SuppressScript},
+               {"sr-Cyrl", "sr-Cyrl", SuppressScript},
+               {"sh", "sr-Latn", Legacy},
+               {"sh-HR", "sr-Latn-HR", Legacy},
+               {"sh-Cyrl-HR", "sr-Cyrl-HR", Legacy},
+               {"tl", "fil", Legacy},
+               {"no", "no", Legacy},
+               {"no", "nb", Legacy | CLDR},
+               {"cmn", "cmn", Legacy},
+               {"cmn", "zh", Macro},
+               {"cmn-u-co-stroke", "zh-u-co-stroke", Macro},
+               {"yue", "yue", Macro},
+               {"nb", "no", Macro},
+               {"nb", "nb", Macro | CLDR},
+               {"no", "no", Macro},
+               {"no", "no", Macro | CLDR},
+               {"iw", "he", DeprecatedBase},
+               {"iw", "he", Deprecated | CLDR},
+               {"mo", "ro-MD", Deprecated}, // Adopted by CLDR as of version 25.
+               {"alb", "sq", Legacy},       // bibliographic
+               {"dut", "nl", Legacy},       // bibliographic
+               // As of CLDR 25, mo is no longer considered a legacy mapping.
+               {"mo", "mo", Legacy | CLDR},
+               {"und-AN", "und-AN", Deprecated},
+               {"und-YD", "und-YE", DeprecatedRegion},
+               {"und-YD", "und-YD", DeprecatedBase},
+               {"und-Qaai", "und-Zinh", DeprecatedScript},
+               {"und-Qaai", "und-Qaai", DeprecatedBase},
+               {"drh", "mn", All}, // drh -> khk -> mn
+       }
+       for i, tt := range tests {
+               in, _ := Raw.Parse(tt.in)
+               in, _ = tt.option.Canonicalize(in)
+               if in.String() != tt.out {
+                       t.Errorf("%d:%s: was %s; want %s", i, tt.in, in.String(), tt.out)
+               }
+               if int(in.pVariant) > int(in.pExt) || int(in.pExt) > len(in.str) {
+                       t.Errorf("%d:%s:offsets %d <= %d <= %d must be true", i, tt.in, in.pVariant, in.pExt, len(in.str))
+               }
+       }
+       // Test idempotence.
+       for _, base := range Supported.BaseLanguages() {
+               tag, _ := Raw.Compose(base)
+               got, _ := All.Canonicalize(tag)
+               want, _ := All.Canonicalize(got)
+               if got != want {
+                       t.Errorf("idem(%s): got %s; want %s", tag, got, want)
+               }
+       }
+}
+
+func TestTypeForKey(t *testing.T) {
+       tests := []struct{ key, in, out string }{
+               {"co", "en", ""},
+               {"co", "en-u-abc", ""},
+               {"co", "en-u-co-phonebk", "phonebk"},
+               {"co", "en-u-co-phonebk-cu-aud", "phonebk"},
+               {"co", "x-foo-u-co-phonebk", ""},
+               {"nu", "en-u-co-phonebk-nu-arabic", "arabic"},
+               {"kc", "cmn-u-co-stroke", ""},
+       }
+       for _, tt := range tests {
+               if v := Make(tt.in).TypeForKey(tt.key); v != tt.out {
+                       t.Errorf("%q[%q]: was %q; want %q", tt.in, tt.key, v, tt.out)
+               }
+       }
+}
+
+func TestSetTypeForKey(t *testing.T) {
+       tests := []struct {
+               key, value, in, out string
+               err                 bool
+       }{
+               // replace existing value
+               {"co", "pinyin", "en-u-co-phonebk", "en-u-co-pinyin", false},
+               {"co", "pinyin", "en-u-co-phonebk-cu-xau", "en-u-co-pinyin-cu-xau", false},
+               {"co", "pinyin", "en-u-co-phonebk-v-xx", "en-u-co-pinyin-v-xx", false},
+               {"co", "pinyin", "en-u-co-phonebk-x-x", "en-u-co-pinyin-x-x", false},
+               {"nu", "arabic", "en-u-co-phonebk-nu-vaai", "en-u-co-phonebk-nu-arabic", false},
+               // add to existing -u extension
+               {"co", "pinyin", "en-u-ca-gregory", "en-u-ca-gregory-co-pinyin", false},
+               {"co", "pinyin", "en-u-ca-gregory-nu-vaai", "en-u-ca-gregory-co-pinyin-nu-vaai", false},
+               {"co", "pinyin", "en-u-ca-gregory-v-va", "en-u-ca-gregory-co-pinyin-v-va", false},
+               {"co", "pinyin", "en-u-ca-gregory-x-a", "en-u-ca-gregory-co-pinyin-x-a", false},
+               {"ca", "gregory", "en-u-co-pinyin", "en-u-ca-gregory-co-pinyin", false},
+               // remove pair
+               {"co", "", "en-u-co-phonebk", "en", false},
+               {"co", "", "en-u-ca-gregory-co-phonebk", "en-u-ca-gregory", false},
+               {"co", "", "en-u-co-phonebk-nu-arabic", "en-u-nu-arabic", false},
+               {"co", "", "en", "en", false},
+               // add -u extension
+               {"co", "pinyin", "en", "en-u-co-pinyin", false},
+               {"co", "pinyin", "und", "und-u-co-pinyin", false},
+               {"co", "pinyin", "en-a-aaa", "en-a-aaa-u-co-pinyin", false},
+               {"co", "pinyin", "en-x-aaa", "en-u-co-pinyin-x-aaa", false},
+               {"co", "pinyin", "en-v-aa", "en-u-co-pinyin-v-aa", false},
+               {"co", "pinyin", "en-a-aaa-x-x", "en-a-aaa-u-co-pinyin-x-x", false},
+               {"co", "pinyin", "en-a-aaa-v-va", "en-a-aaa-u-co-pinyin-v-va", false},
+               // error on invalid values
+               {"co", "pinyinxxx", "en", "en", true},
+               {"co", "piny.n", "en", "en", true},
+               {"co", "pinyinxxx", "en-a-aaa", "en-a-aaa", true},
+               {"co", "pinyinxxx", "en-u-aaa", "en-u-aaa", true},
+               {"co", "pinyinxxx", "en-u-aaa-co-pinyin", "en-u-aaa-co-pinyin", true},
+               {"co", "pinyi.", "en-u-aaa-co-pinyin", "en-u-aaa-co-pinyin", true},
+               {"col", "pinyin", "en", "en", true},
+               {"co", "cu", "en", "en", true},
+               // error when setting on a private use tag
+               {"co", "phonebook", "x-foo", "x-foo", true},
+       }
+       for i, tt := range tests {
+               tag := Make(tt.in)
+               if v, err := tag.SetTypeForKey(tt.key, tt.value); v.String() != tt.out {
+                       t.Errorf("%d:%q[%q]=%q: was %q; want %q", i, tt.in, tt.key, tt.value, v, tt.out)
+               } else if (err != nil) != tt.err {
+                       t.Errorf("%d:%q[%q]=%q: error was %v; want %v", i, tt.in, tt.key, tt.value, err != nil, tt.err)
+               } else if val := v.TypeForKey(tt.key); err == nil && val != tt.value {
+                       t.Errorf("%d:%q[%q]==%q: was %v; want %v", i, tt.out, tt.key, tt.value, val, tt.value)
+               }
+               if len(tag.String()) <= 3 {
+                       // Simulate a tag for which the string has not been set.
+                       tag.str, tag.pExt, tag.pVariant = "", 0, 0
+                       if tag, err := tag.SetTypeForKey(tt.key, tt.value); err == nil {
+                               if val := tag.TypeForKey(tt.key); err == nil && val != tt.value {
+                                       t.Errorf("%d:%q[%q]==%q: was %v; want %v", i, tt.out, tt.key, tt.value, val, tt.value)
+                               }
+                       }
+               }
+       }
+}
+
+func TestFindKeyAndType(t *testing.T) {
+       // out is either the matched type in case of a match or the original
+       // string up till the insertion point.
+       tests := []struct {
+               key     string
+               hasExt  bool
+               in, out string
+       }{
+               // Don't search past a private use extension.
+               {"co", false, "en-x-foo-u-co-pinyin", "en"},
+               {"co", false, "x-foo-u-co-pinyin", ""},
+               {"co", false, "en-s-fff-x-foo", "en-s-fff"},
+               // Insertion points in absence of -u extension.
+               {"cu", false, "en", ""}, // t.str is ""
+               {"cu", false, "en-v-va", "en"},
+               {"cu", false, "en-a-va", "en-a-va"},
+               {"cu", false, "en-a-va-v-va", "en-a-va"},
+               {"cu", false, "en-x-a", "en"},
+               // Tags with the -u extension.
+               {"co", true, "en-u-co-standard", "standard"},
+               {"co", true, "yue-u-co-pinyin", "pinyin"},
+               {"co", true, "en-u-co-abc", "abc"},
+               {"co", true, "en-u-co-abc-def", "abc-def"},
+               {"co", true, "en-u-co-abc-def-x-foo", "abc-def"},
+               {"co", true, "en-u-co-standard-nu-arab", "standard"},
+               {"co", true, "yue-u-co-pinyin-nu-arab", "pinyin"},
+               // Insertion points.
+               {"cu", true, "en-u-co-standard", "en-u-co-standard"},
+               {"cu", true, "yue-u-co-pinyin-x-foo", "yue-u-co-pinyin"},
+               {"cu", true, "en-u-co-abc", "en-u-co-abc"},
+               {"cu", true, "en-u-nu-arabic", "en-u"},
+               {"cu", true, "en-u-co-abc-def-nu-arabic", "en-u-co-abc-def"},
+       }
+       for i, tt := range tests {
+               start, end, hasExt := Make(tt.in).findTypeForKey(tt.key)
+               if start != end {
+                       res := tt.in[start:end]
+                       if res != tt.out {
+                               t.Errorf("%d:%s: was %q; want %q", i, tt.in, res, tt.out)
+                       }
+               } else {
+                       if hasExt != tt.hasExt {
+                               t.Errorf("%d:%s: hasExt was %v; want %v", i, tt.in, hasExt, tt.hasExt)
+                               continue
+                       }
+                       if tt.in[:start] != tt.out {
+                               t.Errorf("%d:%s: insertion point was %q; want %q", i, tt.in, tt.in[:start], tt.out)
+                       }
+               }
+       }
+}
+
+func TestParent(t *testing.T) {
+       tests := []struct{ in, out string }{
+               // Strip variants and extensions first
+               {"de-u-co-phonebk", "de"},
+               {"de-1994", "de"},
+               {"de-Latn-1994", "de"}, // remove superfluous script.
+
+               // Ensure the canonical Tag for an entry is in the chain for base-script
+               // pairs.
+               {"zh-Hans", "zh"},
+
+               // Skip the script if it is the maximized version. CLDR files for the
+               // skipped tag are always empty.
+               {"zh-Hans-TW", "zh"},
+               {"zh-Hans-CN", "zh"},
+
+               // Insert the script if the maximized script is not the same as the
+               // maximized script of the base language.
+               {"zh-TW", "zh-Hant"},
+               {"zh-HK", "zh-Hant"},
+               {"zh-Hant-TW", "zh-Hant"},
+               {"zh-Hant-HK", "zh-Hant"},
+
+               // Non-default script skips to und.
+               // CLDR
+               {"az-Cyrl", "und"},
+               {"bs-Cyrl", "und"},
+               {"en-Dsrt", "und"},
+               {"ha-Arab", "und"},
+               {"mn-Mong", "und"},
+               {"pa-Arab", "und"},
+               {"shi-Latn", "und"},
+               {"sr-Latn", "und"},
+               {"uz-Arab", "und"},
+               {"uz-Cyrl", "und"},
+               {"vai-Latn", "und"},
+               {"zh-Hant", "und"},
+               // extra
+               {"nl-Cyrl", "und"},
+
+               // World english inherits from en-001.
+               {"en-150", "en-001"},
+               {"en-AU", "en-001"},
+               {"en-BE", "en-001"},
+               {"en-GG", "en-001"},
+               {"en-GI", "en-001"},
+               {"en-HK", "en-001"},
+               {"en-IE", "en-001"},
+               {"en-IM", "en-001"},
+               {"en-IN", "en-001"},
+               {"en-JE", "en-001"},
+               {"en-MT", "en-001"},
+               {"en-NZ", "en-001"},
+               {"en-PK", "en-001"},
+               {"en-SG", "en-001"},
+
+               // Spanish in Latin-American countries have es-419 as parent.
+               {"es-AR", "es-419"},
+               {"es-BO", "es-419"},
+               {"es-CL", "es-419"},
+               {"es-CO", "es-419"},
+               {"es-CR", "es-419"},
+               {"es-CU", "es-419"},
+               {"es-DO", "es-419"},
+               {"es-EC", "es-419"},
+               {"es-GT", "es-419"},
+               {"es-HN", "es-419"},
+               {"es-MX", "es-419"},
+               {"es-NI", "es-419"},
+               {"es-PA", "es-419"},
+               {"es-PE", "es-419"},
+               {"es-PR", "es-419"},
+               {"es-PY", "es-419"},
+               {"es-SV", "es-419"},
+               {"es-US", "es-419"},
+               {"es-UY", "es-419"},
+               {"es-VE", "es-419"},
+               // exceptions (according to CLDR)
+               {"es-CW", "es"},
+
+               // Inherit from pt-PT, instead of pt for these countries.
+               {"pt-AO", "pt-PT"},
+               {"pt-CV", "pt-PT"},
+               {"pt-GW", "pt-PT"},
+               {"pt-MO", "pt-PT"},
+               {"pt-MZ", "pt-PT"},
+               {"pt-ST", "pt-PT"},
+               {"pt-TL", "pt-PT"},
+       }
+       for _, tt := range tests {
+               tag := Raw.MustParse(tt.in)
+               if p := Raw.MustParse(tt.out); p != tag.Parent() {
+                       t.Errorf("%s: was %v; want %v", tt.in, tag.Parent(), p)
+               }
+       }
+}
+
+var (
+       // Tags without error that don't need to be changed.
+       benchBasic = []string{
+               "en",
+               "en-Latn",
+               "en-GB",
+               "za",
+               "zh-Hant",
+               "zh",
+               "zh-HK",
+               "ar-MK",
+               "en-CA",
+               "fr-CA",
+               "fr-CH",
+               "fr",
+               "lv",
+               "he-IT",
+               "tlh",
+               "ja",
+               "ja-Jpan",
+               "ja-Jpan-JP",
+               "de-1996",
+               "de-CH",
+               "sr",
+               "sr-Latn",
+       }
+       // Tags with extensions, not changes required.
+       benchExt = []string{
+               "x-a-b-c-d",
+               "x-aa-bbbb-cccccccc-d",
+               "en-x_cc-b-bbb-a-aaa",
+               "en-c_cc-b-bbb-a-aaa-x-x",
+               "en-u-co-phonebk",
+               "en-Cyrl-u-co-phonebk",
+               "en-US-u-co-phonebk-cu-xau",
+               "en-nedix-u-co-phonebk",
+               "en-t-t0-abcd",
+               "en-t-nl-latn",
+               "en-t-t0-abcd-x-a",
+       }
+       // Change, but not memory allocation required.
+       benchSimpleChange = []string{
+               "EN",
+               "i-klingon",
+               "en-latn",
+               "zh-cmn-Hans-CN",
+               "iw-NL",
+       }
+       // Change and memory allocation required.
+       benchChangeAlloc = []string{
+               "en-c_cc-b-bbb-a-aaa",
+               "en-u-cu-xua-co-phonebk",
+               "en-u-cu-xua-co-phonebk-a-cd",
+               "en-u-def-abc-cu-xua-co-phonebk",
+               "en-t-en-Cyrl-NL-1994",
+               "en-t-en-Cyrl-NL-1994-t0-abc-def",
+       }
+       // Tags that result in errors.
+       benchErr = []string{
+               // IllFormed
+               "x_A.-B-C_D",
+               "en-u-cu-co-phonebk",
+               "en-u-cu-xau-co",
+               "en-t-nl-abcd",
+               // Invalid
+               "xx",
+               "nl-Uuuu",
+               "nl-QB",
+       }
+       benchChange = append(benchSimpleChange, benchChangeAlloc...)
+       benchAll    = append(append(append(benchBasic, benchExt...), benchChange...), benchErr...)
+)
+
+func doParse(b *testing.B, tag []string) {
+       for i := 0; i < b.N; i++ {
+               // Use the modulo instead of looping over all tags so that we get a somewhat
+               // meaningful ns/op.
+               Parse(tag[i%len(tag)])
+       }
+}
+
+func BenchmarkParse(b *testing.B) {
+       doParse(b, benchAll)
+}
+
+func BenchmarkParseBasic(b *testing.B) {
+       doParse(b, benchBasic)
+}
+
+func BenchmarkParseError(b *testing.B) {
+       doParse(b, benchErr)
+}
+
+func BenchmarkParseSimpleChange(b *testing.B) {
+       doParse(b, benchSimpleChange)
+}
+
+func BenchmarkParseChangeAlloc(b *testing.B) {
+       doParse(b, benchChangeAlloc)
+}