|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- // 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 catmsg
-
- import (
- "errors"
- "strings"
- "testing"
-
- "golang.org/x/text/language"
- )
-
- type renderer struct {
- args []int
- result string
- }
-
- func (r *renderer) Arg(i int) interface{} {
- if i >= len(r.args) {
- return nil
- }
- return r.args[i]
- }
-
- func (r *renderer) Render(s string) {
- if r.result != "" {
- r.result += "|"
- }
- r.result += s
- }
-
- func TestCodec(t *testing.T) {
- type test struct {
- args []int
- out string
- decErr string
- }
- single := func(out, err string) []test { return []test{{out: out, decErr: err}} }
- testCases := []struct {
- desc string
- m Message
- enc string
- encErr string
- tests []test
- }{{
- desc: "unused variable",
- m: &Var{"name", String("foo")},
- encErr: errIsVar.Error(),
- tests: single("", ""),
- }, {
- desc: "empty",
- m: empty{},
- tests: single("", ""),
- }, {
- desc: "sequence with empty",
- m: seq{empty{}},
- tests: single("", ""),
- }, {
- desc: "raw string",
- m: Raw("foo"),
- tests: single("foo", ""),
- }, {
- desc: "raw string no sub",
- m: Raw("${foo}"),
- enc: "\x02${foo}",
- tests: single("${foo}", ""),
- }, {
- desc: "simple string",
- m: String("foo"),
- tests: single("foo", ""),
- }, {
- desc: "affix",
- m: &Affix{String("foo"), "\t", "\n"},
- tests: single("\t|foo|\n", ""),
- }, {
- desc: "missing var",
- m: String("foo${bar}"),
- enc: "\x03\x03foo\x02\x03bar",
- encErr: `unknown var "bar"`,
- tests: single("foo|bar", ""),
- }, {
- desc: "empty var",
- m: seq{
- &Var{"bar", seq{}},
- String("foo${bar}"),
- },
- enc: "\x00\x05\x04\x02bar\x03\x03foo\x00\x00",
- // TODO: recognize that it is cheaper to substitute bar.
- tests: single("foo|bar", ""),
- }, {
- desc: "var after value",
- m: seq{
- String("foo${bar}"),
- &Var{"bar", String("baz")},
- },
- encErr: errIsVar.Error(),
- tests: single("foo|bar", ""),
- }, {
- desc: "substitution",
- m: seq{
- &Var{"bar", String("baz")},
- String("foo${bar}"),
- },
- tests: single("foo|baz", ""),
- }, {
- desc: "affix with substitution",
- m: &Affix{seq{
- &Var{"bar", String("baz")},
- String("foo${bar}"),
- }, "\t", "\n"},
- tests: single("\t|foo|baz|\n", ""),
- }, {
- desc: "shadowed variable",
- m: seq{
- &Var{"bar", String("baz")},
- seq{
- &Var{"bar", String("BAZ")},
- String("foo${bar}"),
- },
- },
- tests: single("foo|BAZ", ""),
- }, {
- desc: "nested value",
- m: nestedLang{nestedLang{empty{}}},
- tests: single("nl|nl", ""),
- }, {
- desc: "not shadowed variable",
- m: seq{
- &Var{"bar", String("baz")},
- seq{
- String("foo${bar}"),
- &Var{"bar", String("BAZ")},
- },
- },
- encErr: errIsVar.Error(),
- tests: single("foo|baz", ""),
- }, {
- desc: "duplicate variable",
- m: seq{
- &Var{"bar", String("baz")},
- &Var{"bar", String("BAZ")},
- String("${bar}"),
- },
- encErr: "catmsg: duplicate variable \"bar\"",
- tests: single("baz", ""),
- }, {
- desc: "complete incomplete variable",
- m: seq{
- &Var{"bar", incomplete{}},
- String("${bar}"),
- },
- enc: "\x00\t\b\x01\x01\x14\x04\x02bar\x03\x00\x00\x00",
- // TODO: recognize that it is cheaper to substitute bar.
- tests: single("bar", ""),
- }, {
- desc: "incomplete sequence",
- m: seq{
- incomplete{},
- incomplete{},
- },
- encErr: ErrIncomplete.Error(),
- tests: single("", ErrNoMatch.Error()),
- }, {
- desc: "compile error variable",
- m: seq{
- &Var{"bar", errorCompileMsg{}},
- String("${bar}"),
- },
- encErr: errCompileTest.Error(),
- tests: single("bar", ""),
- }, {
- desc: "compile error message",
- m: errorCompileMsg{},
- encErr: errCompileTest.Error(),
- tests: single("", ""),
- }, {
- desc: "compile error sequence",
- m: seq{
- errorCompileMsg{},
- errorCompileMsg{},
- },
- encErr: errCompileTest.Error(),
- tests: single("", ""),
- }, {
- desc: "macro",
- m: String("${exists(1)}"),
- tests: single("you betya!", ""),
- }, {
- desc: "macro incomplete",
- m: String("${incomplete(1)}"),
- enc: "\x03\x00\x01\nincomplete\x01",
- tests: single("incomplete", ""),
- }, {
- desc: "macro undefined at end",
- m: String("${undefined(1)}"),
- enc: "\x03\x00\x01\tundefined\x01",
- tests: single("undefined", "catmsg: undefined macro \"undefined\""),
- }, {
- desc: "macro undefined with more text following",
- m: String("${undefined(1)}."),
- enc: "\x03\x00\x01\tundefined\x01\x01.",
- tests: single("undefined|.", "catmsg: undefined macro \"undefined\""),
- }, {
- desc: "macro missing paren",
- m: String("${missing(1}"),
- encErr: "catmsg: missing ')'",
- tests: single("$!(MISSINGPAREN)", ""),
- }, {
- desc: "macro bad num",
- m: String("aa${bad(a)}"),
- encErr: "catmsg: invalid number \"a\"",
- tests: single("aa$!(BADNUM)", ""),
- }, {
- desc: "var missing brace",
- m: String("a${missing"),
- encErr: "catmsg: missing '}'",
- tests: single("a$!(MISSINGBRACE)", ""),
- }}
- r := &renderer{}
- dec := NewDecoder(language.Und, r, macros)
- for _, tc := range testCases {
- t.Run(tc.desc, func(t *testing.T) {
- // Use a language other than Und so that we can test
- // passing the language to nested values.
- data, err := Compile(language.Dutch, macros, tc.m)
- if failErr(err, tc.encErr) {
- t.Errorf("encoding error: got %+q; want %+q", err, tc.encErr)
- }
- if tc.enc != "" && data != tc.enc {
- t.Errorf("encoding: got %+q; want %+q", data, tc.enc)
- }
- for _, st := range tc.tests {
- t.Run("", func(t *testing.T) {
- *r = renderer{args: st.args}
- if err = dec.Execute(data); failErr(err, st.decErr) {
- t.Errorf("decoding error: got %+q; want %+q", err, st.decErr)
- }
- if r.result != st.out {
- t.Errorf("decode: got %+q; want %+q", r.result, st.out)
- }
- })
- }
- })
- }
- }
-
- func failErr(got error, want string) bool {
- if got == nil {
- return want != ""
- }
- return want == "" || !strings.Contains(got.Error(), want)
- }
-
- type seq []Message
-
- func (s seq) Compile(e *Encoder) (err error) {
- err = ErrIncomplete
- e.EncodeMessageType(msgFirst)
- for _, m := range s {
- // Pass only the last error, but allow erroneous or complete messages
- // here to allow testing different scenarios.
- err = e.EncodeMessage(m)
- }
- return err
- }
-
- type empty struct{}
-
- func (empty) Compile(e *Encoder) (err error) { return nil }
-
- var msgIncomplete = Register(
- "golang.org/x/text/internal/catmsg.incomplete",
- func(d *Decoder) bool { return false })
-
- type incomplete struct{}
-
- func (incomplete) Compile(e *Encoder) (err error) {
- e.EncodeMessageType(msgIncomplete)
- return ErrIncomplete
- }
-
- var msgNested = Register(
- "golang.org/x/text/internal/catmsg.nested",
- func(d *Decoder) bool {
- d.Render(d.DecodeString())
- d.ExecuteMessage()
- return true
- })
-
- type nestedLang struct{ Message }
-
- func (n nestedLang) Compile(e *Encoder) (err error) {
- e.EncodeMessageType(msgNested)
- e.EncodeString(e.Language().String())
- e.EncodeMessage(n.Message)
- return nil
- }
-
- type errorCompileMsg struct{}
-
- var errCompileTest = errors.New("catmsg: compile error test")
-
- func (errorCompileMsg) Compile(e *Encoder) (err error) {
- return errCompileTest
- }
-
- type dictionary struct{}
-
- var (
- macros = dictionary{}
- dictMessages = map[string]string{
- "exists": compile(String("you betya!")),
- "incomplete": compile(incomplete{}),
- }
- )
-
- func (d dictionary) Lookup(key string) (data string, ok bool) {
- data, ok = dictMessages[key]
- return
- }
-
- func compile(m Message) (data string) {
- data, _ = Compile(language.Und, macros, m)
- return data
- }
|