...

Source file src/github.com/golang/protobuf/proto/proto_clone_test.go

Documentation: github.com/golang/protobuf/proto

     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  
     5  package proto_test
     6  
     7  import (
     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  
    16  var 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  
    36  func 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  
    51  func 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  
    90  func 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  
    97  var 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  
   367  func 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  

View as plain text