| // Copyright 2017 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" |
| "strings" |
| "testing" |
| |
| "golang.org/x/text/internal/catmsg" |
| "golang.org/x/text/language" |
| "golang.org/x/text/message/catalog" |
| ) |
| |
| func TestSelect(t *testing.T) { |
| lang := language.English |
| type test struct { |
| arg interface{} |
| result string |
| err string |
| } |
| testCases := []struct { |
| desc string |
| msg catalog.Message |
| err string |
| tests []test |
| }{{ |
| desc: "basic", |
| msg: Selectf(1, "%d", "one", "foo", "other", "bar"), |
| tests: []test{ |
| {arg: 0, result: "bar"}, |
| {arg: 1, result: "foo"}, |
| {arg: 2, result: "bar"}, |
| {arg: opposite(1), result: "bar"}, |
| {arg: opposite(2), result: "foo"}, |
| {arg: "unknown", result: "bar"}, // other |
| }, |
| }, { |
| desc: "comparisons", |
| msg: Selectf(1, "%d", |
| "=0", "zero", |
| "=1", "one", |
| "one", "cannot match", // never matches |
| "<5", "<5", // never matches |
| "=5", "=5", |
| Other, "other"), |
| tests: []test{ |
| {arg: 0, result: "zero"}, |
| {arg: 1, result: "one"}, |
| {arg: 2, result: "<5"}, |
| {arg: 4, result: "<5"}, |
| {arg: 5, result: "=5"}, |
| {arg: 6, result: "other"}, |
| {arg: "unknown", result: "other"}, |
| }, |
| }, { |
| desc: "fractions", |
| msg: Selectf(1, "%.2f", "one", "foo", "other", "bar"), |
| tests: []test{ |
| // fractions are always plural in english |
| {arg: 0, result: "bar"}, |
| {arg: 1, result: "bar"}, |
| }, |
| }, { |
| desc: "decimal without fractions", |
| msg: Selectf(1, "%.0f", "one", "foo", "other", "bar"), |
| tests: []test{ |
| // fractions are always plural in english |
| {arg: 0, result: "bar"}, |
| {arg: 1, result: "foo"}, |
| }, |
| }, { |
| desc: "scientific", |
| msg: Selectf(1, "%.0e", "one", "foo", "other", "bar"), |
| tests: []test{ |
| {arg: 0, result: "bar"}, |
| {arg: 1, result: "foo"}, |
| }, |
| }, { |
| desc: "variable", |
| msg: Selectf(1, "%.1g", "one", "foo", "other", "bar"), |
| tests: []test{ |
| // fractions are always plural in english |
| {arg: 0, result: "bar"}, |
| {arg: 1, result: "foo"}, |
| {arg: 2, result: "bar"}, |
| }, |
| }, { |
| desc: "default", |
| msg: Selectf(1, "", "one", "foo", "other", "bar"), |
| tests: []test{ |
| {arg: 0, result: "bar"}, |
| {arg: 1, result: "foo"}, |
| {arg: 2, result: "bar"}, |
| {arg: 1.0, result: "bar"}, |
| }, |
| }, { |
| desc: "nested", |
| msg: Selectf(1, "", "other", Selectf(2, "", "one", "foo", "other", "bar")), |
| tests: []test{ |
| {arg: 0, result: "bar"}, |
| {arg: 1, result: "foo"}, |
| {arg: 2, result: "bar"}, |
| }, |
| }, { |
| desc: "arg unavailable", |
| msg: Selectf(100, "%.2f", "one", "foo", "other", "bar"), |
| tests: []test{{arg: 1, result: "bar"}}, |
| }, { |
| desc: "no match", |
| msg: Selectf(1, "%.2f", "one", "foo"), |
| tests: []test{{arg: 0, result: "bar", err: catmsg.ErrNoMatch.Error()}}, |
| }, { |
| desc: "error invalid form", |
| err: `invalid plural form "excessive"`, |
| msg: Selectf(1, "%d", "excessive", "foo"), |
| }, { |
| desc: "error form not used by language", |
| err: `form "many" not supported for language "en"`, |
| msg: Selectf(1, "%d", "many", "foo"), |
| }, { |
| desc: "error invalid selector", |
| err: `selector of type int; want string or Form`, |
| msg: Selectf(1, "%d", 1, "foo"), |
| }, { |
| desc: "error missing message", |
| err: `no message defined for selector one`, |
| msg: Selectf(1, "%d", "one"), |
| }, { |
| desc: "error invalid number", |
| err: `invalid number in selector "<1.00"`, |
| msg: Selectf(1, "%d", "<1.00"), |
| }, { |
| desc: "error empty selector", |
| err: `empty selector`, |
| msg: Selectf(1, "%d", "", "foo"), |
| }, { |
| desc: "error invalid message", |
| err: `message of type int; must be string or catalog.Message`, |
| msg: Selectf(1, "%d", "one", 3), |
| }, { |
| desc: "nested error", |
| err: `empty selector`, |
| msg: Selectf(1, "", "other", Selectf(2, "", "")), |
| }} |
| for _, tc := range testCases { |
| t.Run(tc.desc, func(t *testing.T) { |
| data, err := catmsg.Compile(lang, nil, tc.msg) |
| chkError(t, err, tc.err) |
| for _, tx := range tc.tests { |
| t.Run(fmt.Sprint(tx.arg), func(t *testing.T) { |
| r := renderer{arg: tx.arg} |
| d := catmsg.NewDecoder(lang, &r, nil) |
| err := d.Execute(data) |
| chkError(t, err, tx.err) |
| if r.result != tx.result { |
| t.Errorf("got %q; want %q", r.result, tx.result) |
| } |
| }) |
| } |
| }) |
| } |
| } |
| |
| func chkError(t *testing.T, got error, want string) { |
| if (got == nil && want != "") || |
| (got != nil && (want == "" || !strings.Contains(got.Error(), want))) { |
| t.Fatalf("got %v; want %v", got, want) |
| } |
| if got != nil { |
| t.SkipNow() |
| } |
| } |
| |
| type renderer struct { |
| arg interface{} |
| result string |
| } |
| |
| func (r *renderer) Render(s string) { r.result += s } |
| func (r *renderer) Arg(i int) interface{} { |
| if i > 10 { // Allow testing "arg unavailable" path |
| return nil |
| } |
| return r.arg |
| } |
| |
| type opposite int |
| |
| func (o opposite) PluralForm(lang language.Tag, scale int) (Form, int) { |
| if o == 1 { |
| return Other, 1 |
| } |
| return One, int(o) |
| } |