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