// 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) } }