1// Copyright 2015 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 number
6
7import (
8	"reflect"
9	"testing"
10	"unsafe"
11)
12
13var testCases = []struct {
14	pat  string
15	want *Pattern
16}{{
17	"#",
18	&Pattern{
19		FormatWidth: 1,
20		// TODO: Should MinIntegerDigits be 1?
21	},
22}, {
23	"0",
24	&Pattern{
25		FormatWidth: 1,
26		RoundingContext: RoundingContext{
27			MinIntegerDigits: 1,
28		},
29	},
30}, {
31	"+0",
32	&Pattern{
33		Affix:       "\x01+\x00",
34		FormatWidth: 2,
35		RoundingContext: RoundingContext{
36			MinIntegerDigits: 1,
37		},
38	},
39}, {
40	"0+",
41	&Pattern{
42		Affix:       "\x00\x01+",
43		FormatWidth: 2,
44		RoundingContext: RoundingContext{
45			MinIntegerDigits: 1,
46		},
47	},
48}, {
49	"0000",
50	&Pattern{
51		FormatWidth: 4,
52		RoundingContext: RoundingContext{
53			MinIntegerDigits: 4,
54		},
55	},
56}, {
57	".#",
58	&Pattern{
59		FormatWidth: 2,
60		RoundingContext: RoundingContext{
61			MaxFractionDigits: 1,
62		},
63	},
64}, {
65	"#0.###",
66	&Pattern{
67		FormatWidth: 6,
68		RoundingContext: RoundingContext{
69			MinIntegerDigits:  1,
70			MaxFractionDigits: 3,
71		},
72	},
73}, {
74	"#0.######",
75	&Pattern{
76		FormatWidth: 9,
77		RoundingContext: RoundingContext{
78			MinIntegerDigits:  1,
79			MaxFractionDigits: 6,
80		},
81	},
82}, {
83	"#,0",
84	&Pattern{
85		FormatWidth:  3,
86		GroupingSize: [2]uint8{1, 0},
87		RoundingContext: RoundingContext{
88			MinIntegerDigits: 1,
89		},
90	},
91}, {
92	"#,0.00",
93	&Pattern{
94		FormatWidth:  6,
95		GroupingSize: [2]uint8{1, 0},
96		RoundingContext: RoundingContext{
97			MinIntegerDigits:  1,
98			MinFractionDigits: 2,
99			MaxFractionDigits: 2,
100		},
101	},
102}, {
103	"#,##0.###",
104	&Pattern{
105		FormatWidth:  9,
106		GroupingSize: [2]uint8{3, 0},
107		RoundingContext: RoundingContext{
108			MinIntegerDigits:  1,
109			MaxFractionDigits: 3,
110		},
111	},
112}, {
113	"#,##,##0.###",
114	&Pattern{
115		FormatWidth:  12,
116		GroupingSize: [2]uint8{3, 2},
117		RoundingContext: RoundingContext{
118			MinIntegerDigits:  1,
119			MaxFractionDigits: 3,
120		},
121	},
122}, {
123	// Ignore additional separators.
124	"#,####,##,##0.###",
125	&Pattern{
126		FormatWidth:  17,
127		GroupingSize: [2]uint8{3, 2},
128		RoundingContext: RoundingContext{
129			MinIntegerDigits:  1,
130			MaxFractionDigits: 3,
131		},
132	},
133}, {
134	"#E0",
135	&Pattern{
136		FormatWidth: 3,
137		RoundingContext: RoundingContext{
138			MaxIntegerDigits:  1,
139			MinExponentDigits: 1,
140		},
141	},
142}, {
143	// At least one exponent digit is required. As long as this is true, one can
144	// determine that scientific rendering is needed if MinExponentDigits > 0.
145	"#E#",
146	nil,
147}, {
148	"0E0",
149	&Pattern{
150		FormatWidth: 3,
151		RoundingContext: RoundingContext{
152			MinIntegerDigits:  1,
153			MinExponentDigits: 1,
154		},
155	},
156}, {
157	"##0.###E00",
158	&Pattern{
159		FormatWidth: 10,
160		RoundingContext: RoundingContext{
161			MinIntegerDigits:  1,
162			MaxIntegerDigits:  3,
163			MaxFractionDigits: 3,
164			MinExponentDigits: 2,
165		},
166	},
167}, {
168	"##00.0#E0",
169	&Pattern{
170		FormatWidth: 9,
171		RoundingContext: RoundingContext{
172			MinIntegerDigits:  2,
173			MaxIntegerDigits:  4,
174			MinFractionDigits: 1,
175			MaxFractionDigits: 2,
176			MinExponentDigits: 1,
177		},
178	},
179}, {
180	"#00.0E+0",
181	&Pattern{
182		FormatWidth: 8,
183		Flags:       AlwaysExpSign,
184		RoundingContext: RoundingContext{
185			MinIntegerDigits:  2,
186			MaxIntegerDigits:  3,
187			MinFractionDigits: 1,
188			MaxFractionDigits: 1,
189			MinExponentDigits: 1,
190		},
191	},
192}, {
193	"0.0E++0",
194	nil,
195}, {
196	"#0E+",
197	nil,
198}, {
199	// significant digits
200	"@",
201	&Pattern{
202		FormatWidth: 1,
203		RoundingContext: RoundingContext{
204			MinSignificantDigits: 1,
205			MaxSignificantDigits: 1,
206			MaxFractionDigits:    -1,
207		},
208	},
209}, {
210	// significant digits
211	"@@@@",
212	&Pattern{
213		FormatWidth: 4,
214		RoundingContext: RoundingContext{
215			MinSignificantDigits: 4,
216			MaxSignificantDigits: 4,
217			MaxFractionDigits:    -1,
218		},
219	},
220}, {
221	"@###",
222	&Pattern{
223		FormatWidth: 4,
224		RoundingContext: RoundingContext{
225			MinSignificantDigits: 1,
226			MaxSignificantDigits: 4,
227			MaxFractionDigits:    -1,
228		},
229	},
230}, {
231	// Exponents in significant digits mode gets normalized.
232	"@@E0",
233	&Pattern{
234		FormatWidth: 4,
235		RoundingContext: RoundingContext{
236			MinIntegerDigits:  1,
237			MaxIntegerDigits:  1,
238			MinFractionDigits: 1,
239			MaxFractionDigits: 1,
240			MinExponentDigits: 1,
241		},
242	},
243}, {
244	"@###E00",
245	&Pattern{
246		FormatWidth: 7,
247		RoundingContext: RoundingContext{
248			MinIntegerDigits:  1,
249			MaxIntegerDigits:  1,
250			MinFractionDigits: 0,
251			MaxFractionDigits: 3,
252			MinExponentDigits: 2,
253		},
254	},
255}, {
256	// The significant digits mode does not allow fractions.
257	"@###.#E0",
258	nil,
259}, {
260	//alternative negative pattern
261	"#0.###;(#0.###)",
262	&Pattern{
263		Affix:       "\x00\x00\x01(\x01)",
264		NegOffset:   2,
265		FormatWidth: 6,
266		RoundingContext: RoundingContext{
267			MinIntegerDigits:  1,
268			MaxFractionDigits: 3,
269		},
270	},
271}, {
272	// Rounding increment
273	"1.05",
274	&Pattern{
275		FormatWidth: 4,
276		RoundingContext: RoundingContext{
277			Increment:         105,
278			IncrementScale:    2,
279			MinIntegerDigits:  1,
280			MinFractionDigits: 2,
281			MaxFractionDigits: 2,
282		},
283	},
284}, {
285	// Rounding increment with grouping
286	"1,05",
287	&Pattern{
288		FormatWidth:  4,
289		GroupingSize: [2]uint8{2, 0},
290		RoundingContext: RoundingContext{
291			Increment:         105,
292			IncrementScale:    0,
293			MinIntegerDigits:  3,
294			MinFractionDigits: 0,
295			MaxFractionDigits: 0,
296		},
297	},
298}, {
299	"0.0%",
300	&Pattern{
301		Affix:       "\x00\x01%",
302		FormatWidth: 4,
303		RoundingContext: RoundingContext{
304			DigitShift:        2,
305			MinIntegerDigits:  1,
306			MinFractionDigits: 1,
307			MaxFractionDigits: 1,
308		},
309	},
310}, {
311	"0.0‰",
312	&Pattern{
313		Affix:       "\x00\x03‰",
314		FormatWidth: 4,
315		RoundingContext: RoundingContext{
316			DigitShift:        3,
317			MinIntegerDigits:  1,
318			MinFractionDigits: 1,
319			MaxFractionDigits: 1,
320		},
321	},
322}, {
323	"#,##0.00¤",
324	&Pattern{
325		Affix:        "\x00\x02¤",
326		FormatWidth:  9,
327		GroupingSize: [2]uint8{3, 0},
328		RoundingContext: RoundingContext{
329			MinIntegerDigits:  1,
330			MinFractionDigits: 2,
331			MaxFractionDigits: 2,
332		},
333	},
334}, {
335	"#,##0.00 ¤;(#,##0.00 ¤)",
336	&Pattern{Affix: "\x00\x04\u00a0¤\x01(\x05\u00a0¤)",
337		NegOffset:    6,
338		FormatWidth:  10,
339		GroupingSize: [2]uint8{3, 0},
340		RoundingContext: RoundingContext{
341			DigitShift:        0,
342			MinIntegerDigits:  1,
343			MinFractionDigits: 2,
344			MaxFractionDigits: 2,
345		},
346	},
347}, {
348	// padding
349	"*x#",
350	&Pattern{
351		PadRune:     'x',
352		FormatWidth: 1,
353	},
354}, {
355	// padding
356	"#*x",
357	&Pattern{
358		PadRune:     'x',
359		FormatWidth: 1,
360		Flags:       PadBeforeSuffix,
361	},
362}, {
363	"*xpre#suf",
364	&Pattern{
365		Affix:       "\x03pre\x03suf",
366		PadRune:     'x',
367		FormatWidth: 7,
368	},
369}, {
370	"pre*x#suf",
371	&Pattern{
372		Affix:       "\x03pre\x03suf",
373		PadRune:     'x',
374		FormatWidth: 7,
375		Flags:       PadAfterPrefix,
376	},
377}, {
378	"pre#*xsuf",
379	&Pattern{
380		Affix:       "\x03pre\x03suf",
381		PadRune:     'x',
382		FormatWidth: 7,
383		Flags:       PadBeforeSuffix,
384	},
385}, {
386	"pre#suf*x",
387	&Pattern{
388		Affix:       "\x03pre\x03suf",
389		PadRune:     'x',
390		FormatWidth: 7,
391		Flags:       PadAfterSuffix,
392	},
393}, {
394	`* #0 o''clock`,
395	&Pattern{Affix: "\x00\x09 o\\'clock",
396		FormatWidth: 10,
397		PadRune:     32,
398		RoundingContext: RoundingContext{
399			MinIntegerDigits: 0x1,
400		},
401	},
402}, {
403	`'123'* #0'456'`,
404	&Pattern{Affix: "\x05'123'\x05'456'",
405		FormatWidth: 8,
406		PadRune:     32,
407		RoundingContext: RoundingContext{
408			MinIntegerDigits: 0x1,
409		},
410		Flags: PadAfterPrefix},
411}, {
412	// no duplicate padding
413	"*xpre#suf*x", nil,
414}, {
415	// no duplicate padding
416	"*xpre#suf*x", nil,
417}}
418
419func TestParsePattern(t *testing.T) {
420	for i, tc := range testCases {
421		t.Run(tc.pat, func(t *testing.T) {
422			f, err := ParsePattern(tc.pat)
423			if !reflect.DeepEqual(f, tc.want) {
424				t.Errorf("%d:%s:\ngot %#v;\nwant %#v", i, tc.pat, f, tc.want)
425			}
426			if got, want := err != nil, tc.want == nil; got != want {
427				t.Errorf("%d:%s:error: got %v; want %v", i, tc.pat, err, want)
428			}
429		})
430	}
431}
432
433func TestPatternSize(t *testing.T) {
434	if sz := unsafe.Sizeof(Pattern{}); sz > 56 {
435		t.Errorf("got %d; want <= 56", sz)
436	}
437
438}
439