1// Copyright 2011 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 5package proto_test 6 7import ( 8 "testing" 9 10 "github.com/golang/protobuf/proto" 11 12 pb2 "github.com/golang/protobuf/internal/testprotos/proto2_proto" 13 pb3 "github.com/golang/protobuf/internal/testprotos/proto3_proto" 14) 15 16var cloneTestMessage = &pb2.MyMessage{ 17 Count: proto.Int32(42), 18 Name: proto.String("Dave"), 19 Pet: []string{"bunny", "kitty", "horsey"}, 20 Inner: &pb2.InnerMessage{ 21 Host: proto.String("niles"), 22 Port: proto.Int32(9099), 23 Connected: proto.Bool(true), 24 }, 25 Others: []*pb2.OtherMessage{ 26 { 27 Value: []byte("some bytes"), 28 }, 29 }, 30 Somegroup: &pb2.MyMessage_SomeGroup{ 31 GroupField: proto.Int32(6), 32 }, 33 RepBytes: [][]byte{[]byte("sham"), []byte("wow")}, 34} 35 36func init() { 37 ext := &pb2.Ext{ 38 Data: proto.String("extension"), 39 } 40 if err := proto.SetExtension(cloneTestMessage, pb2.E_Ext_More, ext); err != nil { 41 panic("SetExtension: " + err.Error()) 42 } 43 if err := proto.SetExtension(cloneTestMessage, pb2.E_Ext_Text, proto.String("hello")); err != nil { 44 panic("SetExtension: " + err.Error()) 45 } 46 if err := proto.SetExtension(cloneTestMessage, pb2.E_Greeting, []string{"one", "two"}); err != nil { 47 panic("SetExtension: " + err.Error()) 48 } 49} 50 51func TestClone(t *testing.T) { 52 // Create a clone using a marshal/unmarshal roundtrip. 53 vanilla := new(pb2.MyMessage) 54 b, err := proto.Marshal(cloneTestMessage) 55 if err != nil { 56 t.Errorf("unexpected Marshal error: %v", err) 57 } 58 if err := proto.Unmarshal(b, vanilla); err != nil { 59 t.Errorf("unexpected Unarshal error: %v", err) 60 } 61 62 // Create a clone using Clone and verify that it is equal to the original. 63 m := proto.Clone(cloneTestMessage).(*pb2.MyMessage) 64 if !proto.Equal(m, cloneTestMessage) { 65 t.Fatalf("Clone(%v) = %v", cloneTestMessage, m) 66 } 67 68 // Mutate the clone, which should not affect the original. 69 x1, err := proto.GetExtension(m, pb2.E_Ext_More) 70 if err != nil { 71 t.Errorf("unexpected GetExtension(%v) error: %v", pb2.E_Ext_More.Name, err) 72 } 73 x2, err := proto.GetExtension(m, pb2.E_Ext_Text) 74 if err != nil { 75 t.Errorf("unexpected GetExtension(%v) error: %v", pb2.E_Ext_Text.Name, err) 76 } 77 x3, err := proto.GetExtension(m, pb2.E_Greeting) 78 if err != nil { 79 t.Errorf("unexpected GetExtension(%v) error: %v", pb2.E_Greeting.Name, err) 80 } 81 *m.Inner.Port++ 82 *(x1.(*pb2.Ext)).Data = "blah blah" 83 *(x2.(*string)) = "goodbye" 84 x3.([]string)[0] = "zero" 85 if !proto.Equal(cloneTestMessage, vanilla) { 86 t.Fatalf("mutation on original detected:\ngot %v\nwant %v", cloneTestMessage, vanilla) 87 } 88} 89 90func TestCloneNil(t *testing.T) { 91 var m *pb2.MyMessage 92 if c := proto.Clone(m); !proto.Equal(m, c) { 93 t.Errorf("Clone(%v) = %v", m, c) 94 } 95} 96 97var mergeTests = []struct { 98 src, dst, want proto.Message 99}{ 100 { 101 src: &pb2.MyMessage{ 102 Count: proto.Int32(42), 103 }, 104 dst: &pb2.MyMessage{ 105 Name: proto.String("Dave"), 106 }, 107 want: &pb2.MyMessage{ 108 Count: proto.Int32(42), 109 Name: proto.String("Dave"), 110 }, 111 }, 112 { 113 src: &pb2.MyMessage{ 114 Inner: &pb2.InnerMessage{ 115 Host: proto.String("hey"), 116 Connected: proto.Bool(true), 117 }, 118 Pet: []string{"horsey"}, 119 Others: []*pb2.OtherMessage{ 120 { 121 Value: []byte("some bytes"), 122 }, 123 }, 124 }, 125 dst: &pb2.MyMessage{ 126 Inner: &pb2.InnerMessage{ 127 Host: proto.String("niles"), 128 Port: proto.Int32(9099), 129 }, 130 Pet: []string{"bunny", "kitty"}, 131 Others: []*pb2.OtherMessage{ 132 { 133 Key: proto.Int64(31415926535), 134 }, 135 { 136 // Explicitly test a src=nil field 137 Inner: nil, 138 }, 139 }, 140 }, 141 want: &pb2.MyMessage{ 142 Inner: &pb2.InnerMessage{ 143 Host: proto.String("hey"), 144 Connected: proto.Bool(true), 145 Port: proto.Int32(9099), 146 }, 147 Pet: []string{"bunny", "kitty", "horsey"}, 148 Others: []*pb2.OtherMessage{ 149 { 150 Key: proto.Int64(31415926535), 151 }, 152 {}, 153 { 154 Value: []byte("some bytes"), 155 }, 156 }, 157 }, 158 }, 159 { 160 src: &pb2.MyMessage{ 161 RepBytes: [][]byte{[]byte("wow")}, 162 }, 163 dst: &pb2.MyMessage{ 164 Somegroup: &pb2.MyMessage_SomeGroup{ 165 GroupField: proto.Int32(6), 166 }, 167 RepBytes: [][]byte{[]byte("sham")}, 168 }, 169 want: &pb2.MyMessage{ 170 Somegroup: &pb2.MyMessage_SomeGroup{ 171 GroupField: proto.Int32(6), 172 }, 173 RepBytes: [][]byte{[]byte("sham"), []byte("wow")}, 174 }, 175 }, 176 // Check that a scalar bytes field replaces rather than appends. 177 { 178 src: &pb2.OtherMessage{Value: []byte("foo")}, 179 dst: &pb2.OtherMessage{Value: []byte("bar")}, 180 want: &pb2.OtherMessage{Value: []byte("foo")}, 181 }, 182 { 183 src: &pb2.MessageWithMap{ 184 NameMapping: map[int32]string{6: "Nigel"}, 185 MsgMapping: map[int64]*pb2.FloatingPoint{ 186 0x4001: &pb2.FloatingPoint{F: proto.Float64(2.0)}, 187 0x4002: &pb2.FloatingPoint{ 188 F: proto.Float64(2.0), 189 }, 190 }, 191 ByteMapping: map[bool][]byte{true: []byte("wowsa")}, 192 }, 193 dst: &pb2.MessageWithMap{ 194 NameMapping: map[int32]string{ 195 6: "Bruce", // should be overwritten 196 7: "Andrew", 197 }, 198 MsgMapping: map[int64]*pb2.FloatingPoint{ 199 0x4002: &pb2.FloatingPoint{ 200 F: proto.Float64(3.0), 201 Exact: proto.Bool(true), 202 }, // the entire message should be overwritten 203 }, 204 }, 205 want: &pb2.MessageWithMap{ 206 NameMapping: map[int32]string{ 207 6: "Nigel", 208 7: "Andrew", 209 }, 210 MsgMapping: map[int64]*pb2.FloatingPoint{ 211 0x4001: &pb2.FloatingPoint{F: proto.Float64(2.0)}, 212 0x4002: &pb2.FloatingPoint{ 213 F: proto.Float64(2.0), 214 }, 215 }, 216 ByteMapping: map[bool][]byte{true: []byte("wowsa")}, 217 }, 218 }, 219 // proto3 shouldn't merge zero values, 220 // in the same way that proto2 shouldn't merge nils. 221 { 222 src: &pb3.Message{ 223 Name: "Aaron", 224 Data: []byte(""), // zero value, but not nil 225 }, 226 dst: &pb3.Message{ 227 HeightInCm: 176, 228 Data: []byte("texas!"), 229 }, 230 want: &pb3.Message{ 231 Name: "Aaron", 232 HeightInCm: 176, 233 Data: []byte("texas!"), 234 }, 235 }, 236 { // Oneof fields should merge by assignment. 237 src: &pb2.Communique{Union: &pb2.Communique_Number{41}}, 238 dst: &pb2.Communique{Union: &pb2.Communique_Name{"Bobby Tables"}}, 239 want: &pb2.Communique{Union: &pb2.Communique_Number{41}}, 240 }, 241 { // Oneof nil is the same as not set. 242 src: &pb2.Communique{}, 243 dst: &pb2.Communique{Union: &pb2.Communique_Name{"Bobby Tables"}}, 244 want: &pb2.Communique{Union: &pb2.Communique_Name{"Bobby Tables"}}, 245 }, 246 { 247 src: &pb2.Communique{Union: &pb2.Communique_Number{1337}}, 248 dst: &pb2.Communique{}, 249 want: &pb2.Communique{Union: &pb2.Communique_Number{1337}}, 250 }, 251 { 252 src: &pb2.Communique{Union: &pb2.Communique_Col{pb2.MyMessage_RED}}, 253 dst: &pb2.Communique{}, 254 want: &pb2.Communique{Union: &pb2.Communique_Col{pb2.MyMessage_RED}}, 255 }, 256 { 257 src: &pb2.Communique{Union: &pb2.Communique_Data{[]byte("hello")}}, 258 dst: &pb2.Communique{}, 259 want: &pb2.Communique{Union: &pb2.Communique_Data{[]byte("hello")}}, 260 }, 261 { 262 src: &pb2.Communique{Union: &pb2.Communique_Msg{&pb2.Strings{BytesField: []byte{1, 2, 3}}}}, 263 dst: &pb2.Communique{}, 264 want: &pb2.Communique{Union: &pb2.Communique_Msg{&pb2.Strings{BytesField: []byte{1, 2, 3}}}}, 265 }, 266 { 267 src: &pb2.Communique{Union: &pb2.Communique_Msg{}}, 268 dst: &pb2.Communique{}, 269 want: &pb2.Communique{Union: &pb2.Communique_Msg{}}, 270 }, 271 { 272 src: &pb2.Communique{Union: &pb2.Communique_Msg{&pb2.Strings{StringField: proto.String("123")}}}, 273 dst: &pb2.Communique{Union: &pb2.Communique_Msg{&pb2.Strings{BytesField: []byte{1, 2, 3}}}}, 274 want: &pb2.Communique{Union: &pb2.Communique_Msg{&pb2.Strings{StringField: proto.String("123"), BytesField: []byte{1, 2, 3}}}}, 275 }, 276 { 277 src: &pb3.Message{ 278 Terrain: map[string]*pb3.Nested{ 279 "kay_a": &pb3.Nested{Cute: true}, // replace 280 "kay_b": &pb3.Nested{Bunny: "rabbit"}, // insert 281 }, 282 }, 283 dst: &pb3.Message{ 284 Terrain: map[string]*pb3.Nested{ 285 "kay_a": &pb3.Nested{Bunny: "lost"}, // replaced 286 "kay_c": &pb3.Nested{Bunny: "bunny"}, // keep 287 }, 288 }, 289 want: &pb3.Message{ 290 Terrain: map[string]*pb3.Nested{ 291 "kay_a": &pb3.Nested{Cute: true}, 292 "kay_b": &pb3.Nested{Bunny: "rabbit"}, 293 "kay_c": &pb3.Nested{Bunny: "bunny"}, 294 }, 295 }, 296 }, 297 { 298 src: &pb2.GoTest{ 299 F_BoolRepeated: []bool{}, 300 F_Int32Repeated: []int32{}, 301 F_Int64Repeated: []int64{}, 302 F_Uint32Repeated: []uint32{}, 303 F_Uint64Repeated: []uint64{}, 304 F_FloatRepeated: []float32{}, 305 F_DoubleRepeated: []float64{}, 306 F_StringRepeated: []string{}, 307 F_BytesRepeated: [][]byte{}, 308 }, 309 dst: &pb2.GoTest{}, 310 want: &pb2.GoTest{ 311 F_BoolRepeated: []bool{}, 312 F_Int32Repeated: []int32{}, 313 F_Int64Repeated: []int64{}, 314 F_Uint32Repeated: []uint32{}, 315 F_Uint64Repeated: []uint64{}, 316 F_FloatRepeated: []float32{}, 317 F_DoubleRepeated: []float64{}, 318 F_StringRepeated: []string{}, 319 F_BytesRepeated: [][]byte{}, 320 }, 321 }, 322 { 323 src: &pb2.GoTest{}, 324 dst: &pb2.GoTest{ 325 F_BoolRepeated: []bool{}, 326 F_Int32Repeated: []int32{}, 327 F_Int64Repeated: []int64{}, 328 F_Uint32Repeated: []uint32{}, 329 F_Uint64Repeated: []uint64{}, 330 F_FloatRepeated: []float32{}, 331 F_DoubleRepeated: []float64{}, 332 F_StringRepeated: []string{}, 333 F_BytesRepeated: [][]byte{}, 334 }, 335 want: &pb2.GoTest{ 336 F_BoolRepeated: []bool{}, 337 F_Int32Repeated: []int32{}, 338 F_Int64Repeated: []int64{}, 339 F_Uint32Repeated: []uint32{}, 340 F_Uint64Repeated: []uint64{}, 341 F_FloatRepeated: []float32{}, 342 F_DoubleRepeated: []float64{}, 343 F_StringRepeated: []string{}, 344 F_BytesRepeated: [][]byte{}, 345 }, 346 }, 347 { 348 src: &pb2.GoTest{ 349 F_BytesRepeated: [][]byte{nil, []byte{}, []byte{0}}, 350 }, 351 dst: &pb2.GoTest{}, 352 want: &pb2.GoTest{ 353 F_BytesRepeated: [][]byte{nil, []byte{}, []byte{0}}, 354 }, 355 }, 356 { 357 src: &pb2.MyMessage{ 358 Others: []*pb2.OtherMessage{}, 359 }, 360 dst: &pb2.MyMessage{}, 361 want: &pb2.MyMessage{ 362 Others: []*pb2.OtherMessage{}, 363 }, 364 }, 365} 366 367func TestMerge(t *testing.T) { 368 for _, m := range mergeTests { 369 got := proto.Clone(m.dst) 370 if !proto.Equal(got, m.dst) { 371 t.Errorf("Clone()\ngot %v\nwant %v", got, m.dst) 372 continue 373 } 374 proto.Merge(got, m.src) 375 if !proto.Equal(got, m.want) { 376 t.Errorf("Merge(%v, %v)\ngot %v\nwant %v", m.dst, m.src, got, m.want) 377 } 378 } 379} 380