github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/text/language/display/display_test.go (about) 1 // Copyright 2014 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 display 6 7 import ( 8 "fmt" 9 "reflect" 10 "testing" 11 "unicode" 12 13 "golang.org/x/text/language" 14 ) 15 16 // TODO: test that tables are properly dropped by the linker for various use 17 // cases. 18 19 var ( 20 firstLang2aa = language.MustParseBase("aa") 21 lastLang2zu = language.MustParseBase("zu") 22 firstLang3ace = language.MustParseBase("ace") 23 lastLang3zza = language.MustParseBase("zza") 24 firstTagAr001 = language.MustParse("ar-001") 25 lastTagZhHant = language.MustParse("zh-Hant") 26 ) 27 28 // TestValues tests that for all languages, regions, and scripts in Values, at 29 // least one language has a name defined for it by checking it exists in 30 // English, which is assumed to be the most comprehensive. It is also tested 31 // that a Namer returns "" for unsupported values. 32 func TestValues(t *testing.T) { 33 type testcase struct { 34 kind string 35 n Namer 36 } 37 // checkDefined checks that a value exists in a Namer. 38 checkDefined := func(x interface{}, namers []testcase) { 39 for _, n := range namers { 40 if n.n.Name(x) == "" { 41 // As of version 28 there is no data for az-Arab in English, 42 // although there is useful data in other languages. 43 if x.(fmt.Stringer).String() == "az-Arab" { 44 continue 45 } 46 t.Errorf("%s.Name(%s): supported but no result", n.kind, x) 47 } 48 } 49 } 50 // checkUnsupported checks that a value does not exist in a Namer. 51 checkUnsupported := func(x interface{}, namers []testcase) { 52 for _, n := range namers { 53 if got := n.n.Name(x); got != "" { 54 t.Fatalf("%s.Name(%s): unsupported tag gave non-empty result: %q", n.kind, x, got) 55 } 56 } 57 } 58 59 tags := map[language.Tag]bool{} 60 namers := []testcase{ 61 {"Languages(en)", Languages(language.English)}, 62 {"Tags(en)", Tags(language.English)}, 63 {"English.Languages()", English.Languages()}, 64 {"English.Tags()", English.Tags()}, 65 } 66 for _, tag := range Values.Tags() { 67 checkDefined(tag, namers) 68 tags[tag] = true 69 } 70 for _, base := range language.Supported.BaseLanguages() { 71 tag, _ := language.All.Compose(base) 72 if !tags[tag] { 73 checkUnsupported(tag, namers) 74 } 75 } 76 77 regions := map[language.Region]bool{} 78 namers = []testcase{ 79 {"Regions(en)", Regions(language.English)}, 80 {"English.Regions()", English.Regions()}, 81 } 82 for _, r := range Values.Regions() { 83 checkDefined(r, namers) 84 regions[r] = true 85 } 86 for _, r := range language.Supported.Regions() { 87 if r = r.Canonicalize(); !regions[r] { 88 checkUnsupported(r, namers) 89 } 90 } 91 92 scripts := map[language.Script]bool{} 93 namers = []testcase{ 94 {"Scripts(en)", Scripts(language.English)}, 95 {"English.Scripts()", English.Scripts()}, 96 } 97 for _, s := range Values.Scripts() { 98 checkDefined(s, namers) 99 scripts[s] = true 100 } 101 for _, s := range language.Supported.Scripts() { 102 // Canonicalize the script. 103 tag, _ := language.DeprecatedScript.Compose(s) 104 if _, s, _ = tag.Raw(); !scripts[s] { 105 checkUnsupported(s, namers) 106 } 107 } 108 } 109 110 // TestSupported tests that we have at least some Namers for languages that we 111 // claim to support. To test the claims in the documentation, it also verifies 112 // that if a Namer is returned, it will have at least some data. 113 func TestSupported(t *testing.T) { 114 supportedTags := Supported.Tags() 115 if len(supportedTags) != numSupported { 116 t.Errorf("number of supported was %d; want %d", len(supportedTags), numSupported) 117 } 118 119 namerFuncs := []struct { 120 kind string 121 fn func(language.Tag) Namer 122 }{ 123 {"Tags", Tags}, 124 {"Languages", Languages}, 125 {"Regions", Regions}, 126 {"Scripts", Scripts}, 127 } 128 129 // Verify that we have at least one Namer for all tags we claim to support. 130 tags := make(map[language.Tag]bool) 131 for _, tag := range supportedTags { 132 // Test we have at least one Namer for this supported Tag. 133 found := false 134 for _, kind := range namerFuncs { 135 if defined(t, kind.kind, kind.fn(tag), tag) { 136 found = true 137 } 138 } 139 if !found { 140 t.Errorf("%s: supported, but no data available", tag) 141 } 142 if tags[tag] { 143 t.Errorf("%s: included in Supported.Tags more than once", tag) 144 } 145 tags[tag] = true 146 } 147 148 // Verify that we have no Namers for tags we don't claim to support. 149 for _, base := range language.Supported.BaseLanguages() { 150 tag, _ := language.All.Compose(base) 151 // Skip tags that are supported after matching. 152 if _, _, conf := matcher.Match(tag); conf != language.No { 153 continue 154 } 155 // Test there are no Namers for this tag. 156 for _, kind := range namerFuncs { 157 if defined(t, kind.kind, kind.fn(tag), tag) { 158 t.Errorf("%[1]s(%[2]s) returns a Namer, but %[2]s is not in the set of supported Tags.", kind.kind, tag) 159 } 160 } 161 } 162 } 163 164 // defined reports whether n is a proper Namer, which means it is non-nil and 165 // must have at least one non-empty value. 166 func defined(t *testing.T, kind string, n Namer, tag language.Tag) bool { 167 if n == nil { 168 return false 169 } 170 switch kind { 171 case "Tags": 172 for _, t := range Values.Tags() { 173 if n.Name(t) != "" { 174 return true 175 } 176 } 177 case "Languages": 178 for _, t := range Values.BaseLanguages() { 179 if n.Name(t) != "" { 180 return true 181 } 182 } 183 case "Regions": 184 for _, t := range Values.Regions() { 185 if n.Name(t) != "" { 186 return true 187 } 188 } 189 case "Scripts": 190 for _, t := range Values.Scripts() { 191 if n.Name(t) != "" { 192 return true 193 } 194 } 195 } 196 t.Errorf("%s(%s) returns non-nil Namer without content", kind, tag) 197 return false 198 } 199 200 func TestCoverage(t *testing.T) { 201 en := language.English 202 tests := []struct { 203 n Namer 204 x interface{} 205 }{ 206 {Languages(en), Values.Tags()}, 207 {Scripts(en), Values.Scripts()}, 208 {Regions(en), Values.Regions()}, 209 } 210 for i, tt := range tests { 211 uniq := make(map[string]interface{}) 212 213 v := reflect.ValueOf(tt.x) 214 for j := 0; j < v.Len(); j++ { 215 x := v.Index(j).Interface() 216 // As of version 28 there is no data for az-Arab in English, 217 // although there is useful data in other languages. 218 if x.(fmt.Stringer).String() == "az-Arab" { 219 continue 220 } 221 s := tt.n.Name(x) 222 if s == "" { 223 t.Errorf("%d:%d:%s: missing content", i, j, x) 224 } else if uniq[s] != nil { 225 t.Errorf("%d:%d:%s: identical return value %q for %v and %v", i, j, x, s, x, uniq[s]) 226 } 227 uniq[s] = x 228 } 229 } 230 } 231 232 // TestUpdate tests whether dictionary entries for certain languages need to be 233 // updated. For some languages, some of the headers may be empty or they may be 234 // identical to the parent. This code detects if such entries need to be updated 235 // after a table update. 236 func TestUpdate(t *testing.T) { 237 tests := []struct { 238 d *Dictionary 239 tag string 240 }{ 241 {ModernStandardArabic, "ar-001"}, 242 {AmericanEnglish, "en-US"}, 243 {EuropeanSpanish, "es-ES"}, 244 {BrazilianPortuguese, "pt-BR"}, 245 {SimplifiedChinese, "zh-Hans"}, 246 } 247 248 for _, tt := range tests { 249 _, i, _ := matcher.Match(language.MustParse(tt.tag)) 250 if !reflect.DeepEqual(tt.d.lang, langHeaders[i]) { 251 t.Errorf("%s: lang table update needed", tt.tag) 252 } 253 if !reflect.DeepEqual(tt.d.script, scriptHeaders[i]) { 254 t.Errorf("%s: script table update needed", tt.tag) 255 } 256 if !reflect.DeepEqual(tt.d.region, regionHeaders[i]) { 257 t.Errorf("%s: region table update needed", tt.tag) 258 } 259 } 260 } 261 262 func TestIndex(t *testing.T) { 263 notIn := []string{"aa", "xx", "zz", "aaa", "xxx", "zzz", "Aaaa", "Xxxx", "Zzzz"} 264 tests := []tagIndex{ 265 { 266 "", 267 "", 268 "", 269 }, 270 { 271 "bb", 272 "", 273 "", 274 }, 275 { 276 "", 277 "bbb", 278 "", 279 }, 280 { 281 "", 282 "", 283 "Bbbb", 284 }, 285 { 286 "bb", 287 "bbb", 288 "Bbbb", 289 }, 290 { 291 "bbccddyy", 292 "bbbcccdddyyy", 293 "BbbbCcccDdddYyyy", 294 }, 295 } 296 for i, tt := range tests { 297 // Create the test set from the tagIndex. 298 cnt := 0 299 for sz := 2; sz <= 4; sz++ { 300 a := tt[sz-2] 301 for j := 0; j < len(a); j += sz { 302 s := a[j : j+sz] 303 if idx := tt.index(s); idx != cnt { 304 t.Errorf("%d:%s: index was %d; want %d", i, s, idx, cnt) 305 } 306 cnt++ 307 } 308 } 309 if n := tt.len(); n != cnt { 310 t.Errorf("%d: len was %d; want %d", i, n, cnt) 311 } 312 for _, x := range notIn { 313 if idx := tt.index(x); idx != -1 { 314 t.Errorf("%d:%s: index was %d; want -1", i, x, idx) 315 } 316 } 317 } 318 } 319 320 func TestTag(t *testing.T) { 321 tests := []struct { 322 dict string 323 tag string 324 name string 325 }{ 326 {"agq", "sr", ""}, // sr is in Value.Languages(), but is not supported by agq. 327 {"nl", "nl", "Nederlands"}, 328 {"nl", "nl-BE", "Vlaams"}, 329 {"en", "en", "English"}, 330 {"en", "en-GB", "British English"}, 331 {"en", "en-US", "American English"}, // American English in CLDR 24+ 332 {"ru", "ru", "русский"}, 333 {"ru", "ru-RU", "русский (Россия)"}, 334 {"ru", "ru-Cyrl", "русский (кириллица)"}, 335 {"en", lastLang2zu.String(), "Zulu"}, 336 {"en", firstLang2aa.String(), "Afar"}, 337 {"en", lastLang3zza.String(), "Zaza"}, 338 {"en", firstLang3ace.String(), "Achinese"}, 339 {"en", firstTagAr001.String(), "Modern Standard Arabic"}, 340 {"en", lastTagZhHant.String(), "Traditional Chinese"}, 341 {"en", "aaa", ""}, 342 {"en", "zzj", ""}, 343 // If full tag doesn't match, try without script or region. 344 {"en", "aa-Hans", "Afar (Simplified Han)"}, 345 {"en", "af-Arab", "Afrikaans (Arabic)"}, 346 {"en", "zu-Cyrl", "Zulu (Cyrillic)"}, 347 {"en", "aa-GB", "Afar (United Kingdom)"}, 348 {"en", "af-NA", "Afrikaans (Namibia)"}, 349 {"en", "zu-BR", "Zulu (Brazil)"}, 350 // Correct inheritance and language selection. 351 {"zh", "zh-TW", "中文 (台湾)"}, 352 {"zh", "zh-Hant-TW", "繁体中文 (台湾)"}, 353 {"zh-Hant", "zh-TW", "中文 (台灣)"}, 354 {"zh-Hant", "zh-Hant-TW", "繁體中文 (台灣)"}, 355 // Some rather arbitrary interpretations for Serbian. This is arguably 356 // correct and consistent with the way zh-[Hant-]TW is handled. It will 357 // also give results more in line with the expectations if users 358 // explicitly use "sh". 359 {"sr-Latn", "sr-ME", "srpski (Crna Gora)"}, 360 {"sr-Latn", "sr-Latn-ME", "Srpskohrvatski (Crna Gora)"}, 361 // Double script and region 362 {"nl", "en-Cyrl-BE", "Engels (Cyrillisch, België)"}, 363 // Canonical equivalents. 364 {"ro", "ro-MD", "moldovenească"}, 365 {"ro", "mo", "moldovenească"}, 366 } 367 for i, tt := range tests { 368 d := Tags(language.MustParse(tt.dict)) 369 if n := d.Name(language.Raw.MustParse(tt.tag)); n != tt.name { 370 // There are inconsistencies w.r.t. capitalization in the tests 371 // due to CLDR's update procedure which treats modern and other 372 // languages differently. 373 // See http://unicode.org/cldr/trac/ticket/8051. 374 // TODO: use language capitalization to sanitize the strings. 375 t.Errorf("%d:%s:%s: was %q; want %q", i, tt.dict, tt.tag, n, tt.name) 376 } 377 } 378 } 379 380 func TestLanguage(t *testing.T) { 381 tests := []struct { 382 dict string 383 tag string 384 name string 385 }{ 386 {"agq", "sr", ""}, // sr is in Value.Languages(), but is not supported by agq. 387 {"nl", "nl", "Nederlands"}, 388 {"nl", "nl-BE", "Vlaams"}, 389 {"en", "pt", "Portuguese"}, 390 {"en", "pt-PT", "European Portuguese"}, 391 {"en", "pt-BR", "Brazilian Portuguese"}, 392 {"en", "en", "English"}, 393 {"en", "en-GB", "British English"}, 394 {"en", "en-US", "American English"}, // American English in CLDR 24+ 395 {"en", lastLang2zu.String(), "Zulu"}, 396 {"en", firstLang2aa.String(), "Afar"}, 397 {"en", lastLang3zza.String(), "Zaza"}, 398 {"en", firstLang3ace.String(), "Achinese"}, 399 {"en", firstTagAr001.String(), "Modern Standard Arabic"}, 400 {"en", lastTagZhHant.String(), "Traditional Chinese"}, 401 {"en", "aaa", ""}, 402 {"en", "zzj", ""}, 403 // If full tag doesn't match, try without script or region. 404 {"en", "aa-Hans", "Afar"}, 405 {"en", "af-Arab", "Afrikaans"}, 406 {"en", "zu-Cyrl", "Zulu"}, 407 {"en", "aa-GB", "Afar"}, 408 {"en", "af-NA", "Afrikaans"}, 409 {"en", "zu-BR", "Zulu"}, 410 {"agq", "zh-Hant", ""}, 411 // Canonical equivalents. 412 {"ro", "ro-MD", "moldovenească"}, 413 {"ro", "mo", "moldovenească"}, 414 {"en", "sh", "Serbo-Croatian"}, 415 {"en", "sr-Latn", "Serbo-Croatian"}, 416 {"en", "sr", "Serbian"}, 417 {"en", "sr-ME", "Serbian"}, 418 {"en", "sr-Latn-ME", "Serbo-Croatian"}, // See comments in TestTag. 419 } 420 for i, tt := range tests { 421 d := Languages(language.Raw.MustParse(tt.dict)) 422 if n := d.Name(language.Raw.MustParse(tt.tag)); n != tt.name { 423 t.Errorf("%d:%s:%s: was %q; want %q", i, tt.dict, tt.tag, n, tt.name) 424 } 425 if len(tt.tag) <= 3 { 426 if n := d.Name(language.MustParseBase(tt.tag)); n != tt.name { 427 t.Errorf("%d:%s:base(%s): was %q; want %q", i, tt.dict, tt.tag, n, tt.name) 428 } 429 } 430 } 431 } 432 433 func TestScript(t *testing.T) { 434 tests := []struct { 435 dict string 436 scr string 437 name string 438 }{ 439 {"nl", "Arab", "Arabisch"}, 440 {"en", "Arab", "Arabic"}, 441 {"en", "Zzzz", "Unknown Script"}, 442 {"zh-Hant", "Hang", "韓文字"}, 443 {"zh-Hant-HK", "Hang", "韓文字母"}, 444 {"zh", "Arab", "阿拉伯文"}, 445 {"zh-Hans-HK", "Arab", "阿拉伯文"}, // same as zh 446 {"zh-Hant", "Arab", "阿拉伯文"}, 447 {"zh-Hant-HK", "Arab", "阿拉伯文"}, // same as zh 448 // Canonicalized form 449 {"en", "Qaai", "Inherited"}, // deprecated script, now is Zinh 450 {"en", "sh", "Unknown Script"}, // sh canonicalizes to sr-Latn 451 {"en", "en", "Unknown Script"}, 452 // Don't introduce scripts with canonicalization. 453 {"en", "sh", "Unknown Script"}, // sh canonicalizes to sr-Latn 454 } 455 for i, tt := range tests { 456 d := Scripts(language.MustParse(tt.dict)) 457 var x interface{} 458 if unicode.IsUpper(rune(tt.scr[0])) { 459 x = language.MustParseScript(tt.scr) 460 tag, _ := language.Raw.Compose(x) 461 if n := d.Name(tag); n != tt.name { 462 t.Errorf("%d:%s:%s: was %q; want %q", i, tt.dict, tt.scr, n, tt.name) 463 } 464 } else { 465 x = language.Raw.MustParse(tt.scr) 466 } 467 if n := d.Name(x); n != tt.name { 468 t.Errorf("%d:%s:%s: was %q; want %q", i, tt.dict, tt.scr, n, tt.name) 469 } 470 } 471 } 472 473 func TestRegion(t *testing.T) { 474 tests := []struct { 475 dict string 476 reg string 477 name string 478 }{ 479 {"nl", "NL", "Nederland"}, 480 {"en", "US", "United States"}, 481 {"en", "ZZ", "Unknown Region"}, 482 {"en", "UM", "U.S. Outlying Islands"}, 483 {"en-GB", "UM", "U.S. Outlying Islands"}, 484 {"en-GB", "NL", "Netherlands"}, 485 // Canonical equivalents 486 {"en", "UK", "United Kingdom"}, 487 // No region 488 {"en", "pt", "Unknown Region"}, 489 {"en", "und", "Unknown Region"}, 490 // Don't introduce regions with canonicalization. 491 {"en", "mo", "Unknown Region"}, 492 } 493 for i, tt := range tests { 494 d := Regions(language.MustParse(tt.dict)) 495 var x interface{} 496 if unicode.IsUpper(rune(tt.reg[0])) { 497 // Region 498 x = language.MustParseRegion(tt.reg) 499 tag, _ := language.Raw.Compose(x) 500 if n := d.Name(tag); n != tt.name { 501 t.Errorf("%d:%s:%s: was %q; want %q", i, tt.dict, tt.reg, n, tt.name) 502 } 503 } else { 504 // Tag 505 x = language.Raw.MustParse(tt.reg) 506 } 507 if n := d.Name(x); n != tt.name { 508 t.Errorf("%d:%s:%s: was %q; want %q", i, tt.dict, tt.reg, n, tt.name) 509 } 510 } 511 } 512 513 func TestSelf(t *testing.T) { 514 tests := []struct { 515 tag string 516 name string 517 }{ 518 {"nl", "Nederlands"}, 519 {"nl-BE", "Vlaams"}, 520 {"en-GB", "British English"}, 521 {lastLang2zu.String(), "isiZulu"}, 522 {firstLang2aa.String(), ""}, // not defined 523 {lastLang3zza.String(), ""}, // not defined 524 {firstLang3ace.String(), ""}, // not defined 525 {firstTagAr001.String(), "العربية الرسمية الحديثة"}, 526 {"ar", "العربية"}, 527 {lastTagZhHant.String(), "繁體中文"}, 528 {"aaa", ""}, 529 {"zzj", ""}, 530 // Drop entries that are not in the requested script, even if there is 531 // an entry for the language. 532 {"aa-Hans", ""}, 533 {"af-Arab", ""}, 534 {"zu-Cyrl", ""}, 535 // Append the country name in the language of the matching language. 536 {"af-NA", "Afrikaans"}, 537 {"zh", "中文"}, 538 // zh-TW should match zh-Hant instead of zh! 539 {"zh-TW", "繁體中文"}, 540 {"zh-Hant", "繁體中文"}, 541 {"zh-Hans", "简体中文"}, 542 {"zh-Hant-TW", "繁體中文"}, 543 {"zh-Hans-TW", "简体中文"}, 544 // Take the entry for sr which has the matching script. 545 // TODO: Capitalization changed as of CLDR 26, but change seems 546 // arbitrary. Revisit capitalization with revision 27. See 547 // http://unicode.org/cldr/trac/ticket/8051. 548 {"sr", "српски"}, 549 // TODO: sr-ME should show up as Serbian or Montenegrin, not Serbo- 550 // Croatian. This is an artifact of the current algorithm, which is the 551 // way it is to have the preferred behavior for other languages such as 552 // Chinese. We can hardwire this case in the table generator or package 553 // code, but we first check if CLDR can be updated. 554 // {"sr-ME", "Srpski"}, // Is Srpskohrvatski 555 {"sr-Latn-ME", "Srpskohrvatski"}, 556 {"sr-Cyrl-ME", "српски"}, 557 {"sr-NL", "српски"}, 558 // Canonical equivalents. 559 {"ro-MD", "moldovenească"}, 560 {"mo", "moldovenească"}, 561 // NOTE: kk is defined, but in Cyrillic script. For China, Arab is the 562 // dominant script. We do not have data for kk-Arab and we chose to not 563 // fall back in such cases. 564 {"kk-CN", ""}, 565 } 566 for i, tt := range tests { 567 d := Self 568 if n := d.Name(language.Raw.MustParse(tt.tag)); n != tt.name { 569 t.Errorf("%d:%s: was %q; want %q", i, tt.tag, n, tt.name) 570 } 571 } 572 } 573 574 func TestDictionaryLang(t *testing.T) { 575 tests := []struct { 576 d *Dictionary 577 tag string 578 name string 579 }{ 580 {English, "en", "English"}, 581 {Portuguese, "af", "africâner"}, 582 {EuropeanPortuguese, "af", "africânder"}, 583 {English, "nl-BE", "Flemish"}, 584 } 585 for i, test := range tests { 586 tag := language.MustParse(test.tag) 587 if got := test.d.Tags().Name(tag); got != test.name { 588 t.Errorf("%d:%v: got %s; want %s", i, tag, got, test.name) 589 } 590 if base, _ := language.Compose(tag.Base()); base == tag { 591 if got := test.d.Languages().Name(base); got != test.name { 592 t.Errorf("%d:%v: got %s; want %s", i, tag, got, test.name) 593 } 594 } 595 } 596 } 597 598 func TestDictionaryRegion(t *testing.T) { 599 tests := []struct { 600 d *Dictionary 601 region string 602 name string 603 }{ 604 {English, "FR", "France"}, 605 {Portuguese, "009", "Oceania"}, 606 {EuropeanPortuguese, "009", "Oceânia"}, 607 } 608 for i, test := range tests { 609 tag := language.MustParseRegion(test.region) 610 if got := test.d.Regions().Name(tag); got != test.name { 611 t.Errorf("%d:%v: got %s; want %s", i, tag, got, test.name) 612 } 613 } 614 } 615 616 func TestDictionaryScript(t *testing.T) { 617 tests := []struct { 618 d *Dictionary 619 script string 620 name string 621 }{ 622 {English, "Cyrl", "Cyrillic"}, 623 {Portuguese, "Gujr", "gujerati"}, 624 {EuropeanPortuguese, "Gujr", "guzerate"}, 625 } 626 for i, test := range tests { 627 tag := language.MustParseScript(test.script) 628 if got := test.d.Scripts().Name(tag); got != test.name { 629 t.Errorf("%d:%v: got %s; want %s", i, tag, got, test.name) 630 } 631 } 632 }