...

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

Documentation: github.com/gogo/protobuf/proto

     1  // Go support for Protocol Buffers - Google's data interchange format
     2  //
     3  // Copyright 2016 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  	"strings"
    36  	"testing"
    37  
    38  	"github.com/gogo/protobuf/proto"
    39  
    40  	pb "github.com/gogo/protobuf/proto/proto3_proto"
    41  	testpb "github.com/gogo/protobuf/proto/test_proto"
    42  	"github.com/gogo/protobuf/types"
    43  )
    44  
    45  var (
    46  	expandedMarshaler        = proto.TextMarshaler{ExpandAny: true}
    47  	expandedCompactMarshaler = proto.TextMarshaler{Compact: true, ExpandAny: true}
    48  )
    49  
    50  // anyEqual reports whether two messages which may be google.protobuf.Any or may
    51  // contain google.protobuf.Any fields are equal. We can't use proto.Equal for
    52  // comparison, because semantically equivalent messages may be marshaled to
    53  // binary in different tag order. Instead, trust that TextMarshaler with
    54  // ExpandAny option works and compare the text marshaling results.
    55  func anyEqual(got, want proto.Message) bool {
    56  	// if messages are proto.Equal, no need to marshal.
    57  	if proto.Equal(got, want) {
    58  		return true
    59  	}
    60  	g := expandedMarshaler.Text(got)
    61  	w := expandedMarshaler.Text(want)
    62  	return g == w
    63  }
    64  
    65  type golden struct {
    66  	m    proto.Message
    67  	t, c string
    68  }
    69  
    70  var goldenMessages = makeGolden()
    71  
    72  func makeGolden() []golden {
    73  	nested := &pb.Nested{Bunny: "Monty"}
    74  	nb, err := proto.Marshal(nested)
    75  	if err != nil {
    76  		panic(err)
    77  	}
    78  	m1 := &pb.Message{
    79  		Name:        "David",
    80  		ResultCount: 47,
    81  		Anything:    &types.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb},
    82  	}
    83  	m2 := &pb.Message{
    84  		Name:        "David",
    85  		ResultCount: 47,
    86  		Anything:    &types.Any{TypeUrl: "http://[::1]/type.googleapis.com/" + proto.MessageName(nested), Value: nb},
    87  	}
    88  	m3 := &pb.Message{
    89  		Name:        "David",
    90  		ResultCount: 47,
    91  		Anything:    &types.Any{TypeUrl: `type.googleapis.com/"/` + proto.MessageName(nested), Value: nb},
    92  	}
    93  	m4 := &pb.Message{
    94  		Name:        "David",
    95  		ResultCount: 47,
    96  		Anything:    &types.Any{TypeUrl: "type.googleapis.com/a/path/" + proto.MessageName(nested), Value: nb},
    97  	}
    98  	m5 := &types.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb}
    99  
   100  	any1 := &testpb.MyMessage{Count: proto.Int32(47), Name: proto.String("David")}
   101  	proto.SetExtension(any1, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("foo")})
   102  	proto.SetExtension(any1, testpb.E_Ext_Text, proto.String("bar"))
   103  	any1b, err := proto.Marshal(any1)
   104  	if err != nil {
   105  		panic(err)
   106  	}
   107  	any2 := &testpb.MyMessage{Count: proto.Int32(42), Bikeshed: testpb.MyMessage_GREEN.Enum(), RepBytes: [][]byte{[]byte("roboto")}}
   108  	proto.SetExtension(any2, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("baz")})
   109  	any2b, err := proto.Marshal(any2)
   110  	if err != nil {
   111  		panic(err)
   112  	}
   113  	m6 := &pb.Message{
   114  		Name:        "David",
   115  		ResultCount: 47,
   116  		Anything:    &types.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
   117  		ManyThings: []*types.Any{
   118  			{TypeUrl: "type.googleapis.com/" + proto.MessageName(any2), Value: any2b},
   119  			{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
   120  		},
   121  	}
   122  
   123  	const (
   124  		m1Golden = `
   125  name: "David"
   126  result_count: 47
   127  anything: <
   128    [type.googleapis.com/proto3_proto.Nested]: <
   129      bunny: "Monty"
   130    >
   131  >
   132  `
   133  		m2Golden = `
   134  name: "David"
   135  result_count: 47
   136  anything: <
   137    ["http://[::1]/type.googleapis.com/proto3_proto.Nested"]: <
   138      bunny: "Monty"
   139    >
   140  >
   141  `
   142  		m3Golden = `
   143  name: "David"
   144  result_count: 47
   145  anything: <
   146    ["type.googleapis.com/\"/proto3_proto.Nested"]: <
   147      bunny: "Monty"
   148    >
   149  >
   150  `
   151  		m4Golden = `
   152  name: "David"
   153  result_count: 47
   154  anything: <
   155    [type.googleapis.com/a/path/proto3_proto.Nested]: <
   156      bunny: "Monty"
   157    >
   158  >
   159  `
   160  		m5Golden = `
   161  [type.googleapis.com/proto3_proto.Nested]: <
   162    bunny: "Monty"
   163  >
   164  `
   165  		m6Golden = `
   166  name: "David"
   167  result_count: 47
   168  anything: <
   169    [type.googleapis.com/test_proto.MyMessage]: <
   170      count: 47
   171      name: "David"
   172      [test_proto.Ext.more]: <
   173        data: "foo"
   174      >
   175      [test_proto.Ext.text]: "bar"
   176    >
   177  >
   178  many_things: <
   179    [type.googleapis.com/test_proto.MyMessage]: <
   180      count: 42
   181      bikeshed: GREEN
   182      rep_bytes: "roboto"
   183      [test_proto.Ext.more]: <
   184        data: "baz"
   185      >
   186    >
   187  >
   188  many_things: <
   189    [type.googleapis.com/test_proto.MyMessage]: <
   190      count: 47
   191      name: "David"
   192      [test_proto.Ext.more]: <
   193        data: "foo"
   194      >
   195      [test_proto.Ext.text]: "bar"
   196    >
   197  >
   198  `
   199  	)
   200  	return []golden{
   201  		{m1, strings.TrimSpace(m1Golden) + "\n", strings.TrimSpace(compact(m1Golden)) + " "},
   202  		{m2, strings.TrimSpace(m2Golden) + "\n", strings.TrimSpace(compact(m2Golden)) + " "},
   203  		{m3, strings.TrimSpace(m3Golden) + "\n", strings.TrimSpace(compact(m3Golden)) + " "},
   204  		{m4, strings.TrimSpace(m4Golden) + "\n", strings.TrimSpace(compact(m4Golden)) + " "},
   205  		{m5, strings.TrimSpace(m5Golden) + "\n", strings.TrimSpace(compact(m5Golden)) + " "},
   206  		{m6, strings.TrimSpace(m6Golden) + "\n", strings.TrimSpace(compact(m6Golden)) + " "},
   207  	}
   208  }
   209  
   210  func TestMarshalGolden(t *testing.T) {
   211  	for _, tt := range goldenMessages {
   212  		if got, want := expandedMarshaler.Text(tt.m), tt.t; got != want {
   213  			t.Errorf("message %v: got:\n%s\nwant:\n%s", tt.m, got, want)
   214  		}
   215  		if got, want := expandedCompactMarshaler.Text(tt.m), tt.c; got != want {
   216  			t.Errorf("message %v: got:\n`%s`\nwant:\n`%s`", tt.m, got, want)
   217  		}
   218  	}
   219  }
   220  
   221  func TestUnmarshalGolden(t *testing.T) {
   222  	for _, tt := range goldenMessages {
   223  		want := tt.m
   224  		got := proto.Clone(tt.m)
   225  		got.Reset()
   226  		if err := proto.UnmarshalText(tt.t, got); err != nil {
   227  			t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.t, err)
   228  		}
   229  		if !anyEqual(got, want) {
   230  			t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.t, got, want)
   231  		}
   232  		got.Reset()
   233  		if err := proto.UnmarshalText(tt.c, got); err != nil {
   234  			t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.c, err)
   235  		}
   236  		if !anyEqual(got, want) {
   237  			t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.c, got, want)
   238  		}
   239  	}
   240  }
   241  
   242  func TestMarshalUnknownAny(t *testing.T) {
   243  	m := &pb.Message{
   244  		Anything: &types.Any{
   245  			TypeUrl: "foo",
   246  			Value:   []byte("bar"),
   247  		},
   248  	}
   249  	want := `anything: <
   250    type_url: "foo"
   251    value: "bar"
   252  >
   253  `
   254  	got := expandedMarshaler.Text(m)
   255  	if got != want {
   256  		t.Errorf("got\n`%s`\nwant\n`%s`", got, want)
   257  	}
   258  }
   259  
   260  func TestAmbiguousAny(t *testing.T) {
   261  	pb := &types.Any{}
   262  	err := proto.UnmarshalText(`
   263  	type_url: "ttt/proto3_proto.Nested"
   264  	value: "\n\x05Monty"
   265  	`, pb)
   266  	t.Logf("result: %v (error: %v)", expandedMarshaler.Text(pb), err)
   267  	if err != nil {
   268  		t.Errorf("failed to parse ambiguous Any message: %v", err)
   269  	}
   270  }
   271  
   272  func TestUnmarshalOverwriteAny(t *testing.T) {
   273  	pb := &types.Any{}
   274  	err := proto.UnmarshalText(`
   275    [type.googleapis.com/a/path/proto3_proto.Nested]: <
   276      bunny: "Monty"
   277    >
   278    [type.googleapis.com/a/path/proto3_proto.Nested]: <
   279      bunny: "Rabbit of Caerbannog"
   280    >
   281  	`, pb)
   282  	want := `line 7: Any message unpacked multiple times, or "type_url" already set`
   283  	if err.Error() != want {
   284  		t.Errorf("incorrect error.\nHave: %v\nWant: %v", err.Error(), want)
   285  	}
   286  }
   287  
   288  func TestUnmarshalAnyMixAndMatch(t *testing.T) {
   289  	pb := &types.Any{}
   290  	err := proto.UnmarshalText(`
   291  	value: "\n\x05Monty"
   292    [type.googleapis.com/a/path/proto3_proto.Nested]: <
   293      bunny: "Rabbit of Caerbannog"
   294    >
   295  	`, pb)
   296  	want := `line 5: Any message unpacked multiple times, or "value" already set`
   297  	if err.Error() != want {
   298  		t.Errorf("incorrect error.\nHave: %v\nWant: %v", err.Error(), want)
   299  	}
   300  }
   301  

View as plain text