blob: 0b80e1e902ffdbd15f95b7a0bb90000ca5d0c416 [file] [log] [blame]
// Copyright 2024 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 impl_test
import (
"testing"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protopack"
lazytestpb "google.golang.org/protobuf/internal/testprotos/lazy"
)
// Constructs a message encoded in denormalized (non-minimal) wire format, but
// using two levels of nesting: A top-level message with a child message which
// in turn has a grandchild message.
func denormalizedTwoLevel(t *testing.T) ([]byte, *lazytestpb.Top, error) {
// Construct a message with denormalized (non-minimal) wire format:
// 1. Encode a top-level message with submessage B (ext) + C (field)
// 2. Replace the encoding of submessage C (field) with
// another instance of submessage B (ext)
//
// This modification of the wire format is spec'd in Protobuf:
// https://github.com/protocolbuffers/protobuf/issues/9257
grandchild := &lazytestpb.Sub{}
proto.SetExtension(grandchild, lazytestpb.E_Ext_B, &lazytestpb.Ext{
SomeFlag: proto.Bool(true),
})
expectedMessage := &lazytestpb.Top{
Child: &lazytestpb.Sub{
Grandchild: grandchild,
},
A: proto.Uint32(2342),
}
fullMessage := protopack.Message{
protopack.Tag{1, protopack.VarintType}, protopack.Varint(2342),
// Child
protopack.Tag{2, protopack.BytesType}, protopack.LengthPrefix(protopack.Message{
// Grandchild
protopack.Tag{4, protopack.BytesType}, protopack.LengthPrefix(protopack.Message{
// The first occurrence of B matches expectedMessage:
protopack.Tag{2, protopack.BytesType}, protopack.LengthPrefix(protopack.Message{
protopack.Tag{1, protopack.VarintType}, protopack.Varint(1),
}),
// This second duplicative occurrence of B is spec'd in Protobuf:
// https://github.com/protocolbuffers/protobuf/issues/9257
protopack.Tag{2, protopack.BytesType}, protopack.LengthPrefix(protopack.Message{
protopack.Tag{1, protopack.VarintType}, protopack.Varint(1),
}),
}),
}),
}.Marshal()
return fullMessage, expectedMessage, nil
}
func TestNoInvalidWireFormatWithDeterministicLazy(t *testing.T) {
fullMessage, _, err := denormalizedTwoLevel(t)
if err != nil {
t.Fatal(err)
}
top := &lazytestpb.Top{}
if err := proto.Unmarshal(fullMessage, top); err != nil {
t.Fatal(err)
}
// Requesting deterministic marshaling should result in unmarshaling (and
// thereby normalizing the non-minimal encoding) when sizing.
//
// If the deterministic flag is dropped (like before cl/624951104), the size
// cache is populated with the non-minimal size. The Marshal call below
// lazily unmarshals (due to the Deterministic flag), which includes
// normalization, and will then report a size mismatch error (instead of
// producing invalid wire format).
proto.MarshalOptions{Deterministic: true}.Size(top)
_, err = proto.MarshalOptions{
Deterministic: true,
UseCachedSize: true,
}.Marshal(top)
if err != nil {
t.Fatal(err)
}
}