Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 1 | // Copyright 2018 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 text |
| 6 | |
| 7 | import ( |
| 8 | "fmt" |
| 9 | "math" |
| 10 | "strings" |
| 11 | "testing" |
| 12 | "unicode/utf8" |
| 13 | |
| 14 | "github.com/google/go-cmp/cmp" |
| 15 | "github.com/google/go-cmp/cmp/cmpopts" |
Damien Neil | e89e624 | 2019-05-13 23:55:40 -0700 | [diff] [blame] | 16 | "google.golang.org/protobuf/internal/detrand" |
| 17 | "google.golang.org/protobuf/internal/flags" |
| 18 | "google.golang.org/protobuf/reflect/protoreflect" |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 19 | ) |
| 20 | |
Joe Tsai | 71acbc7 | 2018-11-28 19:27:29 -0800 | [diff] [blame] | 21 | // Disable detrand to enable direct comparisons on outputs. |
| 22 | func init() { detrand.Disable() } |
| 23 | |
Herbie Ong | c3f4d48 | 2018-11-22 14:29:07 -0800 | [diff] [blame] | 24 | var S = fmt.Sprintf |
| 25 | var V = ValueOf |
| 26 | var ID = func(n protoreflect.Name) Value { return V(n) } |
| 27 | |
| 28 | type Lst = []Value |
| 29 | type Msg = [][2]Value |
| 30 | |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 31 | func Test(t *testing.T) { |
| 32 | const space = " \n\r\t" |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 33 | |
| 34 | tests := []struct { |
| 35 | in string |
| 36 | wantVal Value |
| 37 | wantOut string |
| 38 | wantOutBracket string |
| 39 | wantOutASCII string |
| 40 | wantOutIndent string |
| 41 | wantErr string |
| 42 | }{{ |
| 43 | in: "", |
| 44 | wantVal: V(Msg{}), |
Joe Tsai | 3c4ab8c | 2019-09-11 15:31:45 -0700 | [diff] [blame] | 45 | wantOutIndent: "", |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 46 | }, { |
| 47 | in: S("%s# hello%s", space, space), |
| 48 | wantVal: V(Msg{}), |
| 49 | }, { |
| 50 | in: S("%s# hello\rfoo:bar", space), |
| 51 | wantVal: V(Msg{}), |
| 52 | }, { |
| 53 | // Comments only extend until the newline. |
| 54 | in: S("%s# hello\nfoo:bar", space), |
| 55 | wantVal: V(Msg{{ID("foo"), ID("bar")}}), |
| 56 | wantOut: "foo:bar", |
| 57 | wantOutIndent: "foo: bar\n", |
| 58 | }, { |
| 59 | // NUL is an invalid whitespace since C++ uses C-strings. |
| 60 | in: "\x00", |
| 61 | wantErr: `invalid "\x00" as identifier`, |
| 62 | }, { |
| 63 | in: "foo:0", |
| 64 | wantVal: V(Msg{{ID("foo"), V(uint32(0))}}), |
| 65 | wantOut: "foo:0", |
| 66 | }, { |
| 67 | in: S("%sfoo%s:0", space, space), |
| 68 | wantVal: V(Msg{{ID("foo"), V(uint32(0))}}), |
| 69 | }, { |
| 70 | in: "foo bar:0", |
| 71 | wantErr: `expected ':' after message key`, |
| 72 | }, { |
| 73 | in: "[foo]:0", |
| 74 | wantVal: V(Msg{{V("foo"), V(uint32(0))}}), |
| 75 | wantOut: "[foo]:0", |
| 76 | wantOutIndent: "[foo]: 0\n", |
| 77 | }, { |
| 78 | in: S("%s[%sfoo%s]%s:0", space, space, space, space), |
| 79 | wantVal: V(Msg{{V("foo"), V(uint32(0))}}), |
| 80 | }, { |
| 81 | in: "[proto.package.name]:0", |
| 82 | wantVal: V(Msg{{V("proto.package.name"), V(uint32(0))}}), |
| 83 | wantOut: "[proto.package.name]:0", |
| 84 | wantOutIndent: "[proto.package.name]: 0\n", |
| 85 | }, { |
| 86 | in: S("%s[%sproto.package.name%s]%s:0", space, space, space, space), |
| 87 | wantVal: V(Msg{{V("proto.package.name"), V(uint32(0))}}), |
| 88 | }, { |
| 89 | in: "['sub.domain.com\x2fpath\x2fto\x2fproto.package.name']:0", |
| 90 | wantVal: V(Msg{{V("sub.domain.com/path/to/proto.package.name"), V(uint32(0))}}), |
| 91 | wantOut: "[sub.domain.com/path/to/proto.package.name]:0", |
| 92 | wantOutIndent: "[sub.domain.com/path/to/proto.package.name]: 0\n", |
| 93 | }, { |
| 94 | in: "[\"sub.domain.com\x2fpath\x2fto\x2fproto.package.name\"]:0", |
| 95 | wantVal: V(Msg{{V("sub.domain.com/path/to/proto.package.name"), V(uint32(0))}}), |
| 96 | }, { |
| 97 | in: S("%s[%s'sub.domain.com\x2fpath\x2fto\x2fproto.package.name'%s]%s:0", space, space, space, space), |
| 98 | wantVal: V(Msg{{V("sub.domain.com/path/to/proto.package.name"), V(uint32(0))}}), |
| 99 | }, { |
| 100 | in: S("%s[%s\"sub.domain.com\x2fpath\x2fto\x2fproto.package.name\"%s]%s:0", space, space, space, space), |
| 101 | wantVal: V(Msg{{V("sub.domain.com/path/to/proto.package.name"), V(uint32(0))}}), |
| 102 | }, { |
| 103 | in: `['http://example.com/path/to/proto.package.name']:0`, |
| 104 | wantVal: V(Msg{{V("http://example.com/path/to/proto.package.name"), V(uint32(0))}}), |
| 105 | wantOut: `["http://example.com/path/to/proto.package.name"]:0`, |
| 106 | wantOutIndent: `["http://example.com/path/to/proto.package.name"]: 0` + "\n", |
| 107 | }, { |
| 108 | in: "[proto.package.name:0", |
| 109 | wantErr: `invalid character ':', expected ']' at end of extension name`, |
| 110 | }, { |
| 111 | in: "[proto.package name]:0", |
| 112 | wantErr: `invalid character 'n', expected ']' at end of extension name`, |
| 113 | }, { |
| 114 | in: `["proto.package" "name"]:0`, |
| 115 | wantErr: `invalid character '"', expected ']' at end of extension name`, |
| 116 | }, { |
| 117 | in: `["\z"]`, |
| 118 | wantErr: `invalid escape code "\\z" in string`, |
| 119 | }, { |
| 120 | in: "[$]", |
| 121 | wantErr: `invalid "$" as identifier`, |
| 122 | }, { |
Herbie Ong | a3369c5 | 2019-04-23 00:24:46 -0700 | [diff] [blame] | 123 | in: `[proto.package.]:0`, |
| 124 | wantErr: `invalid "proto.package." as identifier`, |
| 125 | }, { |
| 126 | in: `[/proto.package]:0`, |
| 127 | wantErr: `invalid "/proto.package" as identifier`, |
| 128 | }, { |
| 129 | in: `[proto.package/]:0`, |
| 130 | wantErr: `invalid "proto.package/" as identifier`, |
| 131 | }, { |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 132 | // This parses fine, but should result in a error later since no |
| 133 | // type name in proto will ever be just a number. |
| 134 | in: "[20]:0", |
| 135 | wantVal: V(Msg{{V("20"), V(uint32(0))}}), |
| 136 | wantOut: "[20]:0", |
| 137 | }, { |
| 138 | in: "20:0", |
| 139 | wantVal: V(Msg{{V(uint32(20)), V(uint32(0))}}), |
| 140 | wantOut: "20:0", |
| 141 | }, { |
| 142 | in: "0x20:0", |
| 143 | wantVal: V(Msg{{V(uint32(0x20)), V(uint32(0))}}), |
| 144 | wantOut: "32:0", |
| 145 | }, { |
| 146 | in: "020:0", |
| 147 | wantVal: V(Msg{{V(uint32(020)), V(uint32(0))}}), |
| 148 | wantOut: "16:0", |
| 149 | }, { |
| 150 | in: "-20:0", |
| 151 | wantErr: `invalid "-20" as identifier`, |
| 152 | }, { |
| 153 | in: `foo:true bar:"s" baz:{} qux:[] wib:id`, |
| 154 | wantVal: V(Msg{ |
| 155 | {ID("foo"), V(true)}, |
| 156 | {ID("bar"), V("s")}, |
| 157 | {ID("baz"), V(Msg{})}, |
| 158 | {ID("qux"), V(Lst{})}, |
| 159 | {ID("wib"), ID("id")}, |
| 160 | }), |
| 161 | wantOut: `foo:true bar:"s" baz:{} qux:[] wib:id`, |
| 162 | wantOutIndent: "foo: true\nbar: \"s\"\nbaz: {}\nqux: []\nwib: id\n", |
| 163 | }, { |
| 164 | in: S(`%sfoo%s:%strue%s %sbar%s:%s"s"%s %sbaz%s:%s<>%s %squx%s:%s[]%s %swib%s:%sid%s`, |
| 165 | space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space, space), |
| 166 | wantVal: V(Msg{ |
| 167 | {ID("foo"), V(true)}, |
| 168 | {ID("bar"), V("s")}, |
| 169 | {ID("baz"), V(Msg{})}, |
| 170 | {ID("qux"), V(Lst{})}, |
| 171 | {ID("wib"), ID("id")}, |
| 172 | }), |
| 173 | }, { |
| 174 | in: `foo:true;`, |
| 175 | wantVal: V(Msg{{ID("foo"), V(true)}}), |
| 176 | wantOut: "foo:true", |
| 177 | wantOutIndent: "foo: true\n", |
| 178 | }, { |
| 179 | in: `foo:true,`, |
| 180 | wantVal: V(Msg{{ID("foo"), V(true)}}), |
| 181 | }, { |
| 182 | in: `foo:bar;,`, |
| 183 | wantErr: `invalid "," as identifier`, |
| 184 | }, { |
| 185 | in: `foo:bar,;`, |
| 186 | wantErr: `invalid ";" as identifier`, |
| 187 | }, { |
| 188 | in: `footrue`, |
| 189 | wantErr: `unexpected EOF`, |
| 190 | }, { |
| 191 | in: `foo true`, |
| 192 | wantErr: `expected ':' after message key`, |
| 193 | }, { |
| 194 | in: `foo"s"`, |
| 195 | wantErr: `expected ':' after message key`, |
| 196 | }, { |
| 197 | in: `foo "s"`, |
| 198 | wantErr: `expected ':' after message key`, |
| 199 | }, { |
| 200 | in: `foo{}`, |
| 201 | wantVal: V(Msg{{ID("foo"), V(Msg{})}}), |
| 202 | wantOut: "foo:{}", |
| 203 | wantOutBracket: "foo:<>", |
| 204 | wantOutIndent: "foo: {}\n", |
| 205 | }, { |
| 206 | in: `foo {}`, |
| 207 | wantVal: V(Msg{{ID("foo"), V(Msg{})}}), |
| 208 | }, { |
| 209 | in: `foo<>`, |
| 210 | wantVal: V(Msg{{ID("foo"), V(Msg{})}}), |
| 211 | }, { |
| 212 | in: `foo <>`, |
| 213 | wantVal: V(Msg{{ID("foo"), V(Msg{})}}), |
| 214 | }, { |
| 215 | in: `foo[]`, |
| 216 | wantErr: `expected ':' after message key`, |
| 217 | }, { |
| 218 | in: `foo []`, |
| 219 | wantErr: `expected ':' after message key`, |
| 220 | }, { |
| 221 | in: `foo:truebar:true`, |
| 222 | wantErr: `invalid ":" as identifier`, |
| 223 | }, { |
| 224 | in: `foo:"s"bar:true`, |
| 225 | wantVal: V(Msg{{ID("foo"), V("s")}, {ID("bar"), V(true)}}), |
| 226 | wantOut: `foo:"s" bar:true`, |
| 227 | wantOutIndent: "foo: \"s\"\nbar: true\n", |
| 228 | }, { |
| 229 | in: `foo:0bar:true`, |
| 230 | wantErr: `invalid "0bar" as number or bool`, |
| 231 | }, { |
| 232 | in: `foo:{}bar:true`, |
| 233 | wantVal: V(Msg{{ID("foo"), V(Msg{})}, {ID("bar"), V(true)}}), |
| 234 | wantOut: "foo:{} bar:true", |
| 235 | wantOutBracket: "foo:<> bar:true", |
| 236 | wantOutIndent: "foo: {}\nbar: true\n", |
| 237 | }, { |
| 238 | in: `foo:[]bar:true`, |
| 239 | wantVal: V(Msg{{ID("foo"), V(Lst{})}, {ID("bar"), V(true)}}), |
| 240 | }, { |
| 241 | in: `foo{bar:true}`, |
| 242 | wantVal: V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(true)}})}}), |
| 243 | wantOut: "foo:{bar:true}", |
| 244 | wantOutBracket: "foo:<bar:true>", |
| 245 | wantOutIndent: "foo: {\n\tbar: true\n}\n", |
| 246 | }, { |
| 247 | in: `foo<bar:true>`, |
| 248 | wantVal: V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(true)}})}}), |
| 249 | }, { |
| 250 | in: `foo{bar:true,}`, |
| 251 | wantVal: V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(true)}})}}), |
| 252 | }, { |
| 253 | in: `foo{bar:true;}`, |
| 254 | wantVal: V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(true)}})}}), |
| 255 | }, { |
| 256 | in: `foo{`, |
| 257 | wantErr: `unexpected EOF`, |
| 258 | }, { |
| 259 | in: `foo{ `, |
| 260 | wantErr: `unexpected EOF`, |
| 261 | }, { |
| 262 | in: `foo{[`, |
| 263 | wantErr: `unexpected EOF`, |
| 264 | }, { |
| 265 | in: `foo{[ `, |
| 266 | wantErr: `unexpected EOF`, |
| 267 | }, { |
| 268 | in: `foo{bar:true,;}`, |
| 269 | wantErr: `invalid ";" as identifier`, |
| 270 | }, { |
| 271 | in: `foo{bar:true;,}`, |
| 272 | wantErr: `invalid "," as identifier`, |
| 273 | }, { |
| 274 | in: `foo<bar:{}>`, |
| 275 | wantVal: V(Msg{{ID("foo"), V(Msg{{ID("bar"), V(Msg{})}})}}), |
| 276 | wantOut: "foo:{bar:{}}", |
| 277 | wantOutBracket: "foo:<bar:<>>", |
| 278 | wantOutIndent: "foo: {\n\tbar: {}\n}\n", |
| 279 | }, { |
| 280 | in: `foo<bar:{>`, |
| 281 | wantErr: `invalid character '>', expected '}' at end of message`, |
| 282 | }, { |
| 283 | in: `foo<bar:{}`, |
| 284 | wantErr: `unexpected EOF`, |
| 285 | }, { |
| 286 | in: `arr:[]`, |
| 287 | wantVal: V(Msg{{ID("arr"), V(Lst{})}}), |
| 288 | wantOut: "arr:[]", |
| 289 | wantOutBracket: "arr:[]", |
| 290 | wantOutIndent: "arr: []\n", |
| 291 | }, { |
| 292 | in: `arr:[,]`, |
| 293 | wantErr: `invalid "," as number or bool`, |
| 294 | }, { |
| 295 | in: `arr:[0 0]`, |
| 296 | wantErr: `invalid character '0', expected ']' at end of list`, |
| 297 | }, { |
| 298 | in: `arr:["foo" "bar"]`, |
| 299 | wantVal: V(Msg{{ID("arr"), V(Lst{V("foobar")})}}), |
| 300 | wantOut: `arr:["foobar"]`, |
| 301 | wantOutBracket: `arr:["foobar"]`, |
| 302 | wantOutIndent: "arr: [\n\t\"foobar\"\n]\n", |
| 303 | }, { |
| 304 | in: `arr:[0,]`, |
| 305 | wantErr: `invalid "]" as number or bool`, |
| 306 | }, { |
| 307 | in: `arr:[true,0,"",id,[],{}]`, |
| 308 | wantVal: V(Msg{{ID("arr"), V(Lst{ |
| 309 | V(true), V(uint32(0)), V(""), ID("id"), V(Lst{}), V(Msg{}), |
| 310 | })}}), |
| 311 | wantOut: `arr:[true,0,"",id,[],{}]`, |
| 312 | wantOutBracket: `arr:[true,0,"",id,[],<>]`, |
| 313 | wantOutIndent: "arr: [\n\ttrue,\n\t0,\n\t\"\",\n\tid,\n\t[],\n\t{}\n]\n", |
| 314 | }, { |
| 315 | in: S(`arr:[%strue%s,%s0%s,%s""%s,%sid%s,%s[]%s,%s{}%s]`, |
| 316 | space, space, space, space, space, space, space, space, space, space, space, space), |
| 317 | wantVal: V(Msg{{ID("arr"), V(Lst{ |
| 318 | V(true), V(uint32(0)), V(""), ID("id"), V(Lst{}), V(Msg{}), |
| 319 | })}}), |
| 320 | }, { |
| 321 | in: `arr:[`, |
| 322 | wantErr: `unexpected EOF`, |
| 323 | }, { |
| 324 | in: `{`, |
| 325 | wantErr: `invalid "{" as identifier`, |
| 326 | }, { |
| 327 | in: `<`, |
| 328 | wantErr: `invalid "<" as identifier`, |
| 329 | }, { |
| 330 | in: `[`, |
| 331 | wantErr: "unexpected EOF", |
| 332 | }, { |
| 333 | in: `}`, |
| 334 | wantErr: "1 bytes of unconsumed input", |
| 335 | }, { |
| 336 | in: `>`, |
| 337 | wantErr: "1 bytes of unconsumed input", |
| 338 | }, { |
| 339 | in: `]`, |
| 340 | wantErr: `invalid "]" as identifier`, |
| 341 | }, { |
| 342 | in: `str: "'"`, |
| 343 | wantVal: V(Msg{{ID("str"), V(`'`)}}), |
| 344 | wantOut: `str:"'"`, |
| 345 | }, { |
| 346 | in: `str: '"'`, |
| 347 | wantVal: V(Msg{{ID("str"), V(`"`)}}), |
| 348 | wantOut: `str:"\""`, |
| 349 | }, { |
| 350 | // String that has as few escaped characters as possible. |
| 351 | in: `str: ` + func() string { |
| 352 | var b []byte |
| 353 | for i := 0; i < utf8.RuneSelf; i++ { |
| 354 | switch i { |
| 355 | case 0, '\\', '\n', '\'': // these must be escaped, so ignore them |
| 356 | default: |
| 357 | b = append(b, byte(i)) |
| 358 | } |
| 359 | } |
| 360 | return "'" + string(b) + "'" |
| 361 | }(), |
| 362 | wantVal: V(Msg{{ID("str"), V("\x01\x02\x03\x04\x05\x06\a\b\t\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f")}}), |
| 363 | wantOut: `str:"\x01\x02\x03\x04\x05\x06\x07\x08\t\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_` + "`abcdefghijklmnopqrstuvwxyz{|}~\x7f\"", |
| 364 | wantOutASCII: `str:"\x01\x02\x03\x04\x05\x06\x07\x08\t\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_` + "`abcdefghijklmnopqrstuvwxyz{|}~\x7f\"", |
| 365 | }, { |
Damien Neil | 8c86fc5 | 2019-06-19 09:28:29 -0700 | [diff] [blame] | 366 | in: "str: '\xde\xad\xbe\xef'", |
| 367 | wantErr: "invalid UTF-8 detected", |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 368 | }, { |
| 369 | // Valid UTF-8 wire encoding, but sub-optimal encoding. |
Damien Neil | 8c86fc5 | 2019-06-19 09:28:29 -0700 | [diff] [blame] | 370 | in: "str: '\xc0\x80'", |
| 371 | wantErr: "invalid UTF-8 detected", |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 372 | }, { |
| 373 | // Valid UTF-8 wire encoding, but invalid rune (surrogate pair). |
Damien Neil | 8c86fc5 | 2019-06-19 09:28:29 -0700 | [diff] [blame] | 374 | in: "str: '\xed\xa0\x80'", |
| 375 | wantErr: "invalid UTF-8 detected", |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 376 | }, { |
| 377 | // Valid UTF-8 wire encoding, but invalid rune (above max rune). |
Damien Neil | 8c86fc5 | 2019-06-19 09:28:29 -0700 | [diff] [blame] | 378 | in: "str: '\xf7\xbf\xbf\xbf'", |
| 379 | wantErr: "invalid UTF-8 detected", |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 380 | }, { |
| 381 | // Valid UTF-8 wire encoding of the RuneError rune. |
| 382 | in: "str: '\xef\xbf\xbd'", |
| 383 | wantVal: V(Msg{{ID("str"), V(string(utf8.RuneError))}}), |
| 384 | wantOut: `str:"` + string(utf8.RuneError) + `"`, |
| 385 | wantOutASCII: `str:"\ufffd"`, |
| 386 | }, { |
| 387 | in: "str: 'hello\u1234world'", |
| 388 | wantVal: V(Msg{{ID("str"), V("hello\u1234world")}}), |
| 389 | wantOut: "str:\"hello\u1234world\"", |
| 390 | wantOutASCII: `str:"hello\u1234world"`, |
| 391 | }, { |
| 392 | in: `str: '\"\'\\\?\a\b\n\r\t\v\f\1\12\123\xA\xaB\x12\uAb8f\U0010FFFF'`, |
| 393 | wantVal: V(Msg{{ID("str"), V("\"'\\?\a\b\n\r\t\v\f\x01\nS\n\xab\x12\uab8f\U0010ffff")}}), |
| 394 | wantOut: `str:"\"'\\?\x07\x08\n\r\t\x0b\x0c\x01\nS\n\xab\x12` + "\uab8f\U0010ffff" + `"`, |
| 395 | wantOutASCII: `str:"\"'\\?\x07\x08\n\r\t\x0b\x0c\x01\nS\n\xab\x12\uab8f\U0010ffff"`, |
| 396 | }, { |
| 397 | in: `str: '`, |
| 398 | wantErr: `unexpected EOF`, |
| 399 | }, { |
| 400 | in: `str: '\`, |
| 401 | wantErr: `unexpected EOF`, |
| 402 | }, { |
| 403 | in: `str: '\'`, |
| 404 | wantErr: `unexpected EOF`, |
| 405 | }, { |
| 406 | in: `str: '\8'`, |
| 407 | wantErr: `invalid escape code "\\8" in string`, |
| 408 | }, { |
| 409 | in: `str: '\1x'`, |
| 410 | wantVal: V(Msg{{ID("str"), V("\001x")}}), |
| 411 | wantOut: `str:"\x01x"`, |
| 412 | wantOutASCII: `str:"\x01x"`, |
| 413 | }, { |
| 414 | in: `str: '\12x'`, |
| 415 | wantVal: V(Msg{{ID("str"), V("\012x")}}), |
| 416 | wantOut: `str:"\nx"`, |
| 417 | wantOutASCII: `str:"\nx"`, |
| 418 | }, { |
| 419 | in: `str: '\123x'`, |
| 420 | wantVal: V(Msg{{ID("str"), V("\123x")}}), |
| 421 | wantOut: `str:"Sx"`, |
| 422 | wantOutASCII: `str:"Sx"`, |
| 423 | }, { |
| 424 | in: `str: '\1234x'`, |
| 425 | wantVal: V(Msg{{ID("str"), V("\1234x")}}), |
| 426 | wantOut: `str:"S4x"`, |
| 427 | wantOutASCII: `str:"S4x"`, |
| 428 | }, { |
| 429 | in: `str: '\1'`, |
| 430 | wantVal: V(Msg{{ID("str"), V("\001")}}), |
| 431 | wantOut: `str:"\x01"`, |
| 432 | wantOutASCII: `str:"\x01"`, |
| 433 | }, { |
| 434 | in: `str: '\12'`, |
| 435 | wantVal: V(Msg{{ID("str"), V("\012")}}), |
| 436 | wantOut: `str:"\n"`, |
| 437 | wantOutASCII: `str:"\n"`, |
| 438 | }, { |
| 439 | in: `str: '\123'`, |
| 440 | wantVal: V(Msg{{ID("str"), V("\123")}}), |
| 441 | wantOut: `str:"S"`, |
| 442 | wantOutASCII: `str:"S"`, |
| 443 | }, { |
| 444 | in: `str: '\1234'`, |
| 445 | wantVal: V(Msg{{ID("str"), V("\1234")}}), |
| 446 | wantOut: `str:"S4"`, |
| 447 | wantOutASCII: `str:"S4"`, |
| 448 | }, { |
| 449 | in: `str: '\377'`, |
| 450 | wantVal: V(Msg{{ID("str"), V("\377")}}), |
| 451 | wantOut: `str:"\xff"`, |
| 452 | wantOutASCII: `str:"\xff"`, |
| 453 | }, { |
| 454 | // Overflow octal escape. |
| 455 | in: `str: '\400'`, |
| 456 | wantErr: `invalid octal escape code "\\400" in string`, |
| 457 | }, { |
| 458 | in: `str: '\xfx'`, |
| 459 | wantVal: V(Msg{{ID("str"), V("\x0fx")}}), |
| 460 | wantOut: `str:"\x0fx"`, |
| 461 | wantOutASCII: `str:"\x0fx"`, |
| 462 | }, { |
| 463 | in: `str: '\xffx'`, |
| 464 | wantVal: V(Msg{{ID("str"), V("\xffx")}}), |
| 465 | wantOut: `str:"\xffx"`, |
| 466 | wantOutASCII: `str:"\xffx"`, |
| 467 | }, { |
| 468 | in: `str: '\xfffx'`, |
| 469 | wantVal: V(Msg{{ID("str"), V("\xfffx")}}), |
| 470 | wantOut: `str:"\xfffx"`, |
| 471 | wantOutASCII: `str:"\xfffx"`, |
| 472 | }, { |
| 473 | in: `str: '\xf'`, |
| 474 | wantVal: V(Msg{{ID("str"), V("\x0f")}}), |
| 475 | wantOut: `str:"\x0f"`, |
| 476 | wantOutASCII: `str:"\x0f"`, |
| 477 | }, { |
| 478 | in: `str: '\xff'`, |
| 479 | wantVal: V(Msg{{ID("str"), V("\xff")}}), |
| 480 | wantOut: `str:"\xff"`, |
| 481 | wantOutASCII: `str:"\xff"`, |
| 482 | }, { |
| 483 | in: `str: '\xfff'`, |
| 484 | wantVal: V(Msg{{ID("str"), V("\xfff")}}), |
| 485 | wantOut: `str:"\xfff"`, |
| 486 | wantOutASCII: `str:"\xfff"`, |
| 487 | }, { |
| 488 | in: `str: '\xz'`, |
| 489 | wantErr: `invalid hex escape code "\\x" in string`, |
| 490 | }, { |
| 491 | in: `str: '\uPo'`, |
| 492 | wantErr: `unexpected EOF`, |
| 493 | }, { |
| 494 | in: `str: '\uPoo'`, |
| 495 | wantErr: `invalid Unicode escape code "\\uPoo'" in string`, |
| 496 | }, { |
| 497 | in: `str: '\uPoop'`, |
| 498 | wantErr: `invalid Unicode escape code "\\uPoop" in string`, |
| 499 | }, { |
| 500 | // Unmatched surrogate pair. |
| 501 | in: `str: '\uDEAD'`, |
| 502 | wantErr: `unexpected EOF`, // trying to reader other half |
| 503 | }, { |
| 504 | // Surrogate pair with invalid other half. |
| 505 | in: `str: '\uDEAD\u0000'`, |
| 506 | wantErr: `invalid Unicode escape code "\\u0000" in string`, |
| 507 | }, { |
| 508 | // Properly matched surrogate pair. |
| 509 | in: `str: '\uD800\uDEAD'`, |
| 510 | wantVal: V(Msg{{ID("str"), V("𐊭")}}), |
| 511 | wantOut: `str:"𐊭"`, |
| 512 | wantOutASCII: `str:"\U000102ad"`, |
| 513 | }, { |
| 514 | // Overflow on Unicode rune. |
| 515 | in: `str: '\U00110000'`, |
| 516 | wantErr: `invalid Unicode escape code "\\U00110000" in string`, |
| 517 | }, { |
| 518 | in: `str: '\z'`, |
| 519 | wantErr: `invalid escape code "\\z" in string`, |
| 520 | }, { |
| 521 | // Strings cannot have NUL literal since C-style strings forbid them. |
| 522 | in: "str: '\x00'", |
| 523 | wantErr: `invalid character '\x00' in string`, |
| 524 | }, { |
| 525 | // Strings cannot have newline literal. The C++ permits them if an |
| 526 | // option is specified to allow them. In Go, we always forbid them. |
| 527 | in: "str: '\n'", |
| 528 | wantErr: `invalid character '\n' in string`, |
| 529 | }, { |
| 530 | in: "name: \"My name is \"\n\"elsewhere\"", |
| 531 | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), |
| 532 | wantOut: `name:"My name is elsewhere"`, |
| 533 | wantOutASCII: `name:"My name is elsewhere"`, |
| 534 | }, { |
| 535 | in: "name: 'My name is '\n'elsewhere'", |
| 536 | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), |
| 537 | }, { |
| 538 | in: "name: 'My name is '\n\"elsewhere\"", |
| 539 | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), |
| 540 | }, { |
| 541 | in: "name: \"My name is \"\n'elsewhere'", |
| 542 | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), |
| 543 | }, { |
| 544 | in: "name: \"My \"'name '\"is \"\n'elsewhere'", |
| 545 | wantVal: V(Msg{{ID("name"), V("My name is elsewhere")}}), |
| 546 | }, { |
| 547 | in: `crazy:"x'"'\""\''"'z"`, |
| 548 | wantVal: V(Msg{{ID("crazy"), V(`x'""''z`)}}), |
| 549 | }, { |
| 550 | in: `nums: [t,T,true,True,TRUE,f,F,false,False,FALSE]`, |
| 551 | wantVal: V(Msg{{ID("nums"), V(Lst{ |
| 552 | V(true), |
| 553 | ID("T"), |
| 554 | V(true), |
| 555 | V(true), |
| 556 | ID("TRUE"), |
| 557 | V(false), |
| 558 | ID("F"), |
| 559 | V(false), |
| 560 | V(false), |
| 561 | ID("FALSE"), |
| 562 | })}}), |
| 563 | wantOut: "nums:[true,T,true,true,TRUE,false,F,false,false,FALSE]", |
| 564 | wantOutIndent: "nums: [\n\ttrue,\n\tT,\n\ttrue,\n\ttrue,\n\tTRUE,\n\tfalse,\n\tF,\n\tfalse,\n\tfalse,\n\tFALSE\n]\n", |
| 565 | }, { |
| 566 | in: `nums: [nan,inf,-inf,NaN,NAN,Inf,INF]`, |
| 567 | wantVal: V(Msg{{ID("nums"), V(Lst{ |
| 568 | V(math.NaN()), |
| 569 | V(math.Inf(+1)), |
| 570 | V(math.Inf(-1)), |
| 571 | ID("NaN"), |
| 572 | ID("NAN"), |
| 573 | ID("Inf"), |
| 574 | ID("INF"), |
| 575 | })}}), |
| 576 | wantOut: "nums:[nan,inf,-inf,NaN,NAN,Inf,INF]", |
| 577 | wantOutIndent: "nums: [\n\tnan,\n\tinf,\n\t-inf,\n\tNaN,\n\tNAN,\n\tInf,\n\tINF\n]\n", |
| 578 | }, { |
| 579 | // C++ permits this, but we currently reject this. |
| 580 | in: `num: -nan`, |
| 581 | wantErr: `invalid "-nan" as number or bool`, |
| 582 | }, { |
| 583 | in: `nums: [0,-0,-9876543210,9876543210,0x0,0x0123456789abcdef,-0x0123456789abcdef,01234567,-01234567]`, |
| 584 | wantVal: V(Msg{{ID("nums"), V(Lst{ |
| 585 | V(uint32(0)), |
| 586 | V(int32(-0)), |
| 587 | V(int64(-9876543210)), |
| 588 | V(uint64(9876543210)), |
| 589 | V(uint32(0x0)), |
| 590 | V(uint64(0x0123456789abcdef)), |
| 591 | V(int64(-0x0123456789abcdef)), |
| 592 | V(uint64(01234567)), |
| 593 | V(int64(-01234567)), |
| 594 | })}}), |
| 595 | wantOut: "nums:[0,0,-9876543210,9876543210,0,81985529216486895,-81985529216486895,342391,-342391]", |
| 596 | wantOutIndent: "nums: [\n\t0,\n\t0,\n\t-9876543210,\n\t9876543210,\n\t0,\n\t81985529216486895,\n\t-81985529216486895,\n\t342391,\n\t-342391\n]\n", |
| 597 | }, { |
| 598 | in: `nums: [0.,0f,1f,10f,-0f,-1f,-10f,1.0,0.1e-3,1.5e+5,1e10,.0]`, |
| 599 | wantVal: V(Msg{{ID("nums"), V(Lst{ |
| 600 | V(0.0), |
| 601 | V(0.0), |
| 602 | V(1.0), |
| 603 | V(10.0), |
| 604 | V(-0.0), |
| 605 | V(-1.0), |
| 606 | V(-10.0), |
| 607 | V(1.0), |
| 608 | V(0.1e-3), |
| 609 | V(1.5e+5), |
| 610 | V(1.0e+10), |
| 611 | V(0.0), |
| 612 | })}}), |
| 613 | wantOut: "nums:[0,0,1,10,0,-1,-10,1,0.0001,150000,1e+10,0]", |
| 614 | wantOutIndent: "nums: [\n\t0,\n\t0,\n\t1,\n\t10,\n\t0,\n\t-1,\n\t-10,\n\t1,\n\t0.0001,\n\t150000,\n\t1e+10,\n\t0\n]\n", |
| 615 | }, { |
| 616 | in: `nums: [0xbeefbeef,0xbeefbeefbeefbeef]`, |
| 617 | wantVal: V(Msg{{ID("nums"), func() Value { |
Joe Tsai | 1799d11 | 2019-08-08 13:31:59 -0700 | [diff] [blame] | 618 | if flags.ProtoLegacy { |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 619 | return V(Lst{V(int32(-1091584273)), V(int64(-4688318750159552785))}) |
| 620 | } else { |
| 621 | return V(Lst{V(uint32(0xbeefbeef)), V(uint64(0xbeefbeefbeefbeef))}) |
| 622 | } |
| 623 | }()}}), |
| 624 | }, { |
| 625 | in: `num: +0`, |
| 626 | wantErr: `invalid "+0" as number or bool`, |
| 627 | }, { |
| 628 | in: `num: 01.1234`, |
| 629 | wantErr: `invalid "01.1234" as number or bool`, |
| 630 | }, { |
| 631 | in: `num: 0x`, |
| 632 | wantErr: `invalid "0x" as number or bool`, |
| 633 | }, { |
| 634 | in: `num: 0xX`, |
| 635 | wantErr: `invalid "0xX" as number or bool`, |
| 636 | }, { |
| 637 | in: `num: 0800`, |
| 638 | wantErr: `invalid "0800" as number or bool`, |
| 639 | }, { |
| 640 | in: `num: true.`, |
| 641 | wantErr: `invalid "true." as number or bool`, |
| 642 | }, { |
| 643 | in: `num: .`, |
| 644 | wantErr: `parsing ".": invalid syntax`, |
| 645 | }, { |
| 646 | in: `num: -.`, |
| 647 | wantErr: `parsing "-.": invalid syntax`, |
| 648 | }, { |
| 649 | in: `num: 1e10000`, |
| 650 | wantErr: `parsing "1e10000": value out of range`, |
| 651 | }, { |
| 652 | in: `num: 99999999999999999999`, |
| 653 | wantErr: `parsing "99999999999999999999": value out of range`, |
| 654 | }, { |
| 655 | in: `num: -99999999999999999999`, |
| 656 | wantErr: `parsing "-99999999999999999999": value out of range`, |
| 657 | }, { |
| 658 | in: "x: -", |
| 659 | wantErr: `syntax error (line 1:5)`, |
| 660 | }, { |
| 661 | in: "x:[\"💩\"x", |
| 662 | wantErr: `syntax error (line 1:7)`, |
| 663 | }, { |
| 664 | in: "x:\n\n[\"🔥🔥🔥\"x", |
| 665 | wantErr: `syntax error (line 3:7)`, |
| 666 | }, { |
| 667 | in: "x:[\"👍🏻👍🏿\"x", |
| 668 | wantErr: `syntax error (line 1:10)`, // multi-rune emojis; could be column:8 |
| 669 | }, { |
| 670 | in: ` |
| 671 | firstName : "John", |
| 672 | lastName : "Smith" , |
| 673 | isAlive : true, |
| 674 | age : 27, |
| 675 | address { # missing colon is okay for messages |
| 676 | streetAddress : "21 2nd Street" , |
| 677 | city : "New York" , |
| 678 | state : "NY" , |
| 679 | postalCode : "10021-3100" ; # trailing semicolon is okay |
| 680 | }, |
| 681 | phoneNumbers : [ { |
| 682 | type : "home" , |
| 683 | number : "212 555-1234" |
| 684 | } , { |
| 685 | type : "office" , |
| 686 | number : "646 555-4567" |
| 687 | } , { |
| 688 | type : "mobile" , |
| 689 | number : "123 456-7890" , # trailing comma is okay |
| 690 | } ], |
| 691 | children : [] , |
| 692 | spouse : null`, |
| 693 | wantVal: V(Msg{ |
| 694 | {ID("firstName"), V("John")}, |
| 695 | {ID("lastName"), V("Smith")}, |
| 696 | {ID("isAlive"), V(true)}, |
| 697 | {ID("age"), V(27.0)}, |
| 698 | {ID("address"), V(Msg{ |
| 699 | {ID("streetAddress"), V("21 2nd Street")}, |
| 700 | {ID("city"), V("New York")}, |
| 701 | {ID("state"), V("NY")}, |
| 702 | {ID("postalCode"), V("10021-3100")}, |
| 703 | })}, |
| 704 | {ID("phoneNumbers"), V([]Value{ |
| 705 | V(Msg{ |
| 706 | {ID("type"), V("home")}, |
| 707 | {ID("number"), V("212 555-1234")}, |
| 708 | }), |
| 709 | V(Msg{ |
| 710 | {ID("type"), V("office")}, |
| 711 | {ID("number"), V("646 555-4567")}, |
| 712 | }), |
| 713 | V(Msg{ |
| 714 | {ID("type"), V("mobile")}, |
| 715 | {ID("number"), V("123 456-7890")}, |
| 716 | }), |
| 717 | })}, |
| 718 | {ID("children"), V([]Value{})}, |
| 719 | {ID("spouse"), V(protoreflect.Name("null"))}, |
| 720 | }), |
| 721 | wantOut: `firstName:"John" lastName:"Smith" isAlive:true age:27 address:{streetAddress:"21 2nd Street" city:"New York" state:"NY" postalCode:"10021-3100"} phoneNumbers:[{type:"home" number:"212 555-1234"},{type:"office" number:"646 555-4567"},{type:"mobile" number:"123 456-7890"}] children:[] spouse:null`, |
| 722 | wantOutBracket: `firstName:"John" lastName:"Smith" isAlive:true age:27 address:<streetAddress:"21 2nd Street" city:"New York" state:"NY" postalCode:"10021-3100"> phoneNumbers:[<type:"home" number:"212 555-1234">,<type:"office" number:"646 555-4567">,<type:"mobile" number:"123 456-7890">] children:[] spouse:null`, |
| 723 | wantOutIndent: `firstName: "John" |
| 724 | lastName: "Smith" |
| 725 | isAlive: true |
| 726 | age: 27 |
| 727 | address: { |
| 728 | streetAddress: "21 2nd Street" |
| 729 | city: "New York" |
| 730 | state: "NY" |
| 731 | postalCode: "10021-3100" |
| 732 | } |
| 733 | phoneNumbers: [ |
| 734 | { |
| 735 | type: "home" |
| 736 | number: "212 555-1234" |
| 737 | }, |
| 738 | { |
| 739 | type: "office" |
| 740 | number: "646 555-4567" |
| 741 | }, |
| 742 | { |
| 743 | type: "mobile" |
| 744 | number: "123 456-7890" |
| 745 | } |
| 746 | ] |
| 747 | children: [] |
| 748 | spouse: null |
| 749 | `, |
| 750 | }} |
| 751 | |
| 752 | opts := cmp.Options{ |
| 753 | cmpopts.EquateEmpty(), |
| 754 | |
| 755 | // Transform composites (List and Message). |
| 756 | cmp.FilterValues(func(x, y Value) bool { |
| 757 | return (x.Type() == List && y.Type() == List) || (x.Type() == Message && y.Type() == Message) |
| 758 | }, cmp.Transformer("", func(v Value) interface{} { |
| 759 | if v.Type() == List { |
| 760 | return v.List() |
| 761 | } else { |
| 762 | return v.Message() |
| 763 | } |
| 764 | })), |
| 765 | |
| 766 | // Compare scalars (Bool, Int, Uint, Float, String, Name). |
| 767 | cmp.FilterValues(func(x, y Value) bool { |
| 768 | return !(x.Type() == List && y.Type() == List) && !(x.Type() == Message && y.Type() == Message) |
| 769 | }, cmp.Comparer(func(x, y Value) bool { |
| 770 | if x.Type() == List || x.Type() == Message || y.Type() == List || y.Type() == Message { |
| 771 | return false |
| 772 | } |
| 773 | // Ensure golden value is always in x variable. |
| 774 | if len(x.raw) > 0 { |
| 775 | x, y = y, x |
| 776 | } |
| 777 | switch x.Type() { |
| 778 | case Bool: |
| 779 | want, _ := x.Bool() |
| 780 | got, ok := y.Bool() |
| 781 | return got == want && ok |
| 782 | case Int: |
| 783 | want, _ := x.Int(true) |
| 784 | got, ok := y.Int(want < math.MinInt32 || math.MaxInt32 < want) |
| 785 | return got == want && ok |
| 786 | case Uint: |
| 787 | want, _ := x.Uint(true) |
| 788 | got, ok := y.Uint(math.MaxUint32 < want) |
| 789 | return got == want && ok |
Herbie Ong | 250c6ea | 2019-03-12 20:55:10 -0700 | [diff] [blame] | 790 | case Float32, Float64: |
| 791 | want, _ := x.Float(true) |
| 792 | got, ok := y.Float(math.MaxFloat32 < math.Abs(want)) |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 793 | if math.IsNaN(got) || math.IsNaN(want) { |
| 794 | return math.IsNaN(got) == math.IsNaN(want) |
| 795 | } |
| 796 | return got == want && ok |
| 797 | case Name: |
| 798 | want, _ := x.Name() |
| 799 | got, ok := y.Name() |
| 800 | return got == want && ok |
| 801 | default: |
| 802 | return x.String() == y.String() |
| 803 | } |
| 804 | })), |
| 805 | } |
| 806 | for _, tt := range tests { |
| 807 | t.Run("", func(t *testing.T) { |
| 808 | if tt.in != "" || tt.wantVal.Type() != 0 || tt.wantErr != "" { |
| 809 | gotVal, err := Unmarshal([]byte(tt.in)) |
| 810 | if err == nil { |
| 811 | if tt.wantErr != "" { |
| 812 | t.Errorf("Unmarshal(): got nil error, want %v", tt.wantErr) |
| 813 | } |
| 814 | } else { |
| 815 | if tt.wantErr == "" { |
| 816 | t.Errorf("Unmarshal(): got %v, want nil error", err) |
| 817 | } else if !strings.Contains(err.Error(), tt.wantErr) { |
| 818 | t.Errorf("Unmarshal(): got %v, want %v", err, tt.wantErr) |
| 819 | } |
| 820 | } |
| 821 | if diff := cmp.Diff(gotVal, tt.wantVal, opts); diff != "" { |
| 822 | t.Errorf("Unmarshal(): output mismatch (-got +want):\n%s", diff) |
| 823 | } |
| 824 | } |
| 825 | if tt.wantOut != "" { |
| 826 | gotOut, err := Marshal(tt.wantVal, "", [2]byte{0, 0}, false) |
| 827 | if err != nil { |
| 828 | t.Errorf("Marshal(): got %v, want nil error", err) |
| 829 | } |
Joe Tsai | 71acbc7 | 2018-11-28 19:27:29 -0800 | [diff] [blame] | 830 | if string(gotOut) != tt.wantOut { |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 831 | t.Errorf("Marshal():\ngot: %s\nwant: %s", gotOut, tt.wantOut) |
| 832 | } |
| 833 | } |
| 834 | if tt.wantOutBracket != "" { |
| 835 | gotOut, err := Marshal(tt.wantVal, "", [2]byte{'<', '>'}, false) |
| 836 | if err != nil { |
| 837 | t.Errorf("Marshal(Bracket): got %v, want nil error", err) |
| 838 | } |
Joe Tsai | 71acbc7 | 2018-11-28 19:27:29 -0800 | [diff] [blame] | 839 | if string(gotOut) != tt.wantOutBracket { |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 840 | t.Errorf("Marshal(Bracket):\ngot: %s\nwant: %s", gotOut, tt.wantOutBracket) |
| 841 | } |
| 842 | } |
| 843 | if tt.wantOutASCII != "" { |
| 844 | gotOut, err := Marshal(tt.wantVal, "", [2]byte{0, 0}, true) |
| 845 | if err != nil { |
| 846 | t.Errorf("Marshal(ASCII): got %v, want nil error", err) |
| 847 | } |
Joe Tsai | 71acbc7 | 2018-11-28 19:27:29 -0800 | [diff] [blame] | 848 | if string(gotOut) != tt.wantOutASCII { |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 849 | t.Errorf("Marshal(ASCII):\ngot: %s\nwant: %s", gotOut, tt.wantOutASCII) |
| 850 | } |
| 851 | } |
| 852 | if tt.wantOutIndent != "" { |
| 853 | gotOut, err := Marshal(tt.wantVal, "\t", [2]byte{0, 0}, false) |
| 854 | if err != nil { |
| 855 | t.Errorf("Marshal(Indent): got %v, want nil error", err) |
| 856 | } |
Joe Tsai | 71acbc7 | 2018-11-28 19:27:29 -0800 | [diff] [blame] | 857 | if string(gotOut) != tt.wantOutIndent { |
Joe Tsai | 27c2a76 | 2018-08-01 16:48:18 -0700 | [diff] [blame] | 858 | t.Errorf("Marshal(Indent):\ngot: %s\nwant: %s", gotOut, tt.wantOutIndent) |
| 859 | } |
| 860 | } |
| 861 | }) |
| 862 | } |
| 863 | } |