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
5package compact
6
7import (
8	"reflect"
9	"testing"
10
11	"golang.org/x/text/internal/language"
12)
13
14func mustParse(s string) Tag {
15	t, err := language.Parse(s)
16	if err != nil {
17		panic(err)
18	}
19	return Make(t)
20}
21
22func TestTagSize(t *testing.T) {
23	id := Tag{}
24	typ := reflect.TypeOf(id)
25	if typ.Size() > 24 {
26		t.Errorf("size of Tag was %d; want 24", typ.Size())
27	}
28}
29
30func TestNoPublic(t *testing.T) {
31	noExportedField(t, reflect.TypeOf(Tag{}))
32}
33
34func noExportedField(t *testing.T, typ reflect.Type) {
35	for i := 0; i < typ.NumField(); i++ {
36		f := typ.Field(i)
37		if f.PkgPath == "" {
38			t.Errorf("Tag may not have exported fields, but has field %q", f.Name)
39		}
40		if f.Anonymous {
41			noExportedField(t, f.Type)
42		}
43	}
44}
45
46func TestEquality(t *testing.T) {
47	for i, tt := range parseTests() {
48		s := tt.in
49		tag := mk(s)
50		t1 := mustParse(tag.Tag().String())
51		if tag != t1 {
52			t.Errorf("%d:%s: equality test 1 failed\n got: %#v\nwant: %#v)", i, s, t1, tag)
53		}
54	}
55}
56
57type compactTest struct {
58	tag   string
59	index ID
60	ok    bool
61}
62
63var compactTests = []compactTest{
64	// TODO: these values will change with each CLDR update. This issue
65	// will be solved if we decide to fix the indexes.
66	{"und", undIndex, true},
67	{"ca-ES-valencia", caESvalenciaIndex, true},
68	{"ca-ES-valencia-u-va-posix", caESvalenciaIndex, false},
69	{"ca-ES-valencia-u-co-phonebk", caESvalenciaIndex, false},
70	{"ca-ES-valencia-u-co-phonebk-va-posix", caESvalenciaIndex, false},
71	{"x-klingon", 0, false},
72	{"en-US", enUSIndex, true},
73	{"en-US-u-va-posix", enUSuvaposixIndex, true},
74	{"en", enIndex, true},
75	{"en-u-co-phonebk", enIndex, false},
76	{"en-001", en001Index, true},
77	{"zh-Hant-HK", zhHantHKIndex, true},
78	{"zh-HK", zhHantHKIndex, false}, // maximized to zh-Hant-HK
79	{"nl-Beng", 0, false},           // parent skips script
80	{"nl-NO", nlIndex, false},       // region is ignored
81	{"nl-Latn-NO", nlIndex, false},
82	{"nl-Latn-NO-u-co-phonebk", nlIndex, false},
83	{"nl-Latn-NO-valencia", nlIndex, false},
84	{"nl-Latn-NO-oxendict", nlIndex, false},
85	{"sh", shIndex, true}, // From plural rules.
86}
87
88func TestLanguageID(t *testing.T) {
89	tests := append(compactTests, []compactTest{
90		{"en-GB", enGBIndex, true},
91		{"en-GB-u-rg-uszzzz", enGBIndex, true},
92		{"en-GB-u-rg-USZZZZ", enGBIndex, true},
93		{"en-GB-u-rg-uszzzz-va-posix", enGBIndex, false},
94		{"en-GB-u-co-phonebk-rg-uszzzz", enGBIndex, false},
95		// Invalid region specifications are ignored.
96		{"en-GB-u-rg-usz-va-posix", enGBIndex, false},
97		{"en-GB-u-co-phonebk-rg-usz", enGBIndex, false},
98	}...)
99	for _, tt := range tests {
100		x, ok := LanguageID(mustParse(tt.tag))
101		if ID(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
107func TestRegionalID(t *testing.T) {
108	tests := append(compactTests, []compactTest{
109		{"en-GB", enGBIndex, true},
110		{"en-GB-u-rg-uszzzz", enUSIndex, true},
111		{"en-GB-u-rg-USZZZZ", enUSIndex, true},
112		// TODO: use different exact values for language and regional tag?
113		{"en-GB-u-rg-uszzzz-va-posix", enUSuvaposixIndex, false},
114		{"en-GB-u-co-phonebk-rg-uszzzz-va-posix", enUSuvaposixIndex, false},
115		{"en-GB-u-co-phonebk-rg-uszzzz", enUSIndex, false},
116		// Invalid region specifications are ignored.
117		{"en-GB-u-rg-usz-va-posix", enGBIndex, false},
118		{"en-GB-u-co-phonebk-rg-usz", enGBIndex, false},
119	}...)
120	for _, tt := range tests {
121		x, ok := RegionalID(mustParse(tt.tag))
122		if ID(x) != tt.index || ok != tt.ok {
123			t.Errorf("%s: got %d, %v; want %d %v", tt.tag, x, ok, tt.index, tt.ok)
124		}
125	}
126}
127
128func TestParent(t *testing.T) {
129	tests := []struct{ in, out string }{
130		// Strip variants and extensions first
131		{"de-u-co-phonebk", "de"},
132		{"de-1994", "de"},
133		{"de-Latn-1994", "de"}, // remove superfluous script.
134
135		// Ensure the canonical Tag for an entry is in the chain for base-script
136		// pairs.
137		{"zh-Hans", "zh"},
138
139		// Skip the script if it is the maximized version. CLDR files for the
140		// skipped tag are always empty.
141		{"zh-Hans-TW", "zh"},
142		{"zh-Hans-CN", "zh"},
143
144		// Insert the script if the maximized script is not the same as the
145		// maximized script of the base language.
146		{"zh-TW", "zh-Hant"},
147		{"zh-HK", "zh-Hant"},
148		{"zh-Hant-TW", "zh-Hant"},
149		{"zh-Hant-HK", "zh-Hant"},
150
151		// Non-default script skips to und.
152		// CLDR
153		{"az-Cyrl", "und"},
154		{"bs-Cyrl", "und"},
155		{"en-Dsrt", "und"},
156		{"ha-Arab", "und"},
157		{"mn-Mong", "und"},
158		{"pa-Arab", "und"},
159		{"shi-Latn", "und"},
160		{"sr-Latn", "und"},
161		{"uz-Arab", "und"},
162		{"uz-Cyrl", "und"},
163		{"vai-Latn", "und"},
164		{"zh-Hant", "und"},
165		// extra
166		{"nl-Cyrl", "und"},
167
168		// World english inherits from en-001.
169		{"en-150", "en-001"},
170		{"en-AU", "en-001"},
171		{"en-BE", "en-001"},
172		{"en-GG", "en-001"},
173		{"en-GI", "en-001"},
174		{"en-HK", "en-001"},
175		{"en-IE", "en-001"},
176		{"en-IM", "en-001"},
177		{"en-IN", "en-001"},
178		{"en-JE", "en-001"},
179		{"en-MT", "en-001"},
180		{"en-NZ", "en-001"},
181		{"en-PK", "en-001"},
182		{"en-SG", "en-001"},
183
184		// Spanish in Latin-American countries have es-419 as parent.
185		{"es-AR", "es-419"},
186		{"es-BO", "es-419"},
187		{"es-CL", "es-419"},
188		{"es-CO", "es-419"},
189		{"es-CR", "es-419"},
190		{"es-CU", "es-419"},
191		{"es-DO", "es-419"},
192		{"es-EC", "es-419"},
193		{"es-GT", "es-419"},
194		{"es-HN", "es-419"},
195		{"es-MX", "es-419"},
196		{"es-NI", "es-419"},
197		{"es-PA", "es-419"},
198		{"es-PE", "es-419"},
199		{"es-PR", "es-419"},
200		{"es-PY", "es-419"},
201		{"es-SV", "es-419"},
202		{"es-US", "es-419"},
203		{"es-UY", "es-419"},
204		{"es-VE", "es-419"},
205		// exceptions (according to CLDR)
206		{"es-CW", "es"},
207
208		// Inherit from pt-PT, instead of pt for these countries.
209		{"pt-AO", "pt-PT"},
210		{"pt-CV", "pt-PT"},
211		{"pt-GW", "pt-PT"},
212		{"pt-MO", "pt-PT"},
213		{"pt-MZ", "pt-PT"},
214		{"pt-ST", "pt-PT"},
215		{"pt-TL", "pt-PT"},
216
217		{"en-GB-u-co-phonebk-rg-uszzzz", "en-GB"},
218		{"en-GB-u-rg-uszzzz", "en-GB"},
219		{"en-US-u-va-posix", "en-US"},
220
221		// Difference between language and regional tag.
222		{"ca-ES-valencia", "ca-ES"},
223		{"ca-ES-valencia-u-rg-ptzzzz", "ca-ES"}, // t.full != nil
224		{"en-US-u-va-variant", "en-US"},
225		{"en-u-va-variant", "en"}, // t.full != nil
226		{"en-u-rg-gbzzzz", "en"},
227		{"en-US-u-rg-gbzzzz", "en-US"},
228		{"nl-US-u-rg-gbzzzz", "nl-US"}, // t.full != nil
229	}
230	for _, tt := range tests {
231		tag := mustParse(tt.in)
232		if p := mustParse(tt.out); p != tag.Parent() {
233			t.Errorf("%s: was %v; want %v", tt.in, tag.Parent(), p)
234		}
235	}
236}
237