• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/golang/protobuf/proto"
38
39	proto3pb "github.com/golang/protobuf/proto/proto3_proto"
40	pb "github.com/golang/protobuf/proto/testdata"
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.Errorf("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: &pb.FloatingPoint{F: proto.Float64(2.0)},
198				0x4002: &pb.FloatingPoint{
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: &pb.FloatingPoint{
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: &pb.FloatingPoint{F: proto.Float64(2.0)},
223				0x4002: &pb.FloatingPoint{
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{41},
251		},
252		dst: &pb.Communique{
253			Union: &pb.Communique_Name{"Bobby Tables"},
254		},
255		want: &pb.Communique{
256			Union: &pb.Communique_Number{41},
257		},
258	},
259	// Oneof nil is the same as not set.
260	{
261		src: &pb.Communique{},
262		dst: &pb.Communique{
263			Union: &pb.Communique_Name{"Bobby Tables"},
264		},
265		want: &pb.Communique{
266			Union: &pb.Communique_Name{"Bobby Tables"},
267		},
268	},
269	{
270		src: &proto3pb.Message{
271			Terrain: map[string]*proto3pb.Nested{
272				"kay_a": &proto3pb.Nested{Cute: true},      // replace
273				"kay_b": &proto3pb.Nested{Bunny: "rabbit"}, // insert
274			},
275		},
276		dst: &proto3pb.Message{
277			Terrain: map[string]*proto3pb.Nested{
278				"kay_a": &proto3pb.Nested{Bunny: "lost"},  // replaced
279				"kay_c": &proto3pb.Nested{Bunny: "bunny"}, // keep
280			},
281		},
282		want: &proto3pb.Message{
283			Terrain: map[string]*proto3pb.Nested{
284				"kay_a": &proto3pb.Nested{Cute: true},
285				"kay_b": &proto3pb.Nested{Bunny: "rabbit"},
286				"kay_c": &proto3pb.Nested{Bunny: "bunny"},
287			},
288		},
289	},
290}
291
292func TestMerge(t *testing.T) {
293	for _, m := range mergeTests {
294		got := proto.Clone(m.dst)
295		proto.Merge(got, m.src)
296		if !proto.Equal(got, m.want) {
297			t.Errorf("Merge(%v, %v)\n got %v\nwant %v\n", m.dst, m.src, got, m.want)
298		}
299	}
300}
301