plural_test.go 5.5 KB

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