123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // Copyright 2016 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 plural
- import (
- "fmt"
- "reflect"
- "strconv"
- "strings"
- "testing"
- "golang.org/x/text/language"
- )
- func TestGetIntApprox(t *testing.T) {
- const big = 1234567890
- testCases := []struct {
- digits string
- start int
- end int
- nMod int
- want int
- }{
- {"123", 0, 1, 1, 1},
- {"123", 0, 2, 1, big},
- {"123", 0, 2, 2, 12},
- {"123", 3, 4, 2, 0},
- {"12345", 3, 4, 2, 4},
- {"40", 0, 1, 2, 4},
- {"1", 0, 7, 2, big},
- {"123", 0, 5, 2, big},
- {"123", 0, 5, 3, big},
- {"123", 0, 5, 4, big},
- {"123", 0, 5, 5, 12300},
- {"123", 0, 5, 6, 12300},
- {"123", 0, 5, 7, 12300},
- // Translation of examples in MatchDigits.
- // Integer parts
- {"123", 0, 3, 3, 123}, // 123
- {"1234", 0, 3, 3, 123}, // 123.4
- {"1", 0, 6, 8, 100000}, // 100000
- // Fraction parts
- {"123", 3, 3, 3, 0}, // 123
- {"1234", 3, 4, 3, 4}, // 123.4
- {"1234", 3, 5, 3, 40}, // 123.40
- {"1", 6, 8, 8, 0}, // 100000.00
- }
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("%s:%d:%d/%d", tc.digits, tc.start, tc.end, tc.nMod), func(t *testing.T) {
- got := getIntApprox(mkDigits(tc.digits), tc.start, tc.end, tc.nMod, big)
- if got != tc.want {
- t.Errorf("got %d; want %d", got, tc.want)
- }
- })
- }
- }
- func mkDigits(s string) []byte {
- b := []byte(s)
- for i := range b {
- b[i] -= '0'
- }
- return b
- }
- func TestValidForms(t *testing.T) {
- testCases := []struct {
- tag language.Tag
- want []Form
- }{
- {language.AmericanEnglish, []Form{Other, One}},
- {language.Portuguese, []Form{Other, One}},
- {language.Latvian, []Form{Other, Zero, One}},
- {language.Arabic, []Form{Other, Zero, One, Two, Few, Many}},
- {language.Russian, []Form{Other, One, Few, Many}},
- }
- for _, tc := range testCases {
- got := validForms(cardinal, tc.tag)
- if !reflect.DeepEqual(got, tc.want) {
- t.Errorf("validForms(%v): got %v; want %v", tc.tag, got, tc.want)
- }
- }
- }
- func TestOrdinal(t *testing.T) {
- testPlurals(t, Ordinal, ordinalTests)
- }
- func TestCardinal(t *testing.T) {
- testPlurals(t, Cardinal, cardinalTests)
- }
- func testPlurals(t *testing.T, p *Rules, testCases []pluralTest) {
- for _, tc := range testCases {
- for _, loc := range strings.Split(tc.locales, " ") {
- tag := language.MustParse(loc)
- // Test integers
- for _, s := range tc.integer {
- a := strings.Split(s, "~")
- from := parseUint(t, a[0])
- to := from
- if len(a) > 1 {
- to = parseUint(t, a[1])
- }
- for n := from; n <= to; n++ {
- t.Run(fmt.Sprintf("%s/int(%d)", loc, n), func(t *testing.T) {
- if f := p.matchComponents(tag, n, 0, 0); f != Form(tc.form) {
- t.Errorf("matchComponents: got %v; want %v", f, Form(tc.form))
- }
- digits := []byte(fmt.Sprint(n))
- for i := range digits {
- digits[i] -= '0'
- }
- if f := p.MatchDigits(tag, digits, len(digits), 0); f != Form(tc.form) {
- t.Errorf("MatchDigits: got %v; want %v", f, Form(tc.form))
- }
- })
- }
- }
- // Test decimals
- for _, s := range tc.decimal {
- a := strings.Split(s, "~")
- from, scale := parseFixedPoint(t, a[0])
- to := from
- if len(a) > 1 {
- var toScale int
- if to, toScale = parseFixedPoint(t, a[1]); toScale != scale {
- t.Fatalf("%s:%s: non-matching scales %d versus %d", loc, s, scale, toScale)
- }
- }
- m := 1
- for i := 0; i < scale; i++ {
- m *= 10
- }
- for n := from; n <= to; n++ {
- num := fmt.Sprintf("%[1]d.%0[3]*[2]d", n/m, n%m, scale)
- name := fmt.Sprintf("%s:dec(%s)", loc, num)
- t.Run(name, func(t *testing.T) {
- ff := n % m
- tt := ff
- w := scale
- for tt > 0 && tt%10 == 0 {
- w--
- tt /= 10
- }
- if f := p.MatchPlural(tag, n/m, scale, w, ff, tt); f != Form(tc.form) {
- t.Errorf("MatchPlural: got %v; want %v", f, Form(tc.form))
- }
- if f := p.matchComponents(tag, n/m, n%m, scale); f != Form(tc.form) {
- t.Errorf("matchComponents: got %v; want %v", f, Form(tc.form))
- }
- exp := strings.IndexByte(num, '.')
- digits := []byte(strings.Replace(num, ".", "", 1))
- for i := range digits {
- digits[i] -= '0'
- }
- if f := p.MatchDigits(tag, digits, exp, scale); f != Form(tc.form) {
- t.Errorf("MatchDigits: got %v; want %v", f, Form(tc.form))
- }
- })
- }
- }
- }
- }
- }
- func parseUint(t *testing.T, s string) int {
- val, err := strconv.ParseUint(s, 10, 32)
- if err != nil {
- t.Fatal(err)
- }
- return int(val)
- }
- func parseFixedPoint(t *testing.T, s string) (val, scale int) {
- p := strings.Index(s, ".")
- s = strings.Replace(s, ".", "", 1)
- v, err := strconv.ParseUint(s, 10, 32)
- if err != nil {
- t.Fatal(err)
- }
- return int(v), len(s) - p
- }
- func BenchmarkPluralSimpleCases(b *testing.B) {
- p := Cardinal
- en := tagToID(language.English)
- zh := tagToID(language.Chinese)
- for i := 0; i < b.N; i++ {
- matchPlural(p, en, 0, 0, 0) // 0
- matchPlural(p, en, 1, 0, 0) // 1
- matchPlural(p, en, 2, 12, 3) // 2.120
- matchPlural(p, zh, 0, 0, 0) // 0
- matchPlural(p, zh, 1, 0, 0) // 1
- matchPlural(p, zh, 2, 12, 3) // 2.120
- }
- }
- func BenchmarkPluralComplexCases(b *testing.B) {
- p := Cardinal
- ar := tagToID(language.Arabic)
- lv := tagToID(language.Latvian)
- for i := 0; i < b.N; i++ {
- matchPlural(p, lv, 0, 19, 2) // 0.19
- matchPlural(p, lv, 11, 0, 3) // 11.000
- matchPlural(p, lv, 100, 123, 4) // 0.1230
- matchPlural(p, ar, 0, 0, 0) // 0
- matchPlural(p, ar, 110, 0, 0) // 110
- matchPlural(p, ar, 99, 99, 2) // 99.99
- }
- }
|