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