blob: 180d5d2f5301ad6e211d6e2e2e53832982702c50 [file] [log] [blame]
Buck Clay67cbcad2015-07-01 11:17:11 +10001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2015 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
32package jsonpb
33
34import (
35 "testing"
36
37 pb "github.com/golang/protobuf/jsonpb/jsonpb_test_proto"
38 "github.com/golang/protobuf/proto"
39)
40
41var (
David Symonds21f81362015-08-17 11:59:00 +100042 marshaler = Marshaler{}
Buck Clay67cbcad2015-07-01 11:17:11 +100043
David Symonds21f81362015-08-17 11:59:00 +100044 marshalerAllOptions = Marshaler{
Buck Clay67cbcad2015-07-01 11:17:11 +100045 EnumsAsString: true,
46 Indent: " ",
47 }
48
49 simpleObject = &pb.Simple{
50 OInt32: proto.Int32(-32),
51 OInt64: proto.Int64(-6400000000),
52 OUint32: proto.Uint32(32),
53 OUint64: proto.Uint64(6400000000),
54 OSint32: proto.Int32(-13),
55 OSint64: proto.Int64(-2600000000),
56 OFloat: proto.Float32(3.14),
57 ODouble: proto.Float64(6.02214179e23),
58 OBool: proto.Bool(true),
59 OString: proto.String("hello \"there\""),
60 OBytes: []byte("beep boop"),
61 }
62
63 simpleObjectJSON = `{` +
64 `"o_bool":true,` +
65 `"o_int32":-32,` +
66 `"o_int64":"-6400000000",` +
67 `"o_uint32":32,` +
68 `"o_uint64":"6400000000",` +
69 `"o_sint32":-13,` +
70 `"o_sint64":"-2600000000",` +
71 `"o_float":3.14,` +
72 `"o_double":6.02214179e+23,` +
73 `"o_string":"hello \"there\"",` +
74 `"o_bytes":"YmVlcCBib29w"` +
75 `}`
76
77 simpleObjectPrettyJSON = `{
78 "o_bool": true,
79 "o_int32": -32,
80 "o_int64": "-6400000000",
81 "o_uint32": 32,
82 "o_uint64": "6400000000",
83 "o_sint32": -13,
84 "o_sint64": "-2600000000",
85 "o_float": 3.14,
86 "o_double": 6.02214179e+23,
87 "o_string": "hello \"there\"",
88 "o_bytes": "YmVlcCBib29w"
89}`
90
91 repeatsObject = &pb.Repeats{
92 RBool: []bool{true, false, true},
93 RInt32: []int32{-3, -4, -5},
94 RInt64: []int64{-123456789, -987654321},
95 RUint32: []uint32{1, 2, 3},
96 RUint64: []uint64{6789012345, 3456789012},
97 RSint32: []int32{-1, -2, -3},
98 RSint64: []int64{-6789012345, -3456789012},
99 RFloat: []float32{3.14, 6.28},
100 RDouble: []float64{299792458, 6.62606957e-34},
101 RString: []string{"happy", "days"},
102 RBytes: [][]byte{[]byte("skittles"), []byte("m&m's")},
103 }
104
105 repeatsObjectJSON = `{` +
106 `"r_bool":[true,false,true],` +
107 `"r_int32":[-3,-4,-5],` +
108 `"r_int64":["-123456789","-987654321"],` +
109 `"r_uint32":[1,2,3],` +
110 `"r_uint64":["6789012345","3456789012"],` +
111 `"r_sint32":[-1,-2,-3],` +
112 `"r_sint64":["-6789012345","-3456789012"],` +
113 `"r_float":[3.14,6.28],` +
114 `"r_double":[2.99792458e+08,6.62606957e-34],` +
115 `"r_string":["happy","days"],` +
116 `"r_bytes":["c2tpdHRsZXM=","bSZtJ3M="]` +
117 `}`
118
119 repeatsObjectPrettyJSON = `{
120 "r_bool": [
121 true,
122 false,
123 true
124 ],
125 "r_int32": [
126 -3,
127 -4,
128 -5
129 ],
130 "r_int64": [
131 "-123456789",
132 "-987654321"
133 ],
134 "r_uint32": [
135 1,
136 2,
137 3
138 ],
139 "r_uint64": [
140 "6789012345",
141 "3456789012"
142 ],
143 "r_sint32": [
144 -1,
145 -2,
146 -3
147 ],
148 "r_sint64": [
149 "-6789012345",
150 "-3456789012"
151 ],
152 "r_float": [
153 3.14,
154 6.28
155 ],
156 "r_double": [
157 2.99792458e+08,
158 6.62606957e-34
159 ],
160 "r_string": [
161 "happy",
162 "days"
163 ],
164 "r_bytes": [
165 "c2tpdHRsZXM=",
166 "bSZtJ3M="
167 ]
168}`
169
170 innerSimple = &pb.Simple{OInt32: proto.Int32(-32)}
171 innerSimple2 = &pb.Simple{OInt64: proto.Int64(25)}
172 innerRepeats = &pb.Repeats{RString: []string{"roses", "red"}}
173 innerRepeats2 = &pb.Repeats{RString: []string{"violets", "blue"}}
174 complexObject = &pb.Widget{
175 Color: pb.Widget_GREEN.Enum(),
176 RColor: []pb.Widget_Color{pb.Widget_RED, pb.Widget_GREEN, pb.Widget_BLUE},
177 Simple: innerSimple,
178 RSimple: []*pb.Simple{innerSimple, innerSimple2},
179 Repeats: innerRepeats,
180 RRepeats: []*pb.Repeats{innerRepeats, innerRepeats2},
181 }
182
183 complexObjectJSON = `{"color":1,` +
184 `"r_color":[0,1,2],` +
185 `"simple":{"o_int32":-32},` +
186 `"r_simple":[{"o_int32":-32},{"o_int64":"25"}],` +
187 `"repeats":{"r_string":["roses","red"]},` +
188 `"r_repeats":[{"r_string":["roses","red"]},{"r_string":["violets","blue"]}]` +
189 `}`
190
191 complexObjectPrettyJSON = `{
192 "color": "GREEN",
193 "r_color": [
194 "RED",
195 "GREEN",
196 "BLUE"
197 ],
198 "simple": {
199 "o_int32": -32
200 },
201 "r_simple": [
202 {
203 "o_int32": -32
204 },
205 {
206 "o_int64": "25"
207 }
208 ],
209 "repeats": {
210 "r_string": [
211 "roses",
212 "red"
213 ]
214 },
215 "r_repeats": [
216 {
217 "r_string": [
218 "roses",
219 "red"
220 ]
221 },
222 {
223 "r_string": [
224 "violets",
225 "blue"
226 ]
227 }
228 ]
229}`
230
231 colorPrettyJSON = `{
232 "color": 2
233}`
234
235 colorListPrettyJSON = `{
236 "color": 1000,
237 "r_color": [
238 "RED"
239 ]
240}`
241
242 nummyPrettyJSON = `{
243 "nummy": {
244 "1": 2,
245 "3": 4
246 }
247}`
248
249 objjyPrettyJSON = `{
250 "objjy": {
251 "1": {
252 "dub": 1
253 }
254 }
255}`
256)
257
David Symonds21f81362015-08-17 11:59:00 +1000258var marshalingTests = []struct {
259 desc string
260 marshaler Marshaler
261 pb proto.Message
262 json string
Buck Clay67cbcad2015-07-01 11:17:11 +1000263}{
David Symonds21f81362015-08-17 11:59:00 +1000264 {"simple flat object", marshaler, simpleObject, simpleObjectJSON},
265 {"simple pretty object", marshalerAllOptions, simpleObject, simpleObjectPrettyJSON},
266 {"repeated fields flat object", marshaler, repeatsObject, repeatsObjectJSON},
267 {"repeated fields pretty object", marshalerAllOptions, repeatsObject, repeatsObjectPrettyJSON},
268 {"nested message/enum flat object", marshaler, complexObject, complexObjectJSON},
269 {"nested message/enum pretty object", marshalerAllOptions, complexObject, complexObjectPrettyJSON},
270 {"enum-string flat object", Marshaler{EnumsAsString: true},
Buck Clay67cbcad2015-07-01 11:17:11 +1000271 &pb.Widget{Color: pb.Widget_BLUE.Enum()}, `{"color":"BLUE"}`},
David Symonds21f81362015-08-17 11:59:00 +1000272 {"enum-value pretty object", Marshaler{Indent: " "},
Buck Clay67cbcad2015-07-01 11:17:11 +1000273 &pb.Widget{Color: pb.Widget_BLUE.Enum()}, colorPrettyJSON},
David Symonds21f81362015-08-17 11:59:00 +1000274 {"unknown enum value object", marshalerAllOptions,
Buck Clay67cbcad2015-07-01 11:17:11 +1000275 &pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}, colorListPrettyJSON},
David Symonds21f81362015-08-17 11:59:00 +1000276 {"proto3 object with empty value", marshaler, &pb.Simple3{}, `{"dub":0}`},
277 {"map<int64, int32>", marshaler, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}, `{"nummy":{"1":2,"3":4}}`},
278 {"map<int64, int32>", marshalerAllOptions, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}, nummyPrettyJSON},
279 {"map<string, string>", marshaler,
Buck Clay67cbcad2015-07-01 11:17:11 +1000280 &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}},
281 `{"strry":{"\"one\"":"two","three":"four"}}`},
David Symonds21f81362015-08-17 11:59:00 +1000282 {"map<int32, Object>", marshaler,
Buck Clay67cbcad2015-07-01 11:17:11 +1000283 &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}, `{"objjy":{"1":{"dub":1}}}`},
David Symonds21f81362015-08-17 11:59:00 +1000284 {"map<int32, Object>", marshalerAllOptions,
Buck Clay67cbcad2015-07-01 11:17:11 +1000285 &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}, objjyPrettyJSON},
David Symonds21f81362015-08-17 11:59:00 +1000286 {"map<int64, string>", marshaler, &pb.Mappy{Buggy: map[int64]string{1234: "yup"}},
Buck Clay67cbcad2015-07-01 11:17:11 +1000287 `{"buggy":{"1234":"yup"}}`},
David Symonds21f81362015-08-17 11:59:00 +1000288 {"map<bool, bool>", marshaler, &pb.Mappy{Booly: map[bool]bool{false: true}}, `{"booly":{"false":true}}`},
289 {"proto2 map<int64, string>", marshaler, &pb.Maps{MInt64Str: map[int64]string{213: "cat"}},
Buck Clay67cbcad2015-07-01 11:17:11 +1000290 `{"m_int64_str":{"213":"cat"}}`},
David Symonds21f81362015-08-17 11:59:00 +1000291 {"proto2 map<bool, Object>", marshaler,
Buck Clay67cbcad2015-07-01 11:17:11 +1000292 &pb.Maps{MBoolSimple: map[bool]*pb.Simple{true: &pb.Simple{OInt32: proto.Int32(1)}}},
293 `{"m_bool_simple":{"true":{"o_int32":1}}}`},
294}
295
David Symonds21f81362015-08-17 11:59:00 +1000296func TestMarshaling(t *testing.T) {
297 for _, tt := range marshalingTests {
298 json, err := tt.marshaler.MarshalToString(tt.pb)
Buck Clay67cbcad2015-07-01 11:17:11 +1000299 if err != nil {
David Symonds21f81362015-08-17 11:59:00 +1000300 t.Errorf("%s: marshaling error: %v", tt.desc, err)
Buck Clay67cbcad2015-07-01 11:17:11 +1000301 } else if tt.json != json {
302 t.Errorf("%s: got [%v] want [%v]", tt.desc, json, tt.json)
303 }
304 }
305}
306
David Symonds21f81362015-08-17 11:59:00 +1000307var unmarshalingTests = []struct {
Buck Clay67cbcad2015-07-01 11:17:11 +1000308 desc string
309 json string
310 pb proto.Message
311}{
312 {"simple flat object", simpleObjectJSON, simpleObject},
313 {"simple pretty object", simpleObjectPrettyJSON, simpleObject},
314 {"repeated fields flat object", repeatsObjectJSON, repeatsObject},
315 {"repeated fields pretty object", repeatsObjectPrettyJSON, repeatsObject},
316 {"nested message/enum flat object", complexObjectJSON, complexObject},
317 {"nested message/enum pretty object", complexObjectPrettyJSON, complexObject},
318 {"enum-string object", `{"color":"BLUE"}`, &pb.Widget{Color: pb.Widget_BLUE.Enum()}},
319 {"enum-value object", "{\n \"color\": 2\n}", &pb.Widget{Color: pb.Widget_BLUE.Enum()}},
320 {"unknown enum value object",
321 "{\n \"color\": 1000,\n \"r_color\": [\n \"RED\"\n ]\n}",
322 &pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}},
323 {"unquoted int64 object", `{"o_int64":-314}`, &pb.Simple{OInt64: proto.Int64(-314)}},
324 {"unquoted uint64 object", `{"o_uint64":123}`, &pb.Simple{OUint64: proto.Uint64(123)}},
325 {"map<int64, int32>", `{"nummy":{"1":2,"3":4}}`, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}},
326 {"map<string, string>", `{"strry":{"\"one\"":"two","three":"four"}}`, &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}},
327 {"map<int32, Object>", `{"objjy":{"1":{"dub":1}}}`, &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}},
328}
329
David Symonds21f81362015-08-17 11:59:00 +1000330func TestUnmarshaling(t *testing.T) {
331 for _, tt := range unmarshalingTests {
Buck Clay67cbcad2015-07-01 11:17:11 +1000332 // Make a new instance of the type of our expected object.
333 p := proto.Clone(tt.pb)
334 p.Reset()
335
336 err := UnmarshalString(tt.json, p)
337 if err != nil {
338 t.Error(err)
339 continue
340 }
341
342 // For easier diffs, compare text strings of the protos.
343 exp := proto.MarshalTextString(tt.pb)
344 act := proto.MarshalTextString(p)
345 if string(exp) != string(act) {
346 t.Errorf("%s: got [%s] want [%s]", tt.desc, act, exp)
347 }
348 }
349}
350
David Symonds21f81362015-08-17 11:59:00 +1000351var unmarshalingShouldError = []struct {
Buck Clay67cbcad2015-07-01 11:17:11 +1000352 desc string
353 in string
354}{
355 {"a value", "666"},
356 {"gibberish", "{adskja123;l23=-="},
357}
358
David Symonds21f81362015-08-17 11:59:00 +1000359func TestUnmarshalingBadInput(t *testing.T) {
360 for _, tt := range unmarshalingShouldError {
Buck Clay67cbcad2015-07-01 11:17:11 +1000361 obj := &pb.Simple{}
362 err := UnmarshalString(tt.in, obj)
363 if err == nil {
364 t.Errorf("an error was expected when parsing %q instead of an object", tt.desc)
365 }
366 }
367}