...

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  	"google.golang.org/protobuf/types/known/durationpb"
    21  
    22  	"google.golang.org/protobuf/internal/errors"
    23  	orderpb "google.golang.org/protobuf/internal/testprotos/order"
    24  	testpb "google.golang.org/protobuf/internal/testprotos/test"
    25  	test3pb "google.golang.org/protobuf/internal/testprotos/test3"
    26  )
    27  
    28  func TestEncode(t *testing.T) {
    29  	for _, test := range testValidMessages {
    30  		for _, want := range test.decodeTo {
    31  			t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
    32  				opts := proto.MarshalOptions{
    33  					AllowPartial: test.partial,
    34  				}
    35  				wire, err := opts.Marshal(want)
    36  				if err != nil {
    37  					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    38  				}
    39  
    40  				size := proto.Size(want)
    41  				if size != len(wire) {
    42  					t.Errorf("Size and marshal disagree: Size(m)=%v; len(Marshal(m))=%v\nMessage:\n%v", size, len(wire), prototext.Format(want))
    43  				}
    44  
    45  				got := want.ProtoReflect().New().Interface()
    46  				uopts := proto.UnmarshalOptions{
    47  					AllowPartial: test.partial,
    48  				}
    49  				if err := uopts.Unmarshal(wire, got); err != nil {
    50  					t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    51  					return
    52  				}
    53  				if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
    54  					t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
    55  				}
    56  			})
    57  		}
    58  	}
    59  }
    60  
    61  func TestEncodeDeterministic(t *testing.T) {
    62  	for _, test := range testValidMessages {
    63  		for _, want := range test.decodeTo {
    64  			t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
    65  				opts := proto.MarshalOptions{
    66  					Deterministic: true,
    67  					AllowPartial:  test.partial,
    68  				}
    69  				wire, err := opts.Marshal(want)
    70  				if err != nil {
    71  					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    72  				}
    73  				wire2, err := opts.Marshal(want)
    74  				if err != nil {
    75  					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    76  				}
    77  				if !bytes.Equal(wire, wire2) {
    78  					t.Fatalf("deterministic marshal returned varying results:\n%v", cmp.Diff(wire, wire2))
    79  				}
    80  
    81  				got := want.ProtoReflect().New().Interface()
    82  				uopts := proto.UnmarshalOptions{
    83  					AllowPartial: test.partial,
    84  				}
    85  				if err := uopts.Unmarshal(wire, got); err != nil {
    86  					t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
    87  					return
    88  				}
    89  				if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
    90  					t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
    91  				}
    92  			})
    93  		}
    94  	}
    95  }
    96  
    97  func TestEncodeRequiredFieldChecks(t *testing.T) {
    98  	for _, test := range testValidMessages {
    99  		if !test.partial {
   100  			continue
   101  		}
   102  		for _, m := range test.decodeTo {
   103  			t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
   104  				_, err := proto.Marshal(m)
   105  				if err == nil {
   106  					t.Fatalf("Marshal succeeded (want error)\nMessage:\n%v", prototext.Format(m))
   107  				}
   108  			})
   109  		}
   110  	}
   111  }
   112  
   113  func TestEncodeAppend(t *testing.T) {
   114  	want := []byte("prefix")
   115  	got := append([]byte(nil), want...)
   116  	got, err := proto.MarshalOptions{}.MarshalAppend(got, &test3pb.TestAllTypes{
   117  		SingularString: "value",
   118  	})
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	if !bytes.HasPrefix(got, want) {
   123  		t.Fatalf("MarshalAppend modified prefix: got %v, want prefix %v", got, want)
   124  	}
   125  }
   126  
   127  func TestEncodeInvalidMessages(t *testing.T) {
   128  	for _, test := range testInvalidMessages {
   129  		for _, m := range test.decodeTo {
   130  			if !m.ProtoReflect().IsValid() {
   131  				continue
   132  			}
   133  			t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
   134  				opts := proto.MarshalOptions{
   135  					AllowPartial: test.partial,
   136  				}
   137  				got, err := opts.Marshal(m)
   138  				if err == nil {
   139  					t.Fatalf("Marshal unexpectedly succeeded\noutput bytes: [%x]\nMessage:\n%v", got, prototext.Format(m))
   140  				}
   141  				if !errors.Is(err, proto.Error) {
   142  					t.Fatalf("Marshal error is not a proto.Error: %v", err)
   143  				}
   144  			})
   145  		}
   146  	}
   147  }
   148  
   149  func TestEncodeOneofNilWrapper(t *testing.T) {
   150  	m := &testpb.TestAllTypes{OneofField: (*testpb.TestAllTypes_OneofUint32)(nil)}
   151  	b, err := proto.Marshal(m)
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	if len(b) > 0 {
   156  		t.Errorf("Marshal return non-empty, want empty")
   157  	}
   158  }
   159  
   160  func TestMarshalAppendAllocations(t *testing.T) {
   161  	// This test ensures that MarshalAppend() has the same performance
   162  	// characteristics as the append() builtin, meaning that repeated calls do
   163  	// not allocate each time, but allocations are amortized.
   164  	m := &test3pb.TestAllTypes{SingularInt32: 1}
   165  	size := proto.Size(m)
   166  	const count = 1000
   167  	b := make([]byte, size)
   168  	// AllocsPerRun returns an integral value.
   169  	marshalAllocs := testing.AllocsPerRun(count, func() {
   170  		_, err := proto.MarshalOptions{}.MarshalAppend(b[:0], m)
   171  		if err != nil {
   172  			t.Fatal(err)
   173  		}
   174  	})
   175  	b = nil
   176  	marshalAppendAllocs := testing.AllocsPerRun(count, func() {
   177  		var err error
   178  		b, err = proto.MarshalOptions{}.MarshalAppend(b, m)
   179  		if err != nil {
   180  			t.Fatal(err)
   181  		}
   182  	})
   183  	if marshalAllocs != marshalAppendAllocs {
   184  		t.Errorf("%v allocs/op when writing to a preallocated buffer", marshalAllocs)
   185  		t.Errorf("%v allocs/op when repeatedly appending to a slice", marshalAppendAllocs)
   186  		t.Errorf("expect amortized allocs/op to be identical")
   187  	}
   188  }
   189  
   190  func TestEncodeOrder(t *testing.T) {
   191  	// We make no guarantees about the stability of wire marshal output.
   192  	// The order in which fields are marshaled may change over time.
   193  	// If deterministic marshaling is not enabled, it may change over
   194  	// successive calls to proto.Marshal in the same binary.
   195  	//
   196  	// Unfortunately, many users have come to rely on the specific current
   197  	// wire marshal output. Perhaps someday we will choose to deliberately
   198  	// change the marshal output; until that day comes, this test verifies
   199  	// that we don't unintentionally change it.
   200  	m := &orderpb.Message{
   201  		Field_1:  proto.String("one"),
   202  		Field_2:  proto.String("two"),
   203  		Field_20: proto.String("twenty"),
   204  		Oneof_1:  &orderpb.Message_Field_10{"ten"},
   205  	}
   206  	proto.SetExtension(m, orderpb.E_Field_30, "thirty")
   207  	proto.SetExtension(m, orderpb.E_Field_31, "thirty-one")
   208  	proto.SetExtension(m, orderpb.E_Field_32, "thirty-two")
   209  	want := []protoreflect.FieldNumber{
   210  		30, 31, 32, // extensions first, in number order
   211  		1, 2, 20, // non-extension, non-oneof in number order
   212  		10, // oneofs last, undefined order
   213  	}
   214  
   215  	// Test with deterministic serialization, since fields are not sorted without
   216  	// it when -tags=protoreflect.
   217  	b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
   218  	if err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	var got []protoreflect.FieldNumber
   222  	for len(b) > 0 {
   223  		num, _, n := protowire.ConsumeField(b)
   224  		if n < 0 {
   225  			t.Fatal(protowire.ParseError(n))
   226  		}
   227  		b = b[n:]
   228  		got = append(got, num)
   229  	}
   230  	if !reflect.DeepEqual(got, want) {
   231  		t.Errorf("unexpected field marshal order:\ngot:  %v\nwant: %v\nmessage:\n%v", got, want, m)
   232  	}
   233  }
   234  
   235  func TestEncodeLarge(t *testing.T) {
   236  	// Encode/decode a message large enough to overflow a 32-bit size cache.
   237  	t.Skip("too slow and memory-hungry to run all the time")
   238  	size := int64(math.MaxUint32 + 1)
   239  	m := &testpb.TestAllTypes_NestedMessage{
   240  		Corecursive: &testpb.TestAllTypes{
   241  			OptionalBytes: make([]byte, size),
   242  		},
   243  	}
   244  	b, err := proto.Marshal(m)
   245  	if err != nil {
   246  		t.Fatalf("Marshal: %v", err)
   247  	}
   248  	if got, want := len(b), proto.Size(m); got != want {
   249  		t.Fatalf("Size(m) = %v, but len(Marshal(m)) = %v", got, want)
   250  	}
   251  	if err := proto.Unmarshal(b, m); err != nil {
   252  		t.Fatalf("Unmarshal: %v", err)
   253  	}
   254  	if got, want := int64(len(m.Corecursive.OptionalBytes)), size; got != want {
   255  		t.Errorf("after round-trip marshal, got len(m.OptionalBytes) = %v, want %v", got, want)
   256  	}
   257  }
   258  
   259  // TestEncodeEmpty tests for boundary conditions when producing an empty output.
   260  // These tests are not necessarily a statement of proper behavior,
   261  // but exist to detect accidental changes in behavior.
   262  func TestEncodeEmpty(t *testing.T) {
   263  	for _, m := range []proto.Message{nil, (*testpb.TestAllTypes)(nil), &testpb.TestAllTypes{}} {
   264  		t.Run(fmt.Sprintf("%T", m), func(t *testing.T) {
   265  			isValid := m != nil && m.ProtoReflect().IsValid()
   266  
   267  			b, err := proto.Marshal(m)
   268  			if err != nil {
   269  				t.Errorf("proto.Marshal() = %v", err)
   270  			}
   271  			if isNil := b == nil; isNil == isValid {
   272  				t.Errorf("proto.Marshal() == nil: %v, want %v", isNil, !isValid)
   273  			}
   274  
   275  			b, err = proto.MarshalOptions{}.Marshal(m)
   276  			if err != nil {
   277  				t.Errorf("proto.MarshalOptions{}.Marshal() = %v", err)
   278  			}
   279  			if isNil := b == nil; isNil == isValid {
   280  				t.Errorf("proto.MarshalOptions{}.Marshal() = %v, want %v", isNil, !isValid)
   281  			}
   282  		})
   283  	}
   284  }
   285  
   286  // This example illustrates how to marshal (encode) a Protobuf message struct
   287  // literal into wire-format encoding.
   288  //
   289  // This example hard-codes a duration of 125ns for the illustration of struct
   290  // fields, but note that you do not need to fill the fields of well-known types
   291  // like duration.proto yourself. To convert a time.Duration, use
   292  // [google.golang.org/protobuf/types/known/durationpb.New].
   293  func ExampleMarshal() {
   294  	b, err := proto.Marshal(&durationpb.Duration{
   295  		Nanos: 125,
   296  	})
   297  	if err != nil {
   298  		panic(err)
   299  	}
   300  
   301  	fmt.Printf("125ns encoded into %d bytes of Protobuf wire format:\n% x\n", len(b), b)
   302  
   303  	// You can use protoscope to explore the wire format:
   304  	// https://github.com/protocolbuffers/protoscope
   305  	//
   306  	// echo -n '10 7d' | xxd -r -ps | protoscope
   307  	// 2: 125
   308  
   309  	// Output: 125ns encoded into 2 bytes of Protobuf wire format:
   310  	// 10 7d
   311  }
   312  
   313  // This example illustrates how to marshal (encode) many Protobuf messages into
   314  // wire-format encoding, using the same buffer.
   315  //
   316  // MarshalAppend will grow the buffer as needed, so over time it will grow large
   317  // enough to not need further allocations.
   318  //
   319  // If unbounded growth of the buffer is undesirable in your application, you can
   320  // use [MarshalOptions.Size] to determine a buffer size that is guaranteed to be
   321  // large enough for marshaling without allocations.
   322  func ExampleMarshalOptions_MarshalAppend_sameBuffer() {
   323  	var m proto.Message
   324  
   325  	opts := proto.MarshalOptions{
   326  		// set e.g. Deterministic: true, if needed
   327  	}
   328  
   329  	var buf []byte
   330  	for i := 0; i < 100000; i++ {
   331  		var err error
   332  		buf, err = opts.MarshalAppend(buf[:0], m)
   333  		if err != nil {
   334  			panic(err)
   335  		}
   336  		// cap(buf) will grow to hold the largest m.
   337  
   338  		// write buf to disk, network, etc.
   339  	}
   340  }
   341  

View as plain text