// 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) }