1
2
3
4
5 package text_test
6
7 import (
8 "math"
9 "strings"
10 "testing"
11 "unicode/utf8"
12
13 "github.com/google/go-cmp/cmp"
14
15 "google.golang.org/protobuf/internal/detrand"
16 "google.golang.org/protobuf/internal/encoding/text"
17 )
18
19
20 func init() { detrand.Disable() }
21
22 func TestEncoder(t *testing.T) {
23 tests := []encoderTestCase{
24 {
25 desc: "no-opt",
26 write: func(e *text.Encoder) {},
27 wantOut: ``,
28 wantOutIndent: ``,
29 },
30 {
31 desc: "true",
32 write: func(e *text.Encoder) {
33 e.WriteName("bool")
34 e.WriteBool(true)
35 },
36 wantOut: `bool:true`,
37 wantOutIndent: `bool: true`,
38 },
39 {
40 desc: "false",
41 write: func(e *text.Encoder) {
42 e.WriteName("bool")
43 e.WriteBool(false)
44 },
45 wantOut: `bool:false`,
46 wantOutIndent: `bool: false`,
47 },
48 {
49 desc: "bracket name",
50 write: func(e *text.Encoder) {
51 e.WriteName("[extension]")
52 e.WriteString("hello")
53 },
54 wantOut: `[extension]:"hello"`,
55 wantOutIndent: `[extension]: "hello"`,
56 },
57 {
58 desc: "numeric name",
59 write: func(e *text.Encoder) {
60 e.WriteName("01234")
61 e.WriteString("hello")
62 },
63 wantOut: `01234:"hello"`,
64 wantOutIndent: `01234: "hello"`,
65 },
66 {
67 desc: "string",
68 write: func(e *text.Encoder) {
69 e.WriteName("str")
70 e.WriteString("hello world")
71 },
72 wantOut: `str:"hello world"`,
73 wantOutIndent: `str: "hello world"`,
74 },
75 {
76 desc: "enum",
77 write: func(e *text.Encoder) {
78 e.WriteName("enum")
79 e.WriteLiteral("ENUM_VALUE")
80 },
81 wantOut: `enum:ENUM_VALUE`,
82 wantOutIndent: `enum: ENUM_VALUE`,
83 },
84 {
85 desc: "float64",
86 write: func(e *text.Encoder) {
87 e.WriteName("float64")
88 e.WriteFloat(1.0199999809265137, 64)
89 },
90 wantOut: `float64:1.0199999809265137`,
91 wantOutIndent: `float64: 1.0199999809265137`,
92 },
93 {
94 desc: "float64 max value",
95 write: func(e *text.Encoder) {
96 e.WriteName("float64")
97 e.WriteFloat(math.MaxFloat64, 64)
98 },
99 wantOut: `float64:1.7976931348623157e+308`,
100 wantOutIndent: `float64: 1.7976931348623157e+308`,
101 },
102 {
103 desc: "float64 min value",
104 write: func(e *text.Encoder) {
105 e.WriteName("float64")
106 e.WriteFloat(-math.MaxFloat64, 64)
107 },
108 wantOut: `float64:-1.7976931348623157e+308`,
109 wantOutIndent: `float64: -1.7976931348623157e+308`,
110 },
111 {
112 desc: "float64 nan",
113 write: func(e *text.Encoder) {
114 e.WriteName("float64")
115 e.WriteFloat(math.NaN(), 64)
116 },
117 wantOut: `float64:nan`,
118 wantOutIndent: `float64: nan`,
119 },
120 {
121 desc: "float64 inf",
122 write: func(e *text.Encoder) {
123 e.WriteName("float64")
124 e.WriteFloat(math.Inf(+1), 64)
125 },
126 wantOut: `float64:inf`,
127 wantOutIndent: `float64: inf`,
128 },
129 {
130 desc: "float64 -inf",
131 write: func(e *text.Encoder) {
132 e.WriteName("float64")
133 e.WriteFloat(math.Inf(-1), 64)
134 },
135 wantOut: `float64:-inf`,
136 wantOutIndent: `float64: -inf`,
137 },
138 {
139 desc: "float64 negative zero",
140 write: func(e *text.Encoder) {
141 e.WriteName("float64")
142 e.WriteFloat(math.Copysign(0, -1), 64)
143 },
144 wantOut: `float64:-0`,
145 wantOutIndent: `float64: -0`,
146 },
147 {
148 desc: "float32",
149 write: func(e *text.Encoder) {
150 e.WriteName("float")
151 e.WriteFloat(1.02, 32)
152 },
153 wantOut: `float:1.02`,
154 wantOutIndent: `float: 1.02`,
155 },
156 {
157 desc: "float32 max value",
158 write: func(e *text.Encoder) {
159 e.WriteName("float32")
160 e.WriteFloat(math.MaxFloat32, 32)
161 },
162 wantOut: `float32:3.4028235e+38`,
163 wantOutIndent: `float32: 3.4028235e+38`,
164 },
165 {
166 desc: "float32 nan",
167 write: func(e *text.Encoder) {
168 e.WriteName("float32")
169 e.WriteFloat(math.NaN(), 32)
170 },
171 wantOut: `float32:nan`,
172 wantOutIndent: `float32: nan`,
173 },
174 {
175 desc: "float32 inf",
176 write: func(e *text.Encoder) {
177 e.WriteName("float32")
178 e.WriteFloat(math.Inf(+1), 32)
179 },
180 wantOut: `float32:inf`,
181 wantOutIndent: `float32: inf`,
182 },
183 {
184 desc: "float32 -inf",
185 write: func(e *text.Encoder) {
186 e.WriteName("float32")
187 e.WriteFloat(math.Inf(-1), 32)
188 },
189 wantOut: `float32:-inf`,
190 wantOutIndent: `float32: -inf`,
191 },
192 {
193 desc: "float32 negative zero",
194 write: func(e *text.Encoder) {
195 e.WriteName("float32")
196 e.WriteFloat(math.Copysign(0, -1), 32)
197 },
198 wantOut: `float32:-0`,
199 wantOutIndent: `float32: -0`,
200 },
201 {
202 desc: "int64 max value",
203 write: func(e *text.Encoder) {
204 e.WriteName("int")
205 e.WriteInt(math.MaxInt64)
206 },
207 wantOut: `int:9223372036854775807`,
208 wantOutIndent: `int: 9223372036854775807`,
209 },
210 {
211 desc: "int64 min value",
212 write: func(e *text.Encoder) {
213 e.WriteName("int")
214 e.WriteInt(math.MinInt64)
215 },
216 wantOut: `int:-9223372036854775808`,
217 wantOutIndent: `int: -9223372036854775808`,
218 },
219 {
220 desc: "uint",
221 write: func(e *text.Encoder) {
222 e.WriteName("uint")
223 e.WriteUint(math.MaxUint64)
224 },
225 wantOut: `uint:18446744073709551615`,
226 wantOutIndent: `uint: 18446744073709551615`,
227 },
228 {
229 desc: "empty message field",
230 write: func(e *text.Encoder) {
231 e.WriteName("m")
232 e.StartMessage()
233 e.EndMessage()
234 },
235 wantOut: `m:{}`,
236 wantOutIndent: `m: {}`,
237 },
238 {
239 desc: "multiple fields",
240 write: func(e *text.Encoder) {
241 e.WriteName("bool")
242 e.WriteBool(true)
243 e.WriteName("str")
244 e.WriteString("hello")
245 e.WriteName("str")
246 e.WriteString("world")
247 e.WriteName("m")
248 e.StartMessage()
249 e.EndMessage()
250 e.WriteName("[int]")
251 e.WriteInt(49)
252 e.WriteName("float64")
253 e.WriteFloat(1.00023e4, 64)
254 e.WriteName("101")
255 e.WriteString("unknown")
256 },
257 wantOut: `bool:true str:"hello" str:"world" m:{} [int]:49 float64:10002.3 101:"unknown"`,
258 wantOutIndent: `bool: true
259 str: "hello"
260 str: "world"
261 m: {}
262 [int]: 49
263 float64: 10002.3
264 101: "unknown"`,
265 },
266 {
267 desc: "populated message fields",
268 write: func(e *text.Encoder) {
269 e.WriteName("m1")
270 e.StartMessage()
271 {
272 e.WriteName("str")
273 e.WriteString("hello")
274 }
275 e.EndMessage()
276
277 e.WriteName("bool")
278 e.WriteBool(true)
279
280 e.WriteName("m2")
281 e.StartMessage()
282 {
283 e.WriteName("str")
284 e.WriteString("world")
285 e.WriteName("m2-1")
286 e.StartMessage()
287 e.EndMessage()
288 e.WriteName("m2-2")
289 e.StartMessage()
290 {
291 e.WriteName("[int]")
292 e.WriteInt(49)
293 }
294 e.EndMessage()
295 e.WriteName("float64")
296 e.WriteFloat(1.00023e4, 64)
297 }
298 e.EndMessage()
299
300 e.WriteName("101")
301 e.WriteString("unknown")
302 },
303 wantOut: `m1:{str:"hello"} bool:true m2:{str:"world" m2-1:{} m2-2:{[int]:49} float64:10002.3} 101:"unknown"`,
304 wantOutIndent: `m1: {
305 str: "hello"
306 }
307 bool: true
308 m2: {
309 str: "world"
310 m2-1: {}
311 m2-2: {
312 [int]: 49
313 }
314 float64: 10002.3
315 }
316 101: "unknown"`,
317 },
318 }
319
320 for _, tc := range tests {
321 t.Run(tc.desc, func(t *testing.T) {
322 runEncoderTest(t, tc, [2]byte{})
323
324
325
326 tc.wantOut = replaceDelims(tc.wantOut)
327 tc.wantOutIndent = replaceDelims(tc.wantOutIndent)
328 runEncoderTest(t, tc, [2]byte{'<', '>'})
329 })
330 }
331 }
332
333 type encoderTestCase struct {
334 desc string
335 write func(*text.Encoder)
336 wantOut string
337 wantOutIndent string
338 }
339
340 func runEncoderTest(t *testing.T, tc encoderTestCase, delims [2]byte) {
341 t.Helper()
342
343 if tc.wantOut != "" {
344 enc, err := text.NewEncoder(nil, "", delims, false)
345 if err != nil {
346 t.Fatalf("NewEncoder returned error: %v", err)
347 }
348 tc.write(enc)
349 got := string(enc.Bytes())
350 if got != tc.wantOut {
351 t.Errorf("(compact)\n<got>\n%v\n<want>\n%v\n", got, tc.wantOut)
352 }
353 }
354 if tc.wantOutIndent != "" {
355 enc, err := text.NewEncoder(nil, "\t", delims, false)
356 if err != nil {
357 t.Fatalf("NewEncoder returned error: %v", err)
358 }
359 tc.write(enc)
360 got, want := string(enc.Bytes()), tc.wantOutIndent
361 if got != want {
362 t.Errorf("(multi-line)\n<got>\n%v\n<want>\n%v\n<diff -want +got>\n%v\n",
363 got, want, cmp.Diff(want, got))
364 }
365 }
366 }
367
368 func replaceDelims(s string) string {
369 s = strings.Replace(s, "{", "<", -1)
370 return strings.Replace(s, "}", ">", -1)
371 }
372
373
374 func TestEncodeStrings(t *testing.T) {
375 tests := []struct {
376 in string
377 wantOut string
378 wantOutASCII string
379 }{
380 {
381 in: `"`,
382 wantOut: `"\""`,
383 },
384 {
385 in: `'`,
386 wantOut: `"'"`,
387 },
388 {
389 in: "hello\u1234world",
390 wantOut: "\"hello\u1234world\"",
391 wantOutASCII: `"hello\u1234world"`,
392 },
393 {
394
395 in: func() string {
396 var b []byte
397 for i := rune(0); i <= 0x00a0; i++ {
398 switch i {
399 case 0, '\\', '\n', '\'':
400 default:
401 var r [utf8.UTFMax]byte
402 n := utf8.EncodeRune(r[:], i)
403 b = append(b, r[:n]...)
404 }
405 }
406 return string(b)
407 }(),
408 wantOut: `"\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\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f` + "\u00a0" + `"`,
409 wantOutASCII: `"\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\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\u00a0"`,
410 },
411 {
412
413 in: string(utf8.RuneError),
414 wantOut: `"` + string(utf8.RuneError) + `"`,
415 wantOutASCII: `"\ufffd"`,
416 },
417 {
418 in: "\"'\\?\a\b\n\r\t\v\f\x01\nS\n\xab\x12\uab8f\U0010ffff",
419 wantOut: `"\"'\\?\x07\x08\n\r\t\x0b\x0c\x01\nS\n\xab\x12` + "\uab8f\U0010ffff" + `"`,
420 wantOutASCII: `"\"'\\?\x07\x08\n\r\t\x0b\x0c\x01\nS\n\xab\x12\uab8f\U0010ffff"`,
421 },
422 {
423 in: "\001x",
424 wantOut: `"\x01x"`,
425 wantOutASCII: `"\x01x"`,
426 },
427 {
428 in: "\012x",
429 wantOut: `"\nx"`,
430 wantOutASCII: `"\nx"`,
431 },
432 {
433 in: "\123x",
434 wantOut: `"Sx"`,
435 wantOutASCII: `"Sx"`,
436 },
437 {
438 in: "\1234x",
439 wantOut: `"S4x"`,
440 wantOutASCII: `"S4x"`,
441 },
442 {
443 in: "\001",
444 wantOut: `"\x01"`,
445 wantOutASCII: `"\x01"`,
446 },
447 {
448 in: "\012",
449 wantOut: `"\n"`,
450 wantOutASCII: `"\n"`,
451 },
452 {
453 in: "\123",
454 wantOut: `"S"`,
455 wantOutASCII: `"S"`,
456 },
457 {
458 in: "\1234",
459 wantOut: `"S4"`,
460 wantOutASCII: `"S4"`,
461 },
462 {
463 in: "\377",
464 wantOut: `"\xff"`,
465 wantOutASCII: `"\xff"`,
466 },
467 {
468 in: "\x0fx",
469 wantOut: `"\x0fx"`,
470 wantOutASCII: `"\x0fx"`,
471 },
472 {
473 in: "\xffx",
474 wantOut: `"\xffx"`,
475 wantOutASCII: `"\xffx"`,
476 },
477 {
478 in: "\xfffx",
479 wantOut: `"\xfffx"`,
480 wantOutASCII: `"\xfffx"`,
481 },
482 {
483 in: "\x0f",
484 wantOut: `"\x0f"`,
485 wantOutASCII: `"\x0f"`,
486 },
487 {
488 in: "\x7f",
489 wantOut: `"\x7f"`,
490 wantOutASCII: `"\x7f"`,
491 },
492 {
493 in: "\xff",
494 wantOut: `"\xff"`,
495 wantOutASCII: `"\xff"`,
496 },
497 {
498 in: "\xfff",
499 wantOut: `"\xfff"`,
500 wantOutASCII: `"\xfff"`,
501 },
502 }
503 for _, tc := range tests {
504 t.Run("", func(t *testing.T) {
505 if tc.wantOut != "" {
506 runEncodeStringsTest(t, tc.in, tc.wantOut, false)
507 }
508 if tc.wantOutASCII != "" {
509 runEncodeStringsTest(t, tc.in, tc.wantOutASCII, true)
510 }
511 })
512 }
513 }
514
515 func runEncodeStringsTest(t *testing.T, in string, want string, outputASCII bool) {
516 t.Helper()
517
518 charType := "UTF-8"
519 if outputASCII {
520 charType = "ASCII"
521 }
522
523 enc, err := text.NewEncoder(nil, "", [2]byte{}, outputASCII)
524 if err != nil {
525 t.Fatalf("[%s] NewEncoder returned error: %v", charType, err)
526 }
527 enc.WriteString(in)
528 got := string(enc.Bytes())
529 if got != want {
530 t.Errorf("[%s] WriteString(%q)\n<got>\n%v\n<want>\n%v\n", charType, in, got, want)
531 }
532 }
533
534 func TestReset(t *testing.T) {
535 enc, err := text.NewEncoder(nil, "\t", [2]byte{}, false)
536 if err != nil {
537 t.Fatalf("NewEncoder returned error: %v", err)
538 }
539
540 enc.WriteName("foo")
541 pos := enc.Snapshot()
542
543
544 enc.StartMessage()
545 enc.WriteName("bar")
546 enc.WriteUint(10)
547
548
549 enc.Reset(pos)
550 enc.WriteString("0123456789")
551
552 got := string(enc.Bytes())
553 want := `foo: "0123456789"`
554 if got != want {
555 t.Errorf("Reset did not restore given position:\n<got>\n%v\n<want>\n%v\n", got, want)
556 }
557 }
558
View as plain text