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