| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package number |
| |
| import ( |
| "reflect" |
| "testing" |
| "unsafe" |
| ) |
| |
| var testCases = []struct { |
| pat string |
| want *Pattern |
| }{{ |
| "#", |
| &Pattern{ |
| FormatWidth: 1, |
| // TODO: Should MinIntegerDigits be 1? |
| }, |
| }, { |
| "0", |
| &Pattern{ |
| FormatWidth: 1, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| }, |
| }, |
| }, { |
| "+0", |
| &Pattern{ |
| Affix: "\x01+\x00", |
| FormatWidth: 2, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| }, |
| }, |
| }, { |
| "0+", |
| &Pattern{ |
| Affix: "\x00\x01+", |
| FormatWidth: 2, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| }, |
| }, |
| }, { |
| "0000", |
| &Pattern{ |
| FormatWidth: 4, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 4, |
| }, |
| }, |
| }, { |
| ".#", |
| &Pattern{ |
| FormatWidth: 2, |
| RoundingContext: RoundingContext{ |
| MaxFractionDigits: 1, |
| }, |
| }, |
| }, { |
| "#0.###", |
| &Pattern{ |
| FormatWidth: 6, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxFractionDigits: 3, |
| }, |
| }, |
| }, { |
| "#0.######", |
| &Pattern{ |
| FormatWidth: 9, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxFractionDigits: 6, |
| }, |
| }, |
| }, { |
| "#,0", |
| &Pattern{ |
| FormatWidth: 3, |
| GroupingSize: [2]uint8{1, 0}, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| }, |
| }, |
| }, { |
| "#,0.00", |
| &Pattern{ |
| FormatWidth: 6, |
| GroupingSize: [2]uint8{1, 0}, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MinFractionDigits: 2, |
| MaxFractionDigits: 2, |
| }, |
| }, |
| }, { |
| "#,##0.###", |
| &Pattern{ |
| FormatWidth: 9, |
| GroupingSize: [2]uint8{3, 0}, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxFractionDigits: 3, |
| }, |
| }, |
| }, { |
| "#,##,##0.###", |
| &Pattern{ |
| FormatWidth: 12, |
| GroupingSize: [2]uint8{3, 2}, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxFractionDigits: 3, |
| }, |
| }, |
| }, { |
| // Ignore additional separators. |
| "#,####,##,##0.###", |
| &Pattern{ |
| FormatWidth: 17, |
| GroupingSize: [2]uint8{3, 2}, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxFractionDigits: 3, |
| }, |
| }, |
| }, { |
| "#E0", |
| &Pattern{ |
| FormatWidth: 3, |
| RoundingContext: RoundingContext{ |
| MaxIntegerDigits: 1, |
| MinExponentDigits: 1, |
| }, |
| }, |
| }, { |
| // At least one exponent digit is required. As long as this is true, one can |
| // determine that scientific rendering is needed if MinExponentDigits > 0. |
| "#E#", |
| nil, |
| }, { |
| "0E0", |
| &Pattern{ |
| FormatWidth: 3, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MinExponentDigits: 1, |
| }, |
| }, |
| }, { |
| "##0.###E00", |
| &Pattern{ |
| FormatWidth: 10, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxIntegerDigits: 3, |
| MaxFractionDigits: 3, |
| MinExponentDigits: 2, |
| }, |
| }, |
| }, { |
| "##00.0#E0", |
| &Pattern{ |
| FormatWidth: 9, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 2, |
| MaxIntegerDigits: 4, |
| MinFractionDigits: 1, |
| MaxFractionDigits: 2, |
| MinExponentDigits: 1, |
| }, |
| }, |
| }, { |
| "#00.0E+0", |
| &Pattern{ |
| FormatWidth: 8, |
| Flags: AlwaysExpSign, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 2, |
| MaxIntegerDigits: 3, |
| MinFractionDigits: 1, |
| MaxFractionDigits: 1, |
| MinExponentDigits: 1, |
| }, |
| }, |
| }, { |
| "0.0E++0", |
| nil, |
| }, { |
| "#0E+", |
| nil, |
| }, { |
| // significant digits |
| "@", |
| &Pattern{ |
| FormatWidth: 1, |
| RoundingContext: RoundingContext{ |
| MinSignificantDigits: 1, |
| MaxSignificantDigits: 1, |
| MaxFractionDigits: -1, |
| }, |
| }, |
| }, { |
| // significant digits |
| "@@@@", |
| &Pattern{ |
| FormatWidth: 4, |
| RoundingContext: RoundingContext{ |
| MinSignificantDigits: 4, |
| MaxSignificantDigits: 4, |
| MaxFractionDigits: -1, |
| }, |
| }, |
| }, { |
| "@###", |
| &Pattern{ |
| FormatWidth: 4, |
| RoundingContext: RoundingContext{ |
| MinSignificantDigits: 1, |
| MaxSignificantDigits: 4, |
| MaxFractionDigits: -1, |
| }, |
| }, |
| }, { |
| // Exponents in significant digits mode gets normalized. |
| "@@E0", |
| &Pattern{ |
| FormatWidth: 4, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxIntegerDigits: 1, |
| MinFractionDigits: 1, |
| MaxFractionDigits: 1, |
| MinExponentDigits: 1, |
| }, |
| }, |
| }, { |
| "@###E00", |
| &Pattern{ |
| FormatWidth: 7, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxIntegerDigits: 1, |
| MinFractionDigits: 0, |
| MaxFractionDigits: 3, |
| MinExponentDigits: 2, |
| }, |
| }, |
| }, { |
| // The significant digits mode does not allow fractions. |
| "@###.#E0", |
| nil, |
| }, { |
| //alternative negative pattern |
| "#0.###;(#0.###)", |
| &Pattern{ |
| Affix: "\x00\x00\x01(\x01)", |
| NegOffset: 2, |
| FormatWidth: 6, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MaxFractionDigits: 3, |
| }, |
| }, |
| }, { |
| // Rounding increment |
| "1.05", |
| &Pattern{ |
| FormatWidth: 4, |
| RoundingContext: RoundingContext{ |
| Increment: 105, |
| IncrementScale: 2, |
| MinIntegerDigits: 1, |
| MinFractionDigits: 2, |
| MaxFractionDigits: 2, |
| }, |
| }, |
| }, { |
| // Rounding increment with grouping |
| "1,05", |
| &Pattern{ |
| FormatWidth: 4, |
| GroupingSize: [2]uint8{2, 0}, |
| RoundingContext: RoundingContext{ |
| Increment: 105, |
| IncrementScale: 0, |
| MinIntegerDigits: 3, |
| MinFractionDigits: 0, |
| MaxFractionDigits: 0, |
| }, |
| }, |
| }, { |
| "0.0%", |
| &Pattern{ |
| Affix: "\x00\x01%", |
| FormatWidth: 4, |
| RoundingContext: RoundingContext{ |
| DigitShift: 2, |
| MinIntegerDigits: 1, |
| MinFractionDigits: 1, |
| MaxFractionDigits: 1, |
| }, |
| }, |
| }, { |
| "0.0‰", |
| &Pattern{ |
| Affix: "\x00\x03‰", |
| FormatWidth: 4, |
| RoundingContext: RoundingContext{ |
| DigitShift: 3, |
| MinIntegerDigits: 1, |
| MinFractionDigits: 1, |
| MaxFractionDigits: 1, |
| }, |
| }, |
| }, { |
| "#,##0.00¤", |
| &Pattern{ |
| Affix: "\x00\x02¤", |
| FormatWidth: 9, |
| GroupingSize: [2]uint8{3, 0}, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 1, |
| MinFractionDigits: 2, |
| MaxFractionDigits: 2, |
| }, |
| }, |
| }, { |
| "#,##0.00 ¤;(#,##0.00 ¤)", |
| &Pattern{Affix: "\x00\x04\u00a0¤\x01(\x05\u00a0¤)", |
| NegOffset: 6, |
| FormatWidth: 10, |
| GroupingSize: [2]uint8{3, 0}, |
| RoundingContext: RoundingContext{ |
| DigitShift: 0, |
| MinIntegerDigits: 1, |
| MinFractionDigits: 2, |
| MaxFractionDigits: 2, |
| }, |
| }, |
| }, { |
| // padding |
| "*x#", |
| &Pattern{ |
| PadRune: 'x', |
| FormatWidth: 1, |
| }, |
| }, { |
| // padding |
| "#*x", |
| &Pattern{ |
| PadRune: 'x', |
| FormatWidth: 1, |
| Flags: PadBeforeSuffix, |
| }, |
| }, { |
| "*xpre#suf", |
| &Pattern{ |
| Affix: "\x03pre\x03suf", |
| PadRune: 'x', |
| FormatWidth: 7, |
| }, |
| }, { |
| "pre*x#suf", |
| &Pattern{ |
| Affix: "\x03pre\x03suf", |
| PadRune: 'x', |
| FormatWidth: 7, |
| Flags: PadAfterPrefix, |
| }, |
| }, { |
| "pre#*xsuf", |
| &Pattern{ |
| Affix: "\x03pre\x03suf", |
| PadRune: 'x', |
| FormatWidth: 7, |
| Flags: PadBeforeSuffix, |
| }, |
| }, { |
| "pre#suf*x", |
| &Pattern{ |
| Affix: "\x03pre\x03suf", |
| PadRune: 'x', |
| FormatWidth: 7, |
| Flags: PadAfterSuffix, |
| }, |
| }, { |
| `* #0 o''clock`, |
| &Pattern{Affix: "\x00\x09 o\\'clock", |
| FormatWidth: 10, |
| PadRune: 32, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 0x1, |
| }, |
| }, |
| }, { |
| `'123'* #0'456'`, |
| &Pattern{Affix: "\x05'123'\x05'456'", |
| FormatWidth: 8, |
| PadRune: 32, |
| RoundingContext: RoundingContext{ |
| MinIntegerDigits: 0x1, |
| }, |
| Flags: PadAfterPrefix}, |
| }, { |
| // no duplicate padding |
| "*xpre#suf*x", nil, |
| }, { |
| // no duplicate padding |
| "*xpre#suf*x", nil, |
| }} |
| |
| func TestParsePattern(t *testing.T) { |
| for i, tc := range testCases { |
| t.Run(tc.pat, func(t *testing.T) { |
| f, err := ParsePattern(tc.pat) |
| if !reflect.DeepEqual(f, tc.want) { |
| t.Errorf("%d:%s:\ngot %#v;\nwant %#v", i, tc.pat, f, tc.want) |
| } |
| if got, want := err != nil, tc.want == nil; got != want { |
| t.Errorf("%d:%s:error: got %v; want %v", i, tc.pat, err, want) |
| } |
| }) |
| } |
| } |
| |
| func TestPatternSize(t *testing.T) { |
| if sz := unsafe.Sizeof(Pattern{}); sz > 56 { |
| t.Errorf("got %d; want <= 56", sz) |
| } |
| |
| } |