protobuf-go/proto/decode_test.go

150 lines
4.4 KiB
Go
Raw Normal View History

// 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"
internal/cmd/generate-protos: initial commit Create a single binary for handling generation of protos. This replaces previous logic spread throughout the repo in: * regenerate.bash * cmd/protoc-gen-go/golden_test.go * cmd/protoc-gen-go-grpc/golden_test.go * (indirectly) internal/protogen/goldentest One of the problems with the former approaches is that they relied on a version of protoc that was specific to a developer's workstation. This meant that the result of generation was not hermetic. To address this, we rely on the hard-coded version of protobuf specified in the test.bash script. A summary of changes in this CL are: * The internal_gengo.GenerateFile and internal_gengogrpc.GenerateFile functions are unified to have consistent signatures. It seems that the former accepted a *protogen.GeneratedFile to support v1 where gRPC code was generated into the same file as the base .pb.go file. However, the same functionality can be achieved by having the function return the generated file object. * The test.bash script patches the protobuf toolchain to have properly specified go_package options in each proto source file. * The test.bash script accepts a "-regenerate" argument. * Add generation for the well-known types. Contrary to how these were laid out in the v1 repo, all the well-known types are placed in the same Go package. * Add generation for the conformance proto. * Remove regenerate.bash * Remove internal/protogen * Remove cmd/protoc-gen-go/golden_test.go * Remove cmd/protoc-gen-go-grpc/golden_test.go * Add cmd/protoc-gen-go/annotation_test.go Change-Id: I4a1a97ae6f66e2fabcf4e4d292c95ab2a2db0248 Reviewed-on: https://go-review.googlesource.com/c/164477 Reviewed-by: Damien Neil <dneil@google.com>
2019-02-28 05:46:29 +00:00
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 := test.unmarshalOptions
opts.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, prototext.Format(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", prototext.Format(got), prototext.Format(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) {
opts := test.unmarshalOptions
opts.AllowPartial = false
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", prototext.Format(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 := test.unmarshalOptions
opts.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, prototext.Format(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)
}
}
all: unify protoV1.ExtensionDesc and proto.ExtensionType Change protoV1.ExtensionDesc to directly implement ExtensionType rather than delegating to one. Unify the previous types protoiface.ExtensionDescV1 and filetype.Extension in impl.ExtensionInfo. The protoV1.ExtensionDesc type becomes an alias to ExtensionInfo. This gives us: - Just one implementation of ExtensionType. - Generated foopb.E_Ext vars are canonical ExtensionTypes. - Generated foopb.E_Ext vars are also v1.ExtensionDescs for backwards compatibility. - Conversion between legacy and modern representations happens transparently when lazily initializing an ExtensionInfo. Overall, a simplification for users of generated code, since they can mostly ignore the ExtensionDesc/ExtentionType distinction and use the same value in either the old or new API. This is change 3/5 in a series of commits changing protoV1.ExtensionDesc to directly implement protoreflect.ExtensionType. 1. [v2] Add protoimpl.ExtensionInfo as an alias for protoiface.ExtensionDescV1. 2. [v1] Update references to protoimpl.ExtensionInfo to use protoiface.ExtensionInfo. 3. [v2] Create protoimpl.ExtensionInfo (an alias to a new type in the impl package) and remove protoiface.ExtensionDescV1. 4. [v1] Remove unneeded explicit conversions between ExtensionDesc and ExtensionType (since the former now directly implements the latter). 5. [v2] Remove stub conversion functions. Change-Id: I96ee890541ec11b2412e1a72c9d7b96e4d7f66b4 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189563 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-08-08 22:45:59 +00:00
func extend(desc protoreflect.ExtensionType, value interface{}) buildOpt {
return func(m proto.Message) {
proto.SetExtension(m, desc, value)
}
}