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.
12 "golang.org/x/text/language"
15 type renderer struct {
20 func (r *renderer) Arg(i int) interface{} {
27 func (r *renderer) Render(s string) {
34 func TestCodec(t *testing.T) {
40 single := func(out, err string) []test { return []test{{out: out, decErr: err}} }
41 testCases := []struct {
48 desc: "unused variable",
49 m: &Var{"name", String("foo")},
50 encErr: errIsVar.Error(),
51 tests: single("", ""),
55 tests: single("", ""),
57 desc: "sequence with empty",
59 tests: single("", ""),
63 tests: single("foo", ""),
65 desc: "raw string no sub",
68 tests: single("${foo}", ""),
70 desc: "simple string",
72 tests: single("foo", ""),
75 m: String("foo${bar}"),
76 enc: "\x03\x03foo\x02\x03bar",
77 encErr: `unknown var "bar"`,
78 tests: single("foo|bar", ""),
85 enc: "\x00\x05\x04\x02bar\x03\x03foo\x00\x00",
86 // TODO: recognize that it is cheaper to substitute bar.
87 tests: single("foo|bar", ""),
89 desc: "var after value",
92 &Var{"bar", String("baz")},
94 encErr: errIsVar.Error(),
95 tests: single("foo|bar", ""),
99 &Var{"bar", String("baz")},
102 tests: single("foo|baz", ""),
104 desc: "shadowed variable",
106 &Var{"bar", String("baz")},
108 &Var{"bar", String("BAZ")},
112 tests: single("foo|BAZ", ""),
114 desc: "nested value",
115 m: nestedLang{nestedLang{empty{}}},
116 tests: single("nl|nl", ""),
118 desc: "not shadowed variable",
120 &Var{"bar", String("baz")},
123 &Var{"bar", String("BAZ")},
126 encErr: errIsVar.Error(),
127 tests: single("foo|baz", ""),
129 desc: "duplicate variable",
131 &Var{"bar", String("baz")},
132 &Var{"bar", String("BAZ")},
135 encErr: "catmsg: duplicate variable \"bar\"",
136 tests: single("baz", ""),
138 desc: "complete incomplete variable",
140 &Var{"bar", incomplete{}},
143 enc: "\x00\t\b\x01\x01\x04\x04\x02bar\x03\x00\x00\x00",
144 // TODO: recognize that it is cheaper to substitute bar.
145 tests: single("bar", ""),
147 desc: "incomplete sequence",
152 encErr: ErrIncomplete.Error(),
153 tests: single("", ErrNoMatch.Error()),
155 desc: "compile error variable",
157 &Var{"bar", errorCompileMsg{}},
160 encErr: errCompileTest.Error(),
161 tests: single("bar", ""),
163 desc: "compile error message",
164 m: errorCompileMsg{},
165 encErr: errCompileTest.Error(),
166 tests: single("", ""),
168 desc: "compile error sequence",
173 encErr: errCompileTest.Error(),
174 tests: single("", ""),
177 m: String("${exists(1)}"),
178 tests: single("you betya!", ""),
180 desc: "macro incomplete",
181 m: String("${incomplete(1)}"),
182 enc: "\x03\x00\x01\nincomplete\x01",
183 tests: single("incomplete", ""),
185 desc: "macro undefined at end",
186 m: String("${undefined(1)}"),
187 enc: "\x03\x00\x01\tundefined\x01",
188 tests: single("undefined", "catmsg: undefined macro \"undefined\""),
190 desc: "macro undefined with more text following",
191 m: String("${undefined(1)}."),
192 enc: "\x03\x00\x01\tundefined\x01\x01.",
193 tests: single("undefined|.", "catmsg: undefined macro \"undefined\""),
195 desc: "macro missing paren",
196 m: String("${missing(1}"),
197 encErr: "catmsg: missing ')'",
198 tests: single("$!(MISSINGPAREN)", ""),
200 desc: "macro bad num",
201 m: String("aa${bad(a)}"),
202 encErr: "catmsg: invalid number \"a\"",
203 tests: single("aa$!(BADNUM)", ""),
205 desc: "var missing brace",
206 m: String("a${missing"),
207 encErr: "catmsg: missing '}'",
208 tests: single("a$!(MISSINGBRACE)", ""),
211 dec := NewDecoder(language.Und, r, macros)
212 for _, tc := range testCases {
213 t.Run(tc.desc, func(t *testing.T) {
214 // Use a language other than Und so that we can test
215 // passing the language to nested values.
216 data, err := Compile(language.Dutch, macros, tc.m)
217 if failErr(err, tc.encErr) {
218 t.Errorf("encoding error: got %+q; want %+q", err, tc.encErr)
220 if tc.enc != "" && data != tc.enc {
221 t.Errorf("encoding: got %+q; want %+q", data, tc.enc)
223 for _, st := range tc.tests {
224 t.Run("", func(t *testing.T) {
225 *r = renderer{args: st.args}
226 if err = dec.Execute(data); failErr(err, st.decErr) {
227 t.Errorf("decoding error: got %+q; want %+q", err, st.decErr)
229 if r.result != st.out {
230 t.Errorf("decode: got %+q; want %+q", r.result, st.out)
238 func failErr(got error, want string) bool {
242 return want == "" || !strings.Contains(got.Error(), want)
247 func (s seq) Compile(e *Encoder) (err error) {
249 e.EncodeMessageType(First)
250 for _, m := range s {
251 // Pass only the last error, but allow erroneous or complete messages
252 // here to allow testing different scenarios.
253 err = e.EncodeMessage(m)
260 func (empty) Compile(e *Encoder) (err error) { return nil }
262 var msgIncomplete = Register(
263 "golang.org/x/text/internal/catmsg.incomplete",
264 func(d *Decoder) bool { return false })
266 type incomplete struct{}
268 func (incomplete) Compile(e *Encoder) (err error) {
269 e.EncodeMessageType(msgIncomplete)
273 var msgNested = Register(
274 "golang.org/x/text/internal/catmsg.nested",
275 func(d *Decoder) bool {
276 d.Render(d.DecodeString())
281 type nestedLang struct{ Message }
283 func (n nestedLang) Compile(e *Encoder) (err error) {
284 e.EncodeMessageType(msgNested)
285 e.EncodeString(e.Language().String())
286 e.EncodeMessage(n.Message)
290 type errorCompileMsg struct{}
292 var errCompileTest = errors.New("catmsg: compile error test")
294 func (errorCompileMsg) Compile(e *Encoder) (err error) {
295 return errCompileTest
298 type dictionary struct{}
301 macros = dictionary{}
302 dictMessages = map[string]string{
303 "exists": compile(String("you betya!")),
304 "incomplete": compile(incomplete{}),
308 func (d dictionary) Lookup(key string) (data string, ok bool) {
309 data, ok = dictMessages[key]
313 func compile(m Message) (data string) {
314 data, _ = Compile(language.Und, macros, m)