1package cldr
2
3import (
4	"fmt"
5	"log"
6	"reflect"
7	"testing"
8)
9
10func failOnError(err error) {
11	if err != nil {
12		log.Panic(err)
13	}
14}
15
16func data() *CLDR {
17	d := Decoder{}
18	data, err := d.Decode(testLoader{})
19	failOnError(err)
20	return data
21}
22
23type h struct {
24	A string `xml:"ha,attr"`
25	E string `xml:"he"`
26	D string `xml:",chardata"`
27	X string
28}
29
30type fieldTest struct {
31	Common
32	To  string `xml:"to,attr"`
33	Key string `xml:"key,attr"`
34	E   string `xml:"e"`
35	D   string `xml:",chardata"`
36	X   string
37	h
38}
39
40var testStruct = fieldTest{
41	Common: Common{
42		name: "mapping", // exclude "type" as distinguishing attribute
43		Type: "foo",
44		Alt:  "foo",
45	},
46	To:  "nyc",
47	Key: "k",
48	E:   "E",
49	D:   "D",
50	h: h{
51		A: "A",
52		E: "E",
53		D: "D",
54	},
55}
56
57func TestIter(t *testing.T) {
58	tests := map[string]string{
59		"Type":  "foo",
60		"Alt":   "foo",
61		"To":    "nyc",
62		"A":     "A",
63		"Alias": "<nil>",
64	}
65	k := 0
66	for i := iter(reflect.ValueOf(testStruct)); !i.done(); i.next() {
67		v := i.value()
68		if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.String {
69			v = v.Elem()
70		}
71		name := i.field().Name
72		if w, ok := tests[name]; ok {
73			s := fmt.Sprint(v.Interface())
74			if w != s {
75				t.Errorf("value: found %q; want %q", w, s)
76			}
77			delete(tests, name)
78		}
79		k++
80	}
81	if len(tests) != 0 {
82		t.Errorf("missing fields: %v", tests)
83	}
84}
85
86func TestFindField(t *testing.T) {
87	tests := []struct {
88		name, val string
89		exist     bool
90	}{
91		{"type", "foo", true},
92		{"alt", "foo", true},
93		{"to", "nyc", true},
94		{"he", "E", true},
95		{"q", "", false},
96	}
97	vf := reflect.ValueOf(testStruct)
98	for i, tt := range tests {
99		v, err := findField(vf, tt.name)
100		if (err == nil) != tt.exist {
101			t.Errorf("%d: field %q present is %v; want %v", i, tt.name, err == nil, tt.exist)
102		} else if tt.exist {
103			if v.Kind() == reflect.Ptr {
104				if v.IsNil() {
105					continue
106				}
107				v = v.Elem()
108			}
109			if v.String() != tt.val {
110				t.Errorf("%d: found value %q; want %q", i, v.String(), tt.val)
111			}
112		}
113	}
114}
115
116var keyTests = []struct {
117	exclude []string
118	key     string
119}{
120	{[]string{}, "alt=foo;key=k;to=nyc"},
121	{[]string{"type"}, "alt=foo;key=k;to=nyc"},
122	{[]string{"choice"}, "alt=foo;key=k;to=nyc"},
123	{[]string{"alt"}, "key=k;to=nyc"},
124	{[]string{"a"}, "alt=foo;key=k;to=nyc"},
125	{[]string{"to"}, "alt=foo;key=k"},
126	{[]string{"alt", "to"}, "key=k"},
127	{[]string{"alt", "to", "key"}, ""},
128}
129
130func TestAttrKey(t *testing.T) {
131	v := reflect.ValueOf(&testStruct)
132	for i, tt := range keyTests {
133		key := attrKey(v, tt.exclude...)
134		if key != tt.key {
135			t.Errorf("%d: found %q, want %q", i, key, tt.key)
136		}
137	}
138}
139
140func TestKey(t *testing.T) {
141	for i, tt := range keyTests {
142		key := Key(&testStruct, tt.exclude...)
143		if key != tt.key {
144			t.Errorf("%d: found %q, want %q", i, key, tt.key)
145		}
146	}
147}
148
149func testEnclosing(t *testing.T, x *LDML, name string) {
150	eq := func(a, b Elem, i int) {
151		for ; i > 0; i-- {
152			b = b.enclosing()
153		}
154		if a != b {
155			t.Errorf("%s: found path %q, want %q", name, getPath(a), getPath(b))
156		}
157	}
158	eq(x, x, 0)
159	eq(x, x.Identity, 1)
160	eq(x, x.Dates.Calendars, 2)
161	eq(x, x.Dates.Calendars.Calendar[0], 3)
162	eq(x, x.Dates.Calendars.Calendar[1], 3)
163	//eq(x, x.Dates.Calendars.Calendar[0].Months, 4)
164	eq(x, x.Dates.Calendars.Calendar[1].Months, 4)
165}
166
167func TestEnclosing(t *testing.T) {
168	testEnclosing(t, data().RawLDML("de"), "enclosing-raw")
169	de, _ := data().LDML("de")
170	testEnclosing(t, de, "enclosing")
171}
172
173func TestDeepCopy(t *testing.T) {
174	eq := func(have, want string) {
175		if have != want {
176			t.Errorf("found %q; want %q", have, want)
177		}
178	}
179	x, _ := data().LDML("de")
180	vc := deepCopy(reflect.ValueOf(x))
181	c := vc.Interface().(*LDML)
182	linkEnclosing(nil, c)
183	if x == c {
184		t.Errorf("did not copy")
185	}
186
187	eq(c.name, "ldml")
188	eq(c.Dates.name, "dates")
189	testEnclosing(t, c, "deepCopy")
190}
191
192type getTest struct {
193	loc     string
194	path    string
195	field   string // used in combination with length
196	data    string
197	altData string // used for buddhist calendar if value != ""
198	typ     string
199	length  int
200	missing bool
201}
202
203const (
204	budMon = "dates/calendars/calendar[@type='buddhist']/months/"
205	chnMon = "dates/calendars/calendar[@type='chinese']/months/"
206	greMon = "dates/calendars/calendar[@type='gregorian']/months/"
207)
208
209func monthVal(path, context, width string, month int) string {
210	const format = "%s/monthContext[@type='%s']/monthWidth[@type='%s']/month[@type='%d']"
211	return fmt.Sprintf(format, path, context, width, month)
212}
213
214var rootGetTests = []getTest{
215	{loc: "root", path: "identity/language", typ: "root"},
216	{loc: "root", path: "characters/moreInformation", data: "?"},
217	{loc: "root", path: "characters", field: "exemplarCharacters", length: 3},
218	{loc: "root", path: greMon, field: "monthContext", length: 2},
219	{loc: "root", path: greMon + "monthContext[@type='format']/monthWidth[@type='narrow']", field: "month", length: 4},
220	{loc: "root", path: greMon + "monthContext[@type='stand-alone']/monthWidth[@type='wide']", field: "month", length: 4},
221	// unescaping character data
222	{loc: "root", path: "characters/exemplarCharacters[@type='punctuation']", data: `[\- ‐ – — … ' ‘ ‚ " “ „ \& #]`},
223	// default resolution
224	{loc: "root", path: "dates/calendars/calendar", typ: "gregorian"},
225	// alias resolution
226	{loc: "root", path: budMon, field: "monthContext", length: 2},
227	// crossing but non-circular alias resolution
228	{loc: "root", path: budMon + "monthContext[@type='format']/monthWidth[@type='narrow']", field: "month", length: 4},
229	{loc: "root", path: budMon + "monthContext[@type='stand-alone']/monthWidth[@type='wide']", field: "month", length: 4},
230	{loc: "root", path: monthVal(greMon, "format", "wide", 1), data: "11"},
231	{loc: "root", path: monthVal(greMon, "format", "narrow", 2), data: "2"},
232	{loc: "root", path: monthVal(greMon, "stand-alone", "wide", 3), data: "33"},
233	{loc: "root", path: monthVal(greMon, "stand-alone", "narrow", 4), data: "4"},
234	{loc: "root", path: monthVal(budMon, "format", "wide", 1), data: "11"},
235	{loc: "root", path: monthVal(budMon, "format", "narrow", 2), data: "2"},
236	{loc: "root", path: monthVal(budMon, "stand-alone", "wide", 3), data: "33"},
237	{loc: "root", path: monthVal(budMon, "stand-alone", "narrow", 4), data: "4"},
238}
239
240// 19
241var deGetTests = []getTest{
242	{loc: "de", path: "identity/language", typ: "de"},
243	{loc: "de", path: "posix", length: 2},
244	{loc: "de", path: "characters", field: "exemplarCharacters", length: 4},
245	{loc: "de", path: "characters/exemplarCharacters[@type='auxiliary']", data: `[á à ă]`},
246	// identity is a blocking element, so de should not inherit generation from root.
247	{loc: "de", path: "identity/generation", missing: true},
248	// default resolution
249	{loc: "root", path: "dates/calendars/calendar", typ: "gregorian"},
250
251	// absolute path alias resolution
252	{loc: "gsw", path: "posix", field: "messages", length: 1},
253	{loc: "gsw", path: "posix/messages/yesstr", data: "yes:y"},
254}
255
256// 27(greMon) - 52(budMon) - 77(chnMon)
257func calGetTests(s string) []getTest {
258	tests := []getTest{
259		{loc: "de", path: s, length: 2},
260		{loc: "de", path: s + "monthContext[@type='format']/monthWidth[@type='wide']", field: "month", length: 5},
261		{loc: "de", path: monthVal(s, "format", "wide", 1), data: "11"},
262		{loc: "de", path: monthVal(s, "format", "wide", 2), data: "22"},
263		{loc: "de", path: monthVal(s, "format", "wide", 3), data: "Maerz", altData: "bbb"},
264		{loc: "de", path: monthVal(s, "format", "wide", 4), data: "April"},
265		{loc: "de", path: monthVal(s, "format", "wide", 5), data: "Mai"},
266
267		{loc: "de", path: s + "monthContext[@type='format']/monthWidth[@type='narrow']", field: "month", length: 5},
268		{loc: "de", path: monthVal(s, "format", "narrow", 1), data: "1"},
269		{loc: "de", path: monthVal(s, "format", "narrow", 2), data: "2"},
270		{loc: "de", path: monthVal(s, "format", "narrow", 3), data: "M", altData: "BBB"},
271		{loc: "de", path: monthVal(s, "format", "narrow", 4), data: "A"},
272		{loc: "de", path: monthVal(s, "format", "narrow", 5), data: "m"},
273
274		{loc: "de", path: s + "monthContext[@type='stand-alone']/monthWidth[@type='wide']", field: "month", length: 5},
275		{loc: "de", path: monthVal(s, "stand-alone", "wide", 1), data: "11"},
276		{loc: "de", path: monthVal(s, "stand-alone", "wide", 2), data: "22"},
277		{loc: "de", path: monthVal(s, "stand-alone", "wide", 3), data: "Maerz", altData: "bbb"},
278		{loc: "de", path: monthVal(s, "stand-alone", "wide", 4), data: "april"},
279		{loc: "de", path: monthVal(s, "stand-alone", "wide", 5), data: "mai"},
280
281		{loc: "de", path: s + "monthContext[@type='stand-alone']/monthWidth[@type='narrow']", field: "month", length: 5},
282		{loc: "de", path: monthVal(s, "stand-alone", "narrow", 1), data: "1"},
283		{loc: "de", path: monthVal(s, "stand-alone", "narrow", 2), data: "2"},
284		{loc: "de", path: monthVal(s, "stand-alone", "narrow", 3), data: "m"},
285		{loc: "de", path: monthVal(s, "stand-alone", "narrow", 4), data: "4"},
286		{loc: "de", path: monthVal(s, "stand-alone", "narrow", 5), data: "m"},
287	}
288	if s == budMon {
289		for i, t := range tests {
290			if t.altData != "" {
291				tests[i].data = t.altData
292			}
293		}
294	}
295	return tests
296}
297
298var getTests = append(rootGetTests,
299	append(deGetTests,
300		append(calGetTests(greMon),
301			append(calGetTests(budMon),
302				calGetTests(chnMon)...)...)...)...)
303
304func TestPath(t *testing.T) {
305	d := data()
306	for i, tt := range getTests {
307		x, _ := d.LDML(tt.loc)
308		e, err := walkXPath(x, tt.path)
309		if err != nil {
310			if !tt.missing {
311				t.Errorf("%d:error: %v %v", i, err, tt.missing)
312			}
313			continue
314		}
315		if tt.missing {
316			t.Errorf("%d: missing is %v; want %v", i, e == nil, tt.missing)
317			continue
318		}
319		if tt.data != "" && e.GetCommon().Data() != tt.data {
320			t.Errorf("%d: data is %v; want %v", i, e.GetCommon().Data(), tt.data)
321			continue
322		}
323		if tt.typ != "" && e.GetCommon().Type != tt.typ {
324			t.Errorf("%d: type is %v; want %v", i, e.GetCommon().Type, tt.typ)
325			continue
326		}
327		if tt.field != "" {
328			slice, _ := findField(reflect.ValueOf(e), tt.field)
329			if slice.Len() != tt.length {
330				t.Errorf("%d: length is %v; want %v", i, slice.Len(), tt.length)
331				continue
332			}
333		}
334	}
335}
336
337func TestGet(t *testing.T) {
338	d := data()
339	for i, tt := range getTests {
340		x, _ := d.LDML(tt.loc)
341		e, err := Get(x, tt.path)
342		if err != nil {
343			if !tt.missing {
344				t.Errorf("%d:error: %v %v", i, err, tt.missing)
345			}
346			continue
347		}
348		if tt.missing {
349			t.Errorf("%d: missing is %v; want %v", i, e == nil, tt.missing)
350			continue
351		}
352		if tt.data != "" && e.GetCommon().Data() != tt.data {
353			t.Errorf("%d: data is %v; want %v", i, e.GetCommon().Data(), tt.data)
354			continue
355		}
356		if tt.typ != "" && e.GetCommon().Type != tt.typ {
357			t.Errorf("%d: type is %v; want %v", i, e.GetCommon().Type, tt.typ)
358			continue
359		}
360		if tt.field != "" {
361			slice, _ := findField(reflect.ValueOf(e), tt.field)
362			if slice.Len() != tt.length {
363				t.Errorf("%d: length is %v; want %v", i, slice.Len(), tt.length)
364				continue
365			}
366		}
367	}
368}
369