...

Source file src/github.com/gogo/protobuf/proto/clone_test.go

Documentation: github.com/gogo/protobuf/proto

     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  
    32  package proto_test
    33  
    34  import (
    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  
    43  var 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  
    63  func 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  
    72  func 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  
   101  func 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  
   108  var 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  
   385  func 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  

View as plain text