any_test.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // Go support for Protocol Buffers - Google's data interchange format
  2. //
  3. // Copyright 2016 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. "strings"
  34. "testing"
  35. "github.com/golang/protobuf/proto"
  36. pb "github.com/golang/protobuf/proto/proto3_proto"
  37. testpb "github.com/golang/protobuf/proto/test_proto"
  38. anypb "github.com/golang/protobuf/ptypes/any"
  39. )
  40. var (
  41. expandedMarshaler = proto.TextMarshaler{ExpandAny: true}
  42. expandedCompactMarshaler = proto.TextMarshaler{Compact: true, ExpandAny: true}
  43. )
  44. // anyEqual reports whether two messages which may be google.protobuf.Any or may
  45. // contain google.protobuf.Any fields are equal. We can't use proto.Equal for
  46. // comparison, because semantically equivalent messages may be marshaled to
  47. // binary in different tag order. Instead, trust that TextMarshaler with
  48. // ExpandAny option works and compare the text marshaling results.
  49. func anyEqual(got, want proto.Message) bool {
  50. // if messages are proto.Equal, no need to marshal.
  51. if proto.Equal(got, want) {
  52. return true
  53. }
  54. g := expandedMarshaler.Text(got)
  55. w := expandedMarshaler.Text(want)
  56. return g == w
  57. }
  58. type golden struct {
  59. m proto.Message
  60. t, c string
  61. }
  62. var goldenMessages = makeGolden()
  63. func makeGolden() []golden {
  64. nested := &pb.Nested{Bunny: "Monty"}
  65. nb, err := proto.Marshal(nested)
  66. if err != nil {
  67. panic(err)
  68. }
  69. m1 := &pb.Message{
  70. Name: "David",
  71. ResultCount: 47,
  72. Anything: &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb},
  73. }
  74. m2 := &pb.Message{
  75. Name: "David",
  76. ResultCount: 47,
  77. Anything: &anypb.Any{TypeUrl: "http://[::1]/type.googleapis.com/" + proto.MessageName(nested), Value: nb},
  78. }
  79. m3 := &pb.Message{
  80. Name: "David",
  81. ResultCount: 47,
  82. Anything: &anypb.Any{TypeUrl: `type.googleapis.com/"/` + proto.MessageName(nested), Value: nb},
  83. }
  84. m4 := &pb.Message{
  85. Name: "David",
  86. ResultCount: 47,
  87. Anything: &anypb.Any{TypeUrl: "type.googleapis.com/a/path/" + proto.MessageName(nested), Value: nb},
  88. }
  89. m5 := &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb}
  90. any1 := &testpb.MyMessage{Count: proto.Int32(47), Name: proto.String("David")}
  91. proto.SetExtension(any1, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("foo")})
  92. proto.SetExtension(any1, testpb.E_Ext_Text, proto.String("bar"))
  93. any1b, err := proto.Marshal(any1)
  94. if err != nil {
  95. panic(err)
  96. }
  97. any2 := &testpb.MyMessage{Count: proto.Int32(42), Bikeshed: testpb.MyMessage_GREEN.Enum(), RepBytes: [][]byte{[]byte("roboto")}}
  98. proto.SetExtension(any2, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("baz")})
  99. any2b, err := proto.Marshal(any2)
  100. if err != nil {
  101. panic(err)
  102. }
  103. m6 := &pb.Message{
  104. Name: "David",
  105. ResultCount: 47,
  106. Anything: &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
  107. ManyThings: []*anypb.Any{
  108. &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any2), Value: any2b},
  109. &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
  110. },
  111. }
  112. const (
  113. m1Golden = `
  114. name: "David"
  115. result_count: 47
  116. anything: <
  117. [type.googleapis.com/proto3_proto.Nested]: <
  118. bunny: "Monty"
  119. >
  120. >
  121. `
  122. m2Golden = `
  123. name: "David"
  124. result_count: 47
  125. anything: <
  126. ["http://[::1]/type.googleapis.com/proto3_proto.Nested"]: <
  127. bunny: "Monty"
  128. >
  129. >
  130. `
  131. m3Golden = `
  132. name: "David"
  133. result_count: 47
  134. anything: <
  135. ["type.googleapis.com/\"/proto3_proto.Nested"]: <
  136. bunny: "Monty"
  137. >
  138. >
  139. `
  140. m4Golden = `
  141. name: "David"
  142. result_count: 47
  143. anything: <
  144. [type.googleapis.com/a/path/proto3_proto.Nested]: <
  145. bunny: "Monty"
  146. >
  147. >
  148. `
  149. m5Golden = `
  150. [type.googleapis.com/proto3_proto.Nested]: <
  151. bunny: "Monty"
  152. >
  153. `
  154. m6Golden = `
  155. name: "David"
  156. result_count: 47
  157. anything: <
  158. [type.googleapis.com/test_proto.MyMessage]: <
  159. count: 47
  160. name: "David"
  161. [test_proto.Ext.more]: <
  162. data: "foo"
  163. >
  164. [test_proto.Ext.text]: "bar"
  165. >
  166. >
  167. many_things: <
  168. [type.googleapis.com/test_proto.MyMessage]: <
  169. count: 42
  170. bikeshed: GREEN
  171. rep_bytes: "roboto"
  172. [test_proto.Ext.more]: <
  173. data: "baz"
  174. >
  175. >
  176. >
  177. many_things: <
  178. [type.googleapis.com/test_proto.MyMessage]: <
  179. count: 47
  180. name: "David"
  181. [test_proto.Ext.more]: <
  182. data: "foo"
  183. >
  184. [test_proto.Ext.text]: "bar"
  185. >
  186. >
  187. `
  188. )
  189. return []golden{
  190. {m1, strings.TrimSpace(m1Golden) + "\n", strings.TrimSpace(compact(m1Golden)) + " "},
  191. {m2, strings.TrimSpace(m2Golden) + "\n", strings.TrimSpace(compact(m2Golden)) + " "},
  192. {m3, strings.TrimSpace(m3Golden) + "\n", strings.TrimSpace(compact(m3Golden)) + " "},
  193. {m4, strings.TrimSpace(m4Golden) + "\n", strings.TrimSpace(compact(m4Golden)) + " "},
  194. {m5, strings.TrimSpace(m5Golden) + "\n", strings.TrimSpace(compact(m5Golden)) + " "},
  195. {m6, strings.TrimSpace(m6Golden) + "\n", strings.TrimSpace(compact(m6Golden)) + " "},
  196. }
  197. }
  198. func TestMarshalGolden(t *testing.T) {
  199. for _, tt := range goldenMessages {
  200. if got, want := expandedMarshaler.Text(tt.m), tt.t; got != want {
  201. t.Errorf("message %v: got:\n%s\nwant:\n%s", tt.m, got, want)
  202. }
  203. if got, want := expandedCompactMarshaler.Text(tt.m), tt.c; got != want {
  204. t.Errorf("message %v: got:\n`%s`\nwant:\n`%s`", tt.m, got, want)
  205. }
  206. }
  207. }
  208. func TestUnmarshalGolden(t *testing.T) {
  209. for _, tt := range goldenMessages {
  210. want := tt.m
  211. got := proto.Clone(tt.m)
  212. got.Reset()
  213. if err := proto.UnmarshalText(tt.t, got); err != nil {
  214. t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.t, err)
  215. }
  216. if !anyEqual(got, want) {
  217. t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.t, got, want)
  218. }
  219. got.Reset()
  220. if err := proto.UnmarshalText(tt.c, got); err != nil {
  221. t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.c, err)
  222. }
  223. if !anyEqual(got, want) {
  224. t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.c, got, want)
  225. }
  226. }
  227. }
  228. func TestMarshalUnknownAny(t *testing.T) {
  229. m := &pb.Message{
  230. Anything: &anypb.Any{
  231. TypeUrl: "foo",
  232. Value: []byte("bar"),
  233. },
  234. }
  235. want := `anything: <
  236. type_url: "foo"
  237. value: "bar"
  238. >
  239. `
  240. got := expandedMarshaler.Text(m)
  241. if got != want {
  242. t.Errorf("got\n`%s`\nwant\n`%s`", got, want)
  243. }
  244. }
  245. func TestAmbiguousAny(t *testing.T) {
  246. pb := &anypb.Any{}
  247. err := proto.UnmarshalText(`
  248. type_url: "ttt/proto3_proto.Nested"
  249. value: "\n\x05Monty"
  250. `, pb)
  251. t.Logf("result: %v (error: %v)", expandedMarshaler.Text(pb), err)
  252. if err != nil {
  253. t.Errorf("failed to parse ambiguous Any message: %v", err)
  254. }
  255. }
  256. func TestUnmarshalOverwriteAny(t *testing.T) {
  257. pb := &anypb.Any{}
  258. err := proto.UnmarshalText(`
  259. [type.googleapis.com/a/path/proto3_proto.Nested]: <
  260. bunny: "Monty"
  261. >
  262. [type.googleapis.com/a/path/proto3_proto.Nested]: <
  263. bunny: "Rabbit of Caerbannog"
  264. >
  265. `, pb)
  266. want := `line 7: Any message unpacked multiple times, or "type_url" already set`
  267. if err.Error() != want {
  268. t.Errorf("incorrect error.\nHave: %v\nWant: %v", err.Error(), want)
  269. }
  270. }
  271. func TestUnmarshalAnyMixAndMatch(t *testing.T) {
  272. pb := &anypb.Any{}
  273. err := proto.UnmarshalText(`
  274. value: "\n\x05Monty"
  275. [type.googleapis.com/a/path/proto3_proto.Nested]: <
  276. bunny: "Rabbit of Caerbannog"
  277. >
  278. `, pb)
  279. want := `line 5: Any message unpacked multiple times, or "value" already set`
  280. if err.Error() != want {
  281. t.Errorf("incorrect error.\nHave: %v\nWant: %v", err.Error(), want)
  282. }
  283. }