...

Source file src/google.golang.org/protobuf/proto/encode_test.go

Documentation: google.golang.org/protobuf/proto

     1  // Copyright 2019 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  	"bytes"
     9  	"fmt"
    10  	"math"
    11  	"reflect"
    12  	"testing"
    13  
    14  	"github.com/google/go-cmp/cmp"
    15  
    16  	"google.golang.org/protobuf/encoding/prototext"
    17  	"google.golang.org/protobuf/encoding/protowire"
    18  	"google.golang.org/protobuf/proto"
    19  	"google.golang.org/protobuf/reflect/protoreflect"
    20  
    21  	"google.golang.org/protobuf/internal/errors"
    22  	orderpb "google.golang.org/protobuf/internal/testprotos/order"
    23  	testpb "google.golang.org/protobuf/internal/testprotos/test"
    24  	test3pb "google.golang.org/protobuf/internal/testprotos/test3"
    25  )
    26  
    27  func TestEncode(t *testing.T) {
    28  	for _, test := range testValidMessages {
    29  		for _, want := range test.decodeTo {
    30  			t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
    31  				opts := proto.MarshalOptions{
    32  					AllowPartial: test.partial,
    33  				}
    34  				wire, err := opts.Marshal(want)
    35  				if err != nil {
    36  					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    37  				}
    38  
    39  				size := proto.Size(want)
    40  				if size != len(wire) {
    41  					t.Errorf("Size and marshal disagree: Size(m)=%v; len(Marshal(m))=%v\nMessage:\n%v", size, len(wire), prototext.Format(want))
    42  				}
    43  
    44  				got := want.ProtoReflect().New().Interface()
    45  				uopts := proto.UnmarshalOptions{
    46  					AllowPartial: test.partial,
    47  				}
    48  				if err := uopts.Unmarshal(wire, got); err != nil {
    49  					t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    50  					return
    51  				}
    52  				if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
    53  					t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
    54  				}
    55  			})
    56  		}
    57  	}
    58  }
    59  
    60  func TestEncodeDeterministic(t *testing.T) {
    61  	for _, test := range testValidMessages {
    62  		for _, want := range test.decodeTo {
    63  			t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
    64  				opts := proto.MarshalOptions{
    65  					Deterministic: true,
    66  					AllowPartial:  test.partial,
    67  				}
    68  				wire, err := opts.Marshal(want)
    69  				if err != nil {
    70  					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    71  				}
    72  				wire2, err := opts.Marshal(want)
    73  				if err != nil {
    74  					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    75  				}
    76  				if !bytes.Equal(wire, wire2) {
    77  					t.Fatalf("deterministic marshal returned varying results:\n%v", cmp.Diff(wire, wire2))
    78  				}
    79  
    80  				got := want.ProtoReflect().New().Interface()
    81  				uopts := proto.UnmarshalOptions{
    82  					AllowPartial: test.partial,
    83  				}
    84  				if err := uopts.Unmarshal(wire, got); err != nil {
    85  					t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    86  					return
    87  				}
    88  				if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
    89  					t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
    90  				}
    91  			})
    92  		}
    93  	}
    94  }
    95  
    96  func TestEncodeRequiredFieldChecks(t *testing.T) {
    97  	for _, test := range testValidMessages {
    98  		if !test.partial {
    99  			continue
   100  		}
   101  		for _, m := range test.decodeTo {
   102  			t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
   103  				_, err := proto.Marshal(m)
   104  				if err == nil {
   105  					t.Fatalf("Marshal succeeded (want error)\nMessage:\n%v", prototext.Format(m))
   106  				}
   107  			})
   108  		}
   109  	}
   110  }
   111  
   112  func TestEncodeAppend(t *testing.T) {
   113  	want := []byte("prefix")
   114  	got := append([]byte(nil), want...)
   115  	got, err := proto.MarshalOptions{}.MarshalAppend(got, &test3pb.TestAllTypes{
   116  		SingularString: "value",
   117  	})
   118  	if err != nil {
   119  		t.Fatal(err)
   120  	}
   121  	if !bytes.HasPrefix(got, want) {
   122  		t.Fatalf("MarshalAppend modified prefix: got %v, want prefix %v", got, want)
   123  	}
   124  }
   125  
   126  func TestEncodeInvalidMessages(t *testing.T) {
   127  	for _, test := range testInvalidMessages {
   128  		for _, m := range test.decodeTo {
   129  			if !m.ProtoReflect().IsValid() {
   130  				continue
   131  			}
   132  			t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
   133  				opts := proto.MarshalOptions{
   134  					AllowPartial: test.partial,
   135  				}
   136  				got, err := opts.Marshal(m)
   137  				if err == nil {
   138  					t.Fatalf("Marshal unexpectedly succeeded\noutput bytes: [%x]\nMessage:\n%v", got, prototext.Format(m))
   139  				}
   140  				if !errors.Is(err, proto.Error) {
   141  					t.Fatalf("Marshal error is not a proto.Error: %v", err)
   142  				}
   143  			})
   144  		}
   145  	}
   146  }
   147  
   148  func TestEncodeOneofNilWrapper(t *testing.T) {
   149  	m := &testpb.TestAllTypes{OneofField: (*testpb.TestAllTypes_OneofUint32)(nil)}
   150  	b, err := proto.Marshal(m)
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  	if len(b) > 0 {
   155  		t.Errorf("Marshal return non-empty, want empty")
   156  	}
   157  }
   158  
   159  func TestMarshalAppendAllocations(t *testing.T) {
   160  	m := &test3pb.TestAllTypes{SingularInt32: 1}
   161  	size := proto.Size(m)
   162  	const count = 1000
   163  	b := make([]byte, size)
   164  	// AllocsPerRun returns an integral value.
   165  	marshalAllocs := testing.AllocsPerRun(count, func() {
   166  		_, err := proto.MarshalOptions{}.MarshalAppend(b[:0], m)
   167  		if err != nil {
   168  			t.Fatal(err)
   169  		}
   170  	})
   171  	b = nil
   172  	marshalAppendAllocs := testing.AllocsPerRun(count, func() {
   173  		var err error
   174  		b, err = proto.MarshalOptions{}.MarshalAppend(b, m)
   175  		if err != nil {
   176  			t.Fatal(err)
   177  		}
   178  	})
   179  	if marshalAllocs != marshalAppendAllocs {
   180  		t.Errorf("%v allocs/op when writing to a preallocated buffer", marshalAllocs)
   181  		t.Errorf("%v allocs/op when repeatedly appending to a slice", marshalAppendAllocs)
   182  		t.Errorf("expect amortized allocs/op to be identical")
   183  	}
   184  }
   185  
   186  func TestEncodeOrder(t *testing.T) {
   187  	// We make no guarantees about the stability of wire marshal output.
   188  	// The order in which fields are marshaled may change over time.
   189  	// If deterministic marshaling is not enabled, it may change over
   190  	// successive calls to proto.Marshal in the same binary.
   191  	//
   192  	// Unfortunately, many users have come to rely on the specific current
   193  	// wire marshal output. Perhaps someday we will choose to deliberately
   194  	// change the marshal output; until that day comes, this test verifies
   195  	// that we don't unintentionally change it.
   196  	m := &orderpb.Message{
   197  		Field_1:  proto.String("one"),
   198  		Field_2:  proto.String("two"),
   199  		Field_20: proto.String("twenty"),
   200  		Oneof_1:  &orderpb.Message_Field_10{"ten"},
   201  	}
   202  	proto.SetExtension(m, orderpb.E_Field_30, "thirty")
   203  	proto.SetExtension(m, orderpb.E_Field_31, "thirty-one")
   204  	proto.SetExtension(m, orderpb.E_Field_32, "thirty-two")
   205  	want := []protoreflect.FieldNumber{
   206  		30, 31, 32, // extensions first, in number order
   207  		1, 2, 20, // non-extension, non-oneof in number order
   208  		10, // oneofs last, undefined order
   209  	}
   210  
   211  	// Test with deterministic serialization, since fields are not sorted without
   212  	// it when -tags=protoreflect.
   213  	b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
   214  	if err != nil {
   215  		t.Fatal(err)
   216  	}
   217  	var got []protoreflect.FieldNumber
   218  	for len(b) > 0 {
   219  		num, _, n := protowire.ConsumeField(b)
   220  		if n < 0 {
   221  			t.Fatal(protowire.ParseError(n))
   222  		}
   223  		b = b[n:]
   224  		got = append(got, num)
   225  	}
   226  	if !reflect.DeepEqual(got, want) {
   227  		t.Errorf("unexpected field marshal order:\ngot:  %v\nwant: %v\nmessage:\n%v", got, want, m)
   228  	}
   229  }
   230  
   231  func TestEncodeLarge(t *testing.T) {
   232  	// Encode/decode a message large enough to overflow a 32-bit size cache.
   233  	t.Skip("too slow and memory-hungry to run all the time")
   234  	size := int64(math.MaxUint32 + 1)
   235  	m := &testpb.TestAllTypes_NestedMessage{
   236  		Corecursive: &testpb.TestAllTypes{
   237  			OptionalBytes: make([]byte, size),
   238  		},
   239  	}
   240  	b, err := proto.Marshal(m)
   241  	if err != nil {
   242  		t.Fatalf("Marshal: %v", err)
   243  	}
   244  	if got, want := len(b), proto.Size(m); got != want {
   245  		t.Fatalf("Size(m) = %v, but len(Marshal(m)) = %v", got, want)
   246  	}
   247  	if err := proto.Unmarshal(b, m); err != nil {
   248  		t.Fatalf("Unmarshal: %v", err)
   249  	}
   250  	if got, want := int64(len(m.Corecursive.OptionalBytes)), size; got != want {
   251  		t.Errorf("after round-trip marshal, got len(m.OptionalBytes) = %v, want %v", got, want)
   252  	}
   253  }
   254  
   255  // TestEncodeEmpty tests for boundary conditions when producing an empty output.
   256  // These tests are not necessarily a statement of proper behavior,
   257  // but exist to detect accidental changes in behavior.
   258  func TestEncodeEmpty(t *testing.T) {
   259  	for _, m := range []proto.Message{nil, (*testpb.TestAllTypes)(nil), &testpb.TestAllTypes{}} {
   260  		isValid := m != nil && m.ProtoReflect().IsValid()
   261  
   262  		b, err := proto.Marshal(m)
   263  		if err != nil {
   264  			t.Errorf("proto.Marshal() = %v", err)
   265  		}
   266  		if isNil := b == nil; isNil == isValid {
   267  			t.Errorf("proto.Marshal() == nil: %v, want %v", isNil, !isValid)
   268  		}
   269  
   270  		b, err = proto.MarshalOptions{}.Marshal(m)
   271  		if err != nil {
   272  			t.Errorf("proto.MarshalOptions{}.Marshal() = %v", err)
   273  		}
   274  		if isNil := b == nil; isNil == isValid {
   275  			t.Errorf("proto.MarshalOptions{}.Marshal() = %v, want %v", isNil, !isValid)
   276  		}
   277  	}
   278  }
   279  

View as plain text