mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-01 03:14:16 +00:00
96a44732e0
The v1 proto.Equal function treats (*Message)(nil) and new(Message) as being different, while v2 proto.Equal treated them as equal since a typed nil pointer is functionally an empty message since the protobuf data model has no concept of presence as a first-class property of messages. Unfortunately, a significant amount of code depends on this distinction that it would be difficult to migrate users from v1 to v2 unless we preserved similar semantics in the v2 proto.Equal. Also, double down on these semantics for protocmp.Transform. Fixes #965 Change-Id: I21e78ba6251401a0ac0ccf495188093973cd7f3f Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/213238 Reviewed-by: Damien Neil <dneil@google.com>
162 lines
4.5 KiB
Go
162 lines
4.5 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style.
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package proto_test
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"google.golang.org/protobuf/encoding/prototext"
|
|
"google.golang.org/protobuf/internal/encoding/pack"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
|
|
testpb "google.golang.org/protobuf/internal/testprotos/test"
|
|
test3pb "google.golang.org/protobuf/internal/testprotos/test3"
|
|
)
|
|
|
|
func TestDecode(t *testing.T) {
|
|
for _, test := range testValidMessages {
|
|
if len(test.decodeTo) == 0 {
|
|
t.Errorf("%v: no test message types", test.desc)
|
|
}
|
|
for _, want := range test.decodeTo {
|
|
t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
|
|
opts := proto.UnmarshalOptions{
|
|
AllowPartial: test.partial,
|
|
}
|
|
wire := append(([]byte)(nil), test.wire...)
|
|
got := reflect.New(reflect.TypeOf(want).Elem()).Interface().(proto.Message)
|
|
if err := opts.Unmarshal(wire, got); err != nil {
|
|
t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, marshalText(want))
|
|
return
|
|
}
|
|
|
|
// Aliasing check: Modifying the original wire bytes shouldn't
|
|
// affect the unmarshaled message.
|
|
for i := range wire {
|
|
wire[i] = 0
|
|
}
|
|
if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
|
|
t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", marshalText(got), marshalText(want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeRequiredFieldChecks(t *testing.T) {
|
|
for _, test := range testValidMessages {
|
|
if !test.partial {
|
|
continue
|
|
}
|
|
for _, m := range test.decodeTo {
|
|
t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
|
|
got := reflect.New(reflect.TypeOf(m).Elem()).Interface().(proto.Message)
|
|
if err := proto.Unmarshal(test.wire, got); err == nil {
|
|
t.Fatalf("Unmarshal succeeded (want error)\nMessage:\n%v", marshalText(got))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeInvalidMessages(t *testing.T) {
|
|
for _, test := range testInvalidMessages {
|
|
if len(test.decodeTo) == 0 {
|
|
t.Errorf("%v: no test message types", test.desc)
|
|
}
|
|
for _, want := range test.decodeTo {
|
|
t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
|
|
opts := proto.UnmarshalOptions{
|
|
AllowPartial: test.partial,
|
|
}
|
|
got := want.ProtoReflect().New().Interface()
|
|
if err := opts.Unmarshal(test.wire, got); err == nil {
|
|
t.Errorf("Unmarshal unexpectedly succeeded\ninput bytes: [%x]\nMessage:\n%v", test.wire, marshalText(got))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeZeroLengthBytes(t *testing.T) {
|
|
// Verify that proto3 bytes fields don't give the mistaken
|
|
// impression that they preserve presence.
|
|
wire := pack.Message{
|
|
pack.Tag{15, pack.BytesType}, pack.Bytes(nil),
|
|
}.Marshal()
|
|
m := &test3pb.TestAllTypes{}
|
|
if err := proto.Unmarshal(wire, m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if m.OptionalBytes != nil {
|
|
t.Errorf("unmarshal zero-length proto3 bytes field: got %v, want nil", m.OptionalBytes)
|
|
}
|
|
}
|
|
|
|
func TestDecodeOneofNilWrapper(t *testing.T) {
|
|
wire := pack.Message{
|
|
pack.Tag{111, pack.VarintType}, pack.Varint(1111),
|
|
}.Marshal()
|
|
m := &testpb.TestAllTypes{OneofField: (*testpb.TestAllTypes_OneofUint32)(nil)}
|
|
if err := proto.Unmarshal(wire, m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := m.GetOneofUint32(); got != 1111 {
|
|
t.Errorf("GetOneofUint32() = %v, want %v", got, 1111)
|
|
}
|
|
}
|
|
|
|
func TestDecodeEmptyBytes(t *testing.T) {
|
|
// There's really nothing wrong with a nil entry in a [][]byte,
|
|
// but we take care to produce non-nil []bytes for zero-length
|
|
// byte strings, so test for it.
|
|
m := &testpb.TestAllTypes{}
|
|
b := pack.Message{
|
|
pack.Tag{45, pack.BytesType}, pack.Bytes(nil),
|
|
}.Marshal()
|
|
if err := proto.Unmarshal(b, m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if m.RepeatedBytes[0] == nil {
|
|
t.Errorf("unmarshaling repeated bytes field containing zero-length value: Got nil bytes, want non-nil")
|
|
}
|
|
}
|
|
|
|
func build(m proto.Message, opts ...buildOpt) proto.Message {
|
|
for _, opt := range opts {
|
|
opt(m)
|
|
}
|
|
return m
|
|
}
|
|
|
|
type buildOpt func(proto.Message)
|
|
|
|
func unknown(raw protoreflect.RawFields) buildOpt {
|
|
return func(m proto.Message) {
|
|
m.ProtoReflect().SetUnknown(raw)
|
|
}
|
|
}
|
|
|
|
func extend(desc protoreflect.ExtensionType, value interface{}) buildOpt {
|
|
return func(m proto.Message) {
|
|
proto.SetExtension(m, desc, value)
|
|
}
|
|
}
|
|
|
|
func marshalText(m proto.Message) string {
|
|
if m == nil {
|
|
return "<nil>\n"
|
|
}
|
|
b, _ := prototext.MarshalOptions{
|
|
AllowPartial: true,
|
|
EmitUnknown: true,
|
|
Indent: "\t",
|
|
}.Marshal(m)
|
|
return string(b)
|
|
}
|