| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 1 | // 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 | |
| 32 | package jsonpb |
| 33 | |
| 34 | import ( |
| 35 | "testing" |
| 36 | |
| 37 | pb "github.com/golang/protobuf/jsonpb/jsonpb_test_proto" |
| 38 | "github.com/golang/protobuf/proto" |
| 39 | ) |
| 40 | |
| 41 | var ( |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 42 | marshaler = Marshaler{} |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 43 | |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 44 | marshalerAllOptions = Marshaler{ |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 45 | 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 Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 258 | var marshalingTests = []struct { |
| 259 | desc string |
| 260 | marshaler Marshaler |
| 261 | pb proto.Message |
| 262 | json string |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 263 | }{ |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 264 | {"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 Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 271 | &pb.Widget{Color: pb.Widget_BLUE.Enum()}, `{"color":"BLUE"}`}, |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 272 | {"enum-value pretty object", Marshaler{Indent: " "}, |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 273 | &pb.Widget{Color: pb.Widget_BLUE.Enum()}, colorPrettyJSON}, |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 274 | {"unknown enum value object", marshalerAllOptions, |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 275 | &pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}, colorListPrettyJSON}, |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 276 | {"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 Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 280 | &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}, |
| 281 | `{"strry":{"\"one\"":"two","three":"four"}}`}, |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 282 | {"map<int32, Object>", marshaler, |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 283 | &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}, `{"objjy":{"1":{"dub":1}}}`}, |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 284 | {"map<int32, Object>", marshalerAllOptions, |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 285 | &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}, objjyPrettyJSON}, |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 286 | {"map<int64, string>", marshaler, &pb.Mappy{Buggy: map[int64]string{1234: "yup"}}, |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 287 | `{"buggy":{"1234":"yup"}}`}, |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 288 | {"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 Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 290 | `{"m_int64_str":{"213":"cat"}}`}, |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 291 | {"proto2 map<bool, Object>", marshaler, |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 292 | &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 Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 296 | func TestMarshaling(t *testing.T) { |
| 297 | for _, tt := range marshalingTests { |
| 298 | json, err := tt.marshaler.MarshalToString(tt.pb) |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 299 | if err != nil { |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 300 | t.Errorf("%s: marshaling error: %v", tt.desc, err) |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 301 | } else if tt.json != json { |
| 302 | t.Errorf("%s: got [%v] want [%v]", tt.desc, json, tt.json) |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 307 | var unmarshalingTests = []struct { |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 308 | 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 Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 330 | func TestUnmarshaling(t *testing.T) { |
| 331 | for _, tt := range unmarshalingTests { |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 332 | // 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 Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 351 | var unmarshalingShouldError = []struct { |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 352 | desc string |
| 353 | in string |
| 354 | }{ |
| 355 | {"a value", "666"}, |
| 356 | {"gibberish", "{adskja123;l23=-="}, |
| 357 | } |
| 358 | |
| David Symonds | 21f8136 | 2015-08-17 11:59:00 +1000 | [diff] [blame^] | 359 | func TestUnmarshalingBadInput(t *testing.T) { |
| 360 | for _, tt := range unmarshalingShouldError { |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 361 | 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 | } |