...

Source file src/golang.org/x/text/feature/plural/plural_test.go

Documentation: golang.org/x/text/feature/plural

     1  // Copyright 2016 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  
     5  package plural
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  
    14  	"golang.org/x/text/language"
    15  )
    16  
    17  func TestGetIntApprox(t *testing.T) {
    18  	const big = 1234567890
    19  	testCases := []struct {
    20  		digits string
    21  		start  int
    22  		end    int
    23  		nMod   int
    24  		want   int
    25  	}{
    26  		{"123", 0, 1, 1, 1},
    27  		{"123", 0, 2, 1, big},
    28  		{"123", 0, 2, 2, 12},
    29  		{"123", 3, 4, 2, 0},
    30  		{"12345", 3, 4, 2, 4},
    31  		{"40", 0, 1, 2, 4},
    32  		{"1", 0, 7, 2, big},
    33  
    34  		{"123", 0, 5, 2, big},
    35  		{"123", 0, 5, 3, big},
    36  		{"123", 0, 5, 4, big},
    37  		{"123", 0, 5, 5, 12300},
    38  		{"123", 0, 5, 6, 12300},
    39  		{"123", 0, 5, 7, 12300},
    40  
    41  		// Translation of examples in MatchDigits.
    42  		// Integer parts
    43  		{"123", 0, 3, 3, 123},  // 123
    44  		{"1234", 0, 3, 3, 123}, // 123.4
    45  		{"1", 0, 6, 8, 100000}, // 100000
    46  
    47  		// Fraction parts
    48  		{"123", 3, 3, 3, 0},   // 123
    49  		{"1234", 3, 4, 3, 4},  // 123.4
    50  		{"1234", 3, 5, 3, 40}, // 123.40
    51  		{"1", 6, 8, 8, 0},     // 100000.00
    52  	}
    53  	for _, tc := range testCases {
    54  		t.Run(fmt.Sprintf("%s:%d:%d/%d", tc.digits, tc.start, tc.end, tc.nMod), func(t *testing.T) {
    55  			got := getIntApprox(mkDigits(tc.digits), tc.start, tc.end, tc.nMod, big)
    56  			if got != tc.want {
    57  				t.Errorf("got %d; want %d", got, tc.want)
    58  			}
    59  		})
    60  	}
    61  }
    62  
    63  func mkDigits(s string) []byte {
    64  	b := []byte(s)
    65  	for i := range b {
    66  		b[i] -= '0'
    67  	}
    68  	return b
    69  }
    70  
    71  func TestValidForms(t *testing.T) {
    72  	testCases := []struct {
    73  		tag  language.Tag
    74  		want []Form
    75  	}{
    76  		{language.AmericanEnglish, []Form{Other, One}},
    77  		{language.Portuguese, []Form{Other, One}},
    78  		{language.Latvian, []Form{Other, Zero, One}},
    79  		{language.Arabic, []Form{Other, Zero, One, Two, Few, Many}},
    80  		{language.Russian, []Form{Other, One, Few, Many}},
    81  	}
    82  	for _, tc := range testCases {
    83  		got := validForms(cardinal, tc.tag)
    84  		if !reflect.DeepEqual(got, tc.want) {
    85  			t.Errorf("validForms(%v): got %v; want %v", tc.tag, got, tc.want)
    86  		}
    87  	}
    88  }
    89  
    90  func TestOrdinal(t *testing.T) {
    91  	testPlurals(t, Ordinal, ordinalTests)
    92  }
    93  
    94  func TestCardinal(t *testing.T) {
    95  	testPlurals(t, Cardinal, cardinalTests)
    96  }
    97  
    98  func testPlurals(t *testing.T, p *Rules, testCases []pluralTest) {
    99  	for _, tc := range testCases {
   100  		for _, loc := range strings.Split(tc.locales, " ") {
   101  			tag := language.MustParse(loc)
   102  			// Test integers
   103  			for _, s := range tc.integer {
   104  				a := strings.Split(s, "~")
   105  				from := parseUint(t, a[0])
   106  				to := from
   107  				if len(a) > 1 {
   108  					to = parseUint(t, a[1])
   109  				}
   110  				for n := from; n <= to; n++ {
   111  					t.Run(fmt.Sprintf("%s/int(%d)", loc, n), func(t *testing.T) {
   112  						if f := p.matchComponents(tag, n, 0, 0); f != Form(tc.form) {
   113  							t.Errorf("matchComponents: got %v; want %v", f, Form(tc.form))
   114  						}
   115  						digits := []byte(fmt.Sprint(n))
   116  						for i := range digits {
   117  							digits[i] -= '0'
   118  						}
   119  						if f := p.MatchDigits(tag, digits, len(digits), 0); f != Form(tc.form) {
   120  							t.Errorf("MatchDigits: got %v; want %v", f, Form(tc.form))
   121  						}
   122  					})
   123  				}
   124  			}
   125  			// Test decimals
   126  			for _, s := range tc.decimal {
   127  				a := strings.Split(s, "~")
   128  				from, scale := parseFixedPoint(t, a[0])
   129  				to := from
   130  				if len(a) > 1 {
   131  					var toScale int
   132  					if to, toScale = parseFixedPoint(t, a[1]); toScale != scale {
   133  						t.Fatalf("%s:%s: non-matching scales %d versus %d", loc, s, scale, toScale)
   134  					}
   135  				}
   136  				m := 1
   137  				for i := 0; i < scale; i++ {
   138  					m *= 10
   139  				}
   140  				for n := from; n <= to; n++ {
   141  					num := fmt.Sprintf("%[1]d.%0[3]*[2]d", n/m, n%m, scale)
   142  					name := fmt.Sprintf("%s:dec(%s)", loc, num)
   143  					t.Run(name, func(t *testing.T) {
   144  						ff := n % m
   145  						tt := ff
   146  						w := scale
   147  						for tt > 0 && tt%10 == 0 {
   148  							w--
   149  							tt /= 10
   150  						}
   151  						if f := p.MatchPlural(tag, n/m, scale, w, ff, tt); f != Form(tc.form) {
   152  							t.Errorf("MatchPlural: got %v; want %v", f, Form(tc.form))
   153  						}
   154  						if f := p.matchComponents(tag, n/m, n%m, scale); f != Form(tc.form) {
   155  							t.Errorf("matchComponents: got %v; want %v", f, Form(tc.form))
   156  						}
   157  						exp := strings.IndexByte(num, '.')
   158  						digits := []byte(strings.Replace(num, ".", "", 1))
   159  						for i := range digits {
   160  							digits[i] -= '0'
   161  						}
   162  						if f := p.MatchDigits(tag, digits, exp, scale); f != Form(tc.form) {
   163  							t.Errorf("MatchDigits: got %v; want %v", f, Form(tc.form))
   164  						}
   165  					})
   166  				}
   167  			}
   168  		}
   169  	}
   170  }
   171  
   172  func parseUint(t *testing.T, s string) int {
   173  	val, err := strconv.ParseUint(s, 10, 32)
   174  	if err != nil {
   175  		t.Fatal(err)
   176  	}
   177  	return int(val)
   178  }
   179  
   180  func parseFixedPoint(t *testing.T, s string) (val, scale int) {
   181  	p := strings.Index(s, ".")
   182  	s = strings.Replace(s, ".", "", 1)
   183  	v, err := strconv.ParseUint(s, 10, 32)
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	return int(v), len(s) - p
   188  }
   189  
   190  func BenchmarkPluralSimpleCases(b *testing.B) {
   191  	p := Cardinal
   192  	en := tagToID(language.English)
   193  	zh := tagToID(language.Chinese)
   194  	for i := 0; i < b.N; i++ {
   195  		matchPlural(p, en, 0, 0, 0)  // 0
   196  		matchPlural(p, en, 1, 0, 0)  // 1
   197  		matchPlural(p, en, 2, 12, 3) // 2.120
   198  		matchPlural(p, zh, 0, 0, 0)  // 0
   199  		matchPlural(p, zh, 1, 0, 0)  // 1
   200  		matchPlural(p, zh, 2, 12, 3) // 2.120
   201  	}
   202  }
   203  
   204  func BenchmarkPluralComplexCases(b *testing.B) {
   205  	p := Cardinal
   206  	ar := tagToID(language.Arabic)
   207  	lv := tagToID(language.Latvian)
   208  	for i := 0; i < b.N; i++ {
   209  		matchPlural(p, lv, 0, 19, 2)    // 0.19
   210  		matchPlural(p, lv, 11, 0, 3)    // 11.000
   211  		matchPlural(p, lv, 100, 123, 4) // 0.1230
   212  		matchPlural(p, ar, 0, 0, 0)     // 0
   213  		matchPlural(p, ar, 110, 0, 0)   // 110
   214  		matchPlural(p, ar, 99, 99, 2)   // 99.99
   215  	}
   216  }
   217  

View as plain text