github.com/go-xe2/third@v1.0.3/golang.org/x/text/internal/catmsg/catmsg_test.go (about) 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 5 package catmsg 6 7 import ( 8 "errors" 9 "strings" 10 "testing" 11 12 "github.com/go-xe2/third/golang.org/x/text/language" 13 ) 14 15 type renderer struct { 16 args []int 17 result string 18 } 19 20 func (r *renderer) Arg(i int) interface{} { 21 if i >= len(r.args) { 22 return nil 23 } 24 return r.args[i] 25 } 26 27 func (r *renderer) Render(s string) { 28 if r.result != "" { 29 r.result += "|" 30 } 31 r.result += s 32 } 33 34 func TestCodec(t *testing.T) { 35 type test struct { 36 args []int 37 out string 38 decErr string 39 } 40 single := func(out, err string) []test { return []test{{out: out, decErr: err}} } 41 testCases := []struct { 42 desc string 43 m Message 44 enc string 45 encErr string 46 tests []test 47 }{{ 48 desc: "unused variable", 49 m: &Var{"name", String("foo")}, 50 encErr: errIsVar.Error(), 51 tests: single("", ""), 52 }, { 53 desc: "empty", 54 m: empty{}, 55 tests: single("", ""), 56 }, { 57 desc: "sequence with empty", 58 m: seq{empty{}}, 59 tests: single("", ""), 60 }, { 61 desc: "raw string", 62 m: Raw("foo"), 63 tests: single("foo", ""), 64 }, { 65 desc: "raw string no sub", 66 m: Raw("${foo}"), 67 enc: "\x02${foo}", 68 tests: single("${foo}", ""), 69 }, { 70 desc: "simple string", 71 m: String("foo"), 72 tests: single("foo", ""), 73 }, { 74 desc: "missing var", 75 m: String("foo${bar}"), 76 enc: "\x03\x03foo\x02\x03bar", 77 encErr: `unknown var "bar"`, 78 tests: single("foo|bar", ""), 79 }, { 80 desc: "empty var", 81 m: seq{ 82 &Var{"bar", seq{}}, 83 String("foo${bar}"), 84 }, 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", ""), 88 }, { 89 desc: "var after value", 90 m: seq{ 91 String("foo${bar}"), 92 &Var{"bar", String("baz")}, 93 }, 94 encErr: errIsVar.Error(), 95 tests: single("foo|bar", ""), 96 }, { 97 desc: "substitution", 98 m: seq{ 99 &Var{"bar", String("baz")}, 100 String("foo${bar}"), 101 }, 102 tests: single("foo|baz", ""), 103 }, { 104 desc: "shadowed variable", 105 m: seq{ 106 &Var{"bar", String("baz")}, 107 seq{ 108 &Var{"bar", String("BAZ")}, 109 String("foo${bar}"), 110 }, 111 }, 112 tests: single("foo|BAZ", ""), 113 }, { 114 desc: "nested value", 115 m: nestedLang{nestedLang{empty{}}}, 116 tests: single("nl|nl", ""), 117 }, { 118 desc: "not shadowed variable", 119 m: seq{ 120 &Var{"bar", String("baz")}, 121 seq{ 122 String("foo${bar}"), 123 &Var{"bar", String("BAZ")}, 124 }, 125 }, 126 encErr: errIsVar.Error(), 127 tests: single("foo|baz", ""), 128 }, { 129 desc: "duplicate variable", 130 m: seq{ 131 &Var{"bar", String("baz")}, 132 &Var{"bar", String("BAZ")}, 133 String("${bar}"), 134 }, 135 encErr: "catmsg: duplicate variable \"bar\"", 136 tests: single("baz", ""), 137 }, { 138 desc: "complete incomplete variable", 139 m: seq{ 140 &Var{"bar", incomplete{}}, 141 String("${bar}"), 142 }, 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", ""), 146 }, { 147 desc: "incomplete sequence", 148 m: seq{ 149 incomplete{}, 150 incomplete{}, 151 }, 152 encErr: ErrIncomplete.Error(), 153 tests: single("", ErrNoMatch.Error()), 154 }, { 155 desc: "compile error variable", 156 m: seq{ 157 &Var{"bar", errorCompileMsg{}}, 158 String("${bar}"), 159 }, 160 encErr: errCompileTest.Error(), 161 tests: single("bar", ""), 162 }, { 163 desc: "compile error message", 164 m: errorCompileMsg{}, 165 encErr: errCompileTest.Error(), 166 tests: single("", ""), 167 }, { 168 desc: "compile error sequence", 169 m: seq{ 170 errorCompileMsg{}, 171 errorCompileMsg{}, 172 }, 173 encErr: errCompileTest.Error(), 174 tests: single("", ""), 175 }, { 176 desc: "macro", 177 m: String("${exists(1)}"), 178 tests: single("you betya!", ""), 179 }, { 180 desc: "macro incomplete", 181 m: String("${incomplete(1)}"), 182 enc: "\x03\x00\x01\nincomplete\x01", 183 tests: single("incomplete", ""), 184 }, { 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\""), 189 }, { 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\""), 194 }, { 195 desc: "macro missing paren", 196 m: String("${missing(1}"), 197 encErr: "catmsg: missing ')'", 198 tests: single("$!(MISSINGPAREN)", ""), 199 }, { 200 desc: "macro bad num", 201 m: String("aa${bad(a)}"), 202 encErr: "catmsg: invalid number \"a\"", 203 tests: single("aa$!(BADNUM)", ""), 204 }, { 205 desc: "var missing brace", 206 m: String("a${missing"), 207 encErr: "catmsg: missing '}'", 208 tests: single("a$!(MISSINGBRACE)", ""), 209 }} 210 r := &renderer{} 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) 219 } 220 if tc.enc != "" && data != tc.enc { 221 t.Errorf("encoding: got %+q; want %+q", data, tc.enc) 222 } 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) 228 } 229 if r.result != st.out { 230 t.Errorf("decode: got %+q; want %+q", r.result, st.out) 231 } 232 }) 233 } 234 }) 235 } 236 } 237 238 func failErr(got error, want string) bool { 239 if got == nil { 240 return want != "" 241 } 242 return want == "" || !strings.Contains(got.Error(), want) 243 } 244 245 type seq []Message 246 247 func (s seq) Compile(e *Encoder) (err error) { 248 err = ErrIncomplete 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) 254 } 255 return err 256 } 257 258 type empty struct{} 259 260 func (empty) Compile(e *Encoder) (err error) { return nil } 261 262 var msgIncomplete = Register( 263 "github.com/go-xe2/third/golang.org/x/text/internal/catmsg.incomplete", 264 func(d *Decoder) bool { return false }) 265 266 type incomplete struct{} 267 268 func (incomplete) Compile(e *Encoder) (err error) { 269 e.EncodeMessageType(msgIncomplete) 270 return ErrIncomplete 271 } 272 273 var msgNested = Register( 274 "github.com/go-xe2/third/golang.org/x/text/internal/catmsg.nested", 275 func(d *Decoder) bool { 276 d.Render(d.DecodeString()) 277 d.ExecuteMessage() 278 return true 279 }) 280 281 type nestedLang struct{ Message } 282 283 func (n nestedLang) Compile(e *Encoder) (err error) { 284 e.EncodeMessageType(msgNested) 285 e.EncodeString(e.Language().String()) 286 e.EncodeMessage(n.Message) 287 return nil 288 } 289 290 type errorCompileMsg struct{} 291 292 var errCompileTest = errors.New("catmsg: compile error test") 293 294 func (errorCompileMsg) Compile(e *Encoder) (err error) { 295 return errCompileTest 296 } 297 298 type dictionary struct{} 299 300 var ( 301 macros = dictionary{} 302 dictMessages = map[string]string{ 303 "exists": compile(String("you betya!")), 304 "incomplete": compile(incomplete{}), 305 } 306 ) 307 308 func (d dictionary) Lookup(key string) (data string, ok bool) { 309 data, ok = dictMessages[key] 310 return 311 } 312 313 func compile(m Message) (data string) { 314 data, _ = Compile(language.Und, macros, m) 315 return data 316 }