You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

519 lines
13 KiB

  1. // Go support for Protocol Buffers - Google's data interchange format
  2. //
  3. // Copyright 2010 The Go Authors. All rights reserved.
  4. // https://github.com/golang/protobuf
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are
  8. // met:
  9. //
  10. // * Redistributions of source code must retain the above copyright
  11. // notice, this list of conditions and the following disclaimer.
  12. // * Redistributions in binary form must reproduce the above
  13. // copyright notice, this list of conditions and the following disclaimer
  14. // in the documentation and/or other materials provided with the
  15. // distribution.
  16. // * Neither the name of Google Inc. nor the names of its
  17. // contributors may be used to endorse or promote products derived from
  18. // this software without specific prior written permission.
  19. //
  20. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. package proto_test
  32. import (
  33. "bytes"
  34. "errors"
  35. "io/ioutil"
  36. "math"
  37. "strings"
  38. "sync"
  39. "testing"
  40. "github.com/golang/protobuf/proto"
  41. proto3pb "github.com/golang/protobuf/proto/proto3_proto"
  42. pb "github.com/golang/protobuf/proto/test_proto"
  43. anypb "github.com/golang/protobuf/ptypes/any"
  44. )
  45. // textMessage implements the methods that allow it to marshal and unmarshal
  46. // itself as text.
  47. type textMessage struct {
  48. }
  49. func (*textMessage) MarshalText() ([]byte, error) {
  50. return []byte("custom"), nil
  51. }
  52. func (*textMessage) UnmarshalText(bytes []byte) error {
  53. if string(bytes) != "custom" {
  54. return errors.New("expected 'custom'")
  55. }
  56. return nil
  57. }
  58. func (*textMessage) Reset() {}
  59. func (*textMessage) String() string { return "" }
  60. func (*textMessage) ProtoMessage() {}
  61. func newTestMessage() *pb.MyMessage {
  62. msg := &pb.MyMessage{
  63. Count: proto.Int32(42),
  64. Name: proto.String("Dave"),
  65. Quote: proto.String(`"I didn't want to go."`),
  66. Pet: []string{"bunny", "kitty", "horsey"},
  67. Inner: &pb.InnerMessage{
  68. Host: proto.String("footrest.syd"),
  69. Port: proto.Int32(7001),
  70. Connected: proto.Bool(true),
  71. },
  72. Others: []*pb.OtherMessage{
  73. {
  74. Key: proto.Int64(0xdeadbeef),
  75. Value: []byte{1, 65, 7, 12},
  76. },
  77. {
  78. Weight: proto.Float32(6.022),
  79. Inner: &pb.InnerMessage{
  80. Host: proto.String("lesha.mtv"),
  81. Port: proto.Int32(8002),
  82. },
  83. },
  84. },
  85. Bikeshed: pb.MyMessage_BLUE.Enum(),
  86. Somegroup: &pb.MyMessage_SomeGroup{
  87. GroupField: proto.Int32(8),
  88. },
  89. // One normally wouldn't do this.
  90. // This is an undeclared tag 13, as a varint (wire type 0) with value 4.
  91. XXX_unrecognized: []byte{13<<3 | 0, 4},
  92. }
  93. ext := &pb.Ext{
  94. Data: proto.String("Big gobs for big rats"),
  95. }
  96. if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
  97. panic(err)
  98. }
  99. greetings := []string{"adg", "easy", "cow"}
  100. if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
  101. panic(err)
  102. }
  103. // Add an unknown extension. We marshal a pb.Ext, and fake the ID.
  104. b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")})
  105. if err != nil {
  106. panic(err)
  107. }
  108. b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
  109. proto.SetRawExtension(msg, 201, b)
  110. // Extensions can be plain fields, too, so let's test that.
  111. b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
  112. proto.SetRawExtension(msg, 202, b)
  113. return msg
  114. }
  115. const text = `count: 42
  116. name: "Dave"
  117. quote: "\"I didn't want to go.\""
  118. pet: "bunny"
  119. pet: "kitty"
  120. pet: "horsey"
  121. inner: <
  122. host: "footrest.syd"
  123. port: 7001
  124. connected: true
  125. >
  126. others: <
  127. key: 3735928559
  128. value: "\001A\007\014"
  129. >
  130. others: <
  131. weight: 6.022
  132. inner: <
  133. host: "lesha.mtv"
  134. port: 8002
  135. >
  136. >
  137. bikeshed: BLUE
  138. SomeGroup {
  139. group_field: 8
  140. }
  141. /* 2 unknown bytes */
  142. 13: 4
  143. [test_proto.Ext.more]: <
  144. data: "Big gobs for big rats"
  145. >
  146. [test_proto.greeting]: "adg"
  147. [test_proto.greeting]: "easy"
  148. [test_proto.greeting]: "cow"
  149. /* 13 unknown bytes */
  150. 201: "\t3G skiing"
  151. /* 3 unknown bytes */
  152. 202: 19
  153. `
  154. func TestMarshalText(t *testing.T) {
  155. buf := new(bytes.Buffer)
  156. if err := proto.MarshalText(buf, newTestMessage()); err != nil {
  157. t.Fatalf("proto.MarshalText: %v", err)
  158. }
  159. s := buf.String()
  160. if s != text {
  161. t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
  162. }
  163. }
  164. func TestMarshalTextCustomMessage(t *testing.T) {
  165. buf := new(bytes.Buffer)
  166. if err := proto.MarshalText(buf, &textMessage{}); err != nil {
  167. t.Fatalf("proto.MarshalText: %v", err)
  168. }
  169. s := buf.String()
  170. if s != "custom" {
  171. t.Errorf("Got %q, expected %q", s, "custom")
  172. }
  173. }
  174. func TestMarshalTextNil(t *testing.T) {
  175. want := "<nil>"
  176. tests := []proto.Message{nil, (*pb.MyMessage)(nil)}
  177. for i, test := range tests {
  178. buf := new(bytes.Buffer)
  179. if err := proto.MarshalText(buf, test); err != nil {
  180. t.Fatal(err)
  181. }
  182. if got := buf.String(); got != want {
  183. t.Errorf("%d: got %q want %q", i, got, want)
  184. }
  185. }
  186. }
  187. func TestMarshalTextUnknownEnum(t *testing.T) {
  188. // The Color enum only specifies values 0-2.
  189. m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()}
  190. got := m.String()
  191. const want = `bikeshed:3 `
  192. if got != want {
  193. t.Errorf("\n got %q\nwant %q", got, want)
  194. }
  195. }
  196. func TestTextOneof(t *testing.T) {
  197. tests := []struct {
  198. m proto.Message
  199. want string
  200. }{
  201. // zero message
  202. {&pb.Communique{}, ``},
  203. // scalar field
  204. {&pb.Communique{Union: &pb.Communique_Number{4}}, `number:4`},
  205. // message field
  206. {&pb.Communique{Union: &pb.Communique_Msg{
  207. &pb.Strings{StringField: proto.String("why hello!")},
  208. }}, `msg:<string_field:"why hello!" >`},
  209. // bad oneof (should not panic)
  210. {&pb.Communique{Union: &pb.Communique_Msg{nil}}, `msg:/* nil */`},
  211. }
  212. for _, test := range tests {
  213. got := strings.TrimSpace(test.m.String())
  214. if got != test.want {
  215. t.Errorf("\n got %s\nwant %s", got, test.want)
  216. }
  217. }
  218. }
  219. func BenchmarkMarshalTextBuffered(b *testing.B) {
  220. buf := new(bytes.Buffer)
  221. m := newTestMessage()
  222. for i := 0; i < b.N; i++ {
  223. buf.Reset()
  224. proto.MarshalText(buf, m)
  225. }
  226. }
  227. func BenchmarkMarshalTextUnbuffered(b *testing.B) {
  228. w := ioutil.Discard
  229. m := newTestMessage()
  230. for i := 0; i < b.N; i++ {
  231. proto.MarshalText(w, m)
  232. }
  233. }
  234. func compact(src string) string {
  235. // s/[ \n]+/ /g; s/ $//;
  236. dst := make([]byte, len(src))
  237. space, comment := false, false
  238. j := 0
  239. for i := 0; i < len(src); i++ {
  240. if strings.HasPrefix(src[i:], "/*") {
  241. comment = true
  242. i++
  243. continue
  244. }
  245. if comment && strings.HasPrefix(src[i:], "*/") {
  246. comment = false
  247. i++
  248. continue
  249. }
  250. if comment {
  251. continue
  252. }
  253. c := src[i]
  254. if c == ' ' || c == '\n' {
  255. space = true
  256. continue
  257. }
  258. if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
  259. space = false
  260. }
  261. if c == '{' {
  262. space = false
  263. }
  264. if space {
  265. dst[j] = ' '
  266. j++
  267. space = false
  268. }
  269. dst[j] = c
  270. j++
  271. }
  272. if space {
  273. dst[j] = ' '
  274. j++
  275. }
  276. return string(dst[0:j])
  277. }
  278. var compactText = compact(text)
  279. func TestCompactText(t *testing.T) {
  280. s := proto.CompactTextString(newTestMessage())
  281. if s != compactText {
  282. t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText)
  283. }
  284. }
  285. func TestStringEscaping(t *testing.T) {
  286. testCases := []struct {
  287. in *pb.Strings
  288. out string
  289. }{
  290. {
  291. // Test data from C++ test (TextFormatTest.StringEscape).
  292. // Single divergence: we don't escape apostrophes.
  293. &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")},
  294. "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n",
  295. },
  296. {
  297. // Test data from the same C++ test.
  298. &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")},
  299. "string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
  300. },
  301. {
  302. // Some UTF-8.
  303. &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
  304. `string_field: "\000\001\377\201"` + "\n",
  305. },
  306. }
  307. for i, tc := range testCases {
  308. var buf bytes.Buffer
  309. if err := proto.MarshalText(&buf, tc.in); err != nil {
  310. t.Errorf("proto.MarsalText: %v", err)
  311. continue
  312. }
  313. s := buf.String()
  314. if s != tc.out {
  315. t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
  316. continue
  317. }
  318. // Check round-trip.
  319. pb := new(pb.Strings)
  320. if err := proto.UnmarshalText(s, pb); err != nil {
  321. t.Errorf("#%d: UnmarshalText: %v", i, err)
  322. continue
  323. }
  324. if !proto.Equal(pb, tc.in) {
  325. t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb)
  326. }
  327. }
  328. }
  329. // A limitedWriter accepts some output before it fails.
  330. // This is a proxy for something like a nearly-full or imminently-failing disk,
  331. // or a network connection that is about to die.
  332. type limitedWriter struct {
  333. b bytes.Buffer
  334. limit int
  335. }
  336. var outOfSpace = errors.New("proto: insufficient space")
  337. func (w *limitedWriter) Write(p []byte) (n int, err error) {
  338. var avail = w.limit - w.b.Len()
  339. if avail <= 0 {
  340. return 0, outOfSpace
  341. }
  342. if len(p) <= avail {
  343. return w.b.Write(p)
  344. }
  345. n, _ = w.b.Write(p[:avail])
  346. return n, outOfSpace
  347. }
  348. func TestMarshalTextFailing(t *testing.T) {
  349. // Try lots of different sizes to exercise more error code-paths.
  350. for lim := 0; lim < len(text); lim++ {
  351. buf := new(limitedWriter)
  352. buf.limit = lim
  353. err := proto.MarshalText(buf, newTestMessage())
  354. // We expect a certain error, but also some partial results in the buffer.
  355. if err != outOfSpace {
  356. t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace)
  357. }
  358. s := buf.b.String()
  359. x := text[:buf.limit]
  360. if s != x {
  361. t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
  362. }
  363. }
  364. }
  365. func TestFloats(t *testing.T) {
  366. tests := []struct {
  367. f float64
  368. want string
  369. }{
  370. {0, "0"},
  371. {4.7, "4.7"},
  372. {math.Inf(1), "inf"},
  373. {math.Inf(-1), "-inf"},
  374. {math.NaN(), "nan"},
  375. }
  376. for _, test := range tests {
  377. msg := &pb.FloatingPoint{F: &test.f}
  378. got := strings.TrimSpace(msg.String())
  379. want := `f:` + test.want
  380. if got != want {
  381. t.Errorf("f=%f: got %q, want %q", test.f, got, want)
  382. }
  383. }
  384. }
  385. func TestRepeatedNilText(t *testing.T) {
  386. m := &pb.MessageList{
  387. Message: []*pb.MessageList_Message{
  388. nil,
  389. &pb.MessageList_Message{
  390. Name: proto.String("Horse"),
  391. },
  392. nil,
  393. },
  394. }
  395. want := `Message <nil>
  396. Message {
  397. name: "Horse"
  398. }
  399. Message <nil>
  400. `
  401. if s := proto.MarshalTextString(m); s != want {
  402. t.Errorf(" got: %s\nwant: %s", s, want)
  403. }
  404. }
  405. func TestProto3Text(t *testing.T) {
  406. tests := []struct {
  407. m proto.Message
  408. want string
  409. }{
  410. // zero message
  411. {&proto3pb.Message{}, ``},
  412. // zero message except for an empty byte slice
  413. {&proto3pb.Message{Data: []byte{}}, ``},
  414. // trivial case
  415. {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
  416. // empty map
  417. {&pb.MessageWithMap{}, ``},
  418. // non-empty map; map format is the same as a repeated struct,
  419. // and they are sorted by key (numerically for numeric keys).
  420. {
  421. &pb.MessageWithMap{NameMapping: map[int32]string{
  422. -1: "Negatory",
  423. 7: "Lucky",
  424. 1234: "Feist",
  425. 6345789: "Otis",
  426. }},
  427. `name_mapping:<key:-1 value:"Negatory" > ` +
  428. `name_mapping:<key:7 value:"Lucky" > ` +
  429. `name_mapping:<key:1234 value:"Feist" > ` +
  430. `name_mapping:<key:6345789 value:"Otis" >`,
  431. },
  432. // map with nil value; not well-defined, but we shouldn't crash
  433. {
  434. &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
  435. `msg_mapping:<key:7 >`,
  436. },
  437. }
  438. for _, test := range tests {
  439. got := strings.TrimSpace(test.m.String())
  440. if got != test.want {
  441. t.Errorf("\n got %s\nwant %s", got, test.want)
  442. }
  443. }
  444. }
  445. func TestRacyMarshal(t *testing.T) {
  446. // This test should be run with the race detector.
  447. any := &pb.MyMessage{Count: proto.Int32(47), Name: proto.String("David")}
  448. proto.SetExtension(any, pb.E_Ext_Text, proto.String("bar"))
  449. b, err := proto.Marshal(any)
  450. if err != nil {
  451. panic(err)
  452. }
  453. m := &proto3pb.Message{
  454. Name: "David",
  455. ResultCount: 47,
  456. Anything: &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any), Value: b},
  457. }
  458. wantText := proto.MarshalTextString(m)
  459. wantBytes, err := proto.Marshal(m)
  460. if err != nil {
  461. t.Fatalf("proto.Marshal error: %v", err)
  462. }
  463. var wg sync.WaitGroup
  464. defer wg.Wait()
  465. wg.Add(20)
  466. for i := 0; i < 10; i++ {
  467. go func() {
  468. defer wg.Done()
  469. got := proto.MarshalTextString(m)
  470. if got != wantText {
  471. t.Errorf("proto.MarshalTextString = %q, want %q", got, wantText)
  472. }
  473. }()
  474. go func() {
  475. defer wg.Done()
  476. got, err := proto.Marshal(m)
  477. if !bytes.Equal(got, wantBytes) || err != nil {
  478. t.Errorf("proto.Marshal = (%x, %v), want (%x, nil)", got, err, wantBytes)
  479. }
  480. }()
  481. }
  482. }