language_test.go 19 KB

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