| // 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" |
| |
| mixedpb "google.golang.org/protobuf/internal/testprotos/mixed" |
| "google.golang.org/protobuf/proto" |
| ) |
| |
| var enableLazy = proto.UnmarshalOptions{} |
| var disableLazy = proto.UnmarshalOptions{ |
| NoLazyDecoding: true, |
| } |
| |
| func TestCopyTopLevelLazy(t *testing.T) { |
| testCopyTopLevel(t, enableLazy) |
| } |
| |
| func TestCopyTopLevelEager(t *testing.T) { |
| testCopyTopLevel(t, disableLazy) |
| } |
| |
| // testCopyTopLevel tests that the buffer is copied to a safe location |
| // when the opaque proto is the top level proto |
| func testCopyTopLevel(t *testing.T, unmarshalOpts proto.UnmarshalOptions) { |
| m := mixedpb.OpaqueLazy_builder{ |
| Opaque: mixedpb.OpaqueLazy_builder{ |
| OptionalInt32: proto.Int32(23), |
| }.Build(), |
| }.Build() |
| if got, want := m.GetOpaque().GetOptionalInt32(), int32(23); got != want { |
| t.Errorf("Build(): unexpected optional_int32: got %v, want %v", got, want) |
| } |
| b, err := proto.Marshal(m) |
| if err != nil { |
| t.Fatalf("Could not marshal healthy proto %v.", m) |
| } |
| m2 := &mixedpb.OpaqueLazy{} |
| if err := unmarshalOpts.Unmarshal(b, m2); err != nil { |
| t.Fatalf("Could not unmarshal healthy proto buffer: %v.", b) |
| } |
| for i := 0; i < len(b); i++ { |
| b[i] = byte(0xFF) |
| } |
| if got, want := m2.GetOpaque().GetOptionalInt32(), int32(23); got != want { |
| t.Errorf("Mixed proto referred to shared buffer: got %v, want %v", got, want) |
| } |
| } |
| |
| func TestCopyWhenContainedInOpenLazy(t *testing.T) { |
| testCopyWhenContainedInOpen(t, enableLazy) |
| } |
| |
| func TestCopyWhenContainedInOpenEager(t *testing.T) { |
| testCopyWhenContainedInOpen(t, disableLazy) |
| } |
| |
| // testCopyWhenContainedInOpen tests that the buffer is copied |
| // for opaque messages that are not on the top level |
| func testCopyWhenContainedInOpen(t *testing.T, unmarshalOpts proto.UnmarshalOptions) { |
| m := &mixedpb.OpenLazy{ |
| Opaque: mixedpb.OpaqueLazy_builder{ |
| Opaque: mixedpb.OpaqueLazy_builder{ |
| OptionalInt32: proto.Int32(23), |
| }.Build(), |
| }.Build(), |
| } |
| if got, want := m.GetOpaque().GetOpaque().GetOptionalInt32(), int32(23); got != want { |
| t.Errorf("Build(): unexpected optional_int32: got %v, want %v", got, want) |
| } |
| b, err := proto.Marshal(m) |
| if err != nil { |
| t.Fatalf("Could not marshal healthy proto %v.", m) |
| } |
| m2 := &mixedpb.OpenLazy{} |
| if err := unmarshalOpts.Unmarshal(b, m2); err != nil { |
| t.Fatalf("Could not unmarshal healthy proto buffer: %v.", b) |
| } |
| for i := 0; i < len(b); i++ { |
| b[i] = byte(0xFF) |
| } |
| if got, want := m2.GetOpaque().GetOpaque().GetOptionalInt32(), int32(23); got != want { |
| t.Errorf("Build(): unexpected optional_int32: got %v, want %v", got, want) |
| } |
| } |
| |
| func TestNoExcessiveCopyLazy(t *testing.T) { |
| testNoExcessiveCopy(t, enableLazy) |
| } |
| |
| func TestNoExcessiveCopyEager(t *testing.T) { |
| testNoExcessiveCopy(t, disableLazy) |
| } |
| |
| // testNoExcessiveCopy tests that an opaque submessage does share the buffer |
| // if the message above already got it copied |
| func testNoExcessiveCopy(t *testing.T, unmarshalOpts proto.UnmarshalOptions) { |
| m := &mixedpb.OpenLazy{ |
| Opaque: mixedpb.OpaqueLazy_builder{ |
| Opaque: mixedpb.OpaqueLazy_builder{ |
| OptionalInt32: proto.Int32(23), |
| }.Build(), |
| }.Build(), |
| } |
| if got, want := m.GetOpaque().GetOpaque().GetOptionalInt32(), int32(23); got != want { |
| t.Errorf("Build(): unexpected optional_int32: got %v, want %v", got, want) |
| } |
| b, err := proto.Marshal(m) |
| if err != nil { |
| t.Fatalf("Could not marshal healthy proto %v.", m) |
| } |
| mm := &mixedpb.OpenLazy{} |
| if err := unmarshalOpts.Unmarshal(b, mm); err != nil { |
| t.Fatalf("Could not unmarshal healthy proto buffer: %v.", b) |
| } |
| m2 := mm.GetOpaque() |
| m3 := mm.GetOpaque().GetOpaque() |
| // Now, if we deliberately destroy the OpaqueM2 buffer, the OpaqueM3 buffer should |
| // be destroyed as well |
| if m2.XXX_lazyUnmarshalInfo == nil { |
| if m3.XXX_lazyUnmarshalInfo != nil { |
| t.Errorf("Inconsistent lazyUnmarshalInfo for subprotos") |
| } |
| // nothing to check, we don't have backing store |
| return |
| } |
| |
| if m3.XXX_lazyUnmarshalInfo == nil { |
| t.Errorf("Inconsistent lazyUnmarshalInfo for subprotos (2)") |
| return |
| } |
| b = (*m2.XXX_lazyUnmarshalInfo).Protobuf |
| m2len := len(b) |
| for i := 0; i < len(b); i++ { |
| b[i] = byte(0xFF) |
| } |
| b = (*m3.XXX_lazyUnmarshalInfo).Protobuf |
| if m2len != 0 && len(b) == 0 { |
| t.Errorf("The lazy backing store for submessage is empty when it is not for the surronding message: %v.", m2len) |
| } |
| for i, x := range b { |
| if x != byte(0xFF) { |
| t.Errorf("Backing store for protocol buffer is not shared (index = %d, x = 0x%x)", i, x) |
| } |
| } |
| |
| } |