message_test.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright 2017 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. "strings"
  8. "testing"
  9. "golang.org/x/text/internal/catmsg"
  10. "golang.org/x/text/language"
  11. "golang.org/x/text/message/catalog"
  12. )
  13. func TestSelect(t *testing.T) {
  14. lang := language.English
  15. type test struct {
  16. arg interface{}
  17. result string
  18. err string
  19. }
  20. testCases := []struct {
  21. desc string
  22. msg catalog.Message
  23. err string
  24. tests []test
  25. }{{
  26. desc: "basic",
  27. msg: Selectf(1, "%d", "one", "foo", "other", "bar"),
  28. tests: []test{
  29. {arg: 0, result: "bar"},
  30. {arg: 1, result: "foo"},
  31. {arg: 2, result: "bar"},
  32. {arg: opposite(1), result: "bar"},
  33. {arg: opposite(2), result: "foo"},
  34. {arg: "unknown", result: "bar"}, // other
  35. },
  36. }, {
  37. desc: "comparisons",
  38. msg: Selectf(1, "%d",
  39. "=0", "zero",
  40. "=1", "one",
  41. "one", "cannot match", // never matches
  42. "<5", "<5", // never matches
  43. "=5", "=5",
  44. Other, "other"),
  45. tests: []test{
  46. {arg: 0, result: "zero"},
  47. {arg: 1, result: "one"},
  48. {arg: 2, result: "<5"},
  49. {arg: 4, result: "<5"},
  50. {arg: 5, result: "=5"},
  51. {arg: 6, result: "other"},
  52. {arg: "unknown", result: "other"},
  53. },
  54. }, {
  55. desc: "fractions",
  56. msg: Selectf(1, "%.2f", "one", "foo", "other", "bar"),
  57. tests: []test{
  58. // fractions are always plural in english
  59. {arg: 0, result: "bar"},
  60. {arg: 1, result: "bar"},
  61. },
  62. }, {
  63. desc: "decimal without fractions",
  64. msg: Selectf(1, "%.0f", "one", "foo", "other", "bar"),
  65. tests: []test{
  66. // fractions are always plural in english
  67. {arg: 0, result: "bar"},
  68. {arg: 1, result: "foo"},
  69. },
  70. }, {
  71. desc: "scientific",
  72. msg: Selectf(1, "%.0e", "one", "foo", "other", "bar"),
  73. tests: []test{
  74. {arg: 0, result: "bar"},
  75. {arg: 1, result: "foo"},
  76. },
  77. }, {
  78. desc: "variable",
  79. msg: Selectf(1, "%.1g", "one", "foo", "other", "bar"),
  80. tests: []test{
  81. // fractions are always plural in english
  82. {arg: 0, result: "bar"},
  83. {arg: 1, result: "foo"},
  84. {arg: 2, result: "bar"},
  85. },
  86. }, {
  87. desc: "default",
  88. msg: Selectf(1, "", "one", "foo", "other", "bar"),
  89. tests: []test{
  90. {arg: 0, result: "bar"},
  91. {arg: 1, result: "foo"},
  92. {arg: 2, result: "bar"},
  93. {arg: 1.0, result: "bar"},
  94. },
  95. }, {
  96. desc: "nested",
  97. msg: Selectf(1, "", "other", Selectf(2, "", "one", "foo", "other", "bar")),
  98. tests: []test{
  99. {arg: 0, result: "bar"},
  100. {arg: 1, result: "foo"},
  101. {arg: 2, result: "bar"},
  102. },
  103. }, {
  104. desc: "arg unavailable",
  105. msg: Selectf(100, "%.2f", "one", "foo", "other", "bar"),
  106. tests: []test{{arg: 1, result: "bar"}},
  107. }, {
  108. desc: "no match",
  109. msg: Selectf(1, "%.2f", "one", "foo"),
  110. tests: []test{{arg: 0, result: "bar", err: catmsg.ErrNoMatch.Error()}},
  111. }, {
  112. desc: "error invalid form",
  113. err: `invalid plural form "excessive"`,
  114. msg: Selectf(1, "%d", "excessive", "foo"),
  115. }, {
  116. desc: "error form not used by language",
  117. err: `form "many" not supported for language "en"`,
  118. msg: Selectf(1, "%d", "many", "foo"),
  119. }, {
  120. desc: "error invalid selector",
  121. err: `selector of type int; want string or Form`,
  122. msg: Selectf(1, "%d", 1, "foo"),
  123. }, {
  124. desc: "error missing message",
  125. err: `no message defined for selector one`,
  126. msg: Selectf(1, "%d", "one"),
  127. }, {
  128. desc: "error invalid number",
  129. err: `invalid number in selector "<1.00"`,
  130. msg: Selectf(1, "%d", "<1.00"),
  131. }, {
  132. desc: "error empty selector",
  133. err: `empty selector`,
  134. msg: Selectf(1, "%d", "", "foo"),
  135. }, {
  136. desc: "error invalid message",
  137. err: `message of type int; must be string or catalog.Message`,
  138. msg: Selectf(1, "%d", "one", 3),
  139. }, {
  140. desc: "nested error",
  141. err: `empty selector`,
  142. msg: Selectf(1, "", "other", Selectf(2, "", "")),
  143. }}
  144. for _, tc := range testCases {
  145. t.Run(tc.desc, func(t *testing.T) {
  146. data, err := catmsg.Compile(lang, nil, tc.msg)
  147. chkError(t, err, tc.err)
  148. for _, tx := range tc.tests {
  149. t.Run(fmt.Sprint(tx.arg), func(t *testing.T) {
  150. r := renderer{arg: tx.arg}
  151. d := catmsg.NewDecoder(lang, &r, nil)
  152. err := d.Execute(data)
  153. chkError(t, err, tx.err)
  154. if r.result != tx.result {
  155. t.Errorf("got %q; want %q", r.result, tx.result)
  156. }
  157. })
  158. }
  159. })
  160. }
  161. }
  162. func chkError(t *testing.T, got error, want string) {
  163. if (got == nil && want != "") ||
  164. (got != nil && (want == "" || !strings.Contains(got.Error(), want))) {
  165. t.Fatalf("got %v; want %v", got, want)
  166. }
  167. if got != nil {
  168. t.SkipNow()
  169. }
  170. }
  171. type renderer struct {
  172. arg interface{}
  173. result string
  174. }
  175. func (r *renderer) Render(s string) { r.result += s }
  176. func (r *renderer) Arg(i int) interface{} {
  177. if i > 10 { // Allow testing "arg unavailable" path
  178. return nil
  179. }
  180. return r.arg
  181. }
  182. type opposite int
  183. func (o opposite) PluralForm(lang language.Tag, scale int) (Form, int) {
  184. if o == 1 {
  185. return Other, 1
  186. }
  187. return One, int(o)
  188. }