encode_test.go 13 KB

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