|  | // 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 proto_test | 
|  |  | 
|  | import ( | 
|  | "reflect" | 
|  | "testing" | 
|  | "unsafe" | 
|  |  | 
|  | "github.com/google/go-cmp/cmp" | 
|  | "google.golang.org/protobuf/internal/impl" | 
|  | testopaquepb "google.golang.org/protobuf/internal/testprotos/testeditions/testeditions_opaque" | 
|  | "google.golang.org/protobuf/proto" | 
|  | "google.golang.org/protobuf/testing/protocmp" | 
|  | ) | 
|  |  | 
|  | func fillLazyRequiredMessage() *testopaquepb.TestRequiredLazy { | 
|  | return testopaquepb.TestRequiredLazy_builder{ | 
|  | OptionalLazyMessage: testopaquepb.TestRequired_builder{ | 
|  | RequiredField: proto.Int32(12), | 
|  | }.Build(), | 
|  | }.Build() | 
|  | } | 
|  |  | 
|  | func expandedLazy(m *testopaquepb.TestRequiredLazy) bool { | 
|  | v := reflect.ValueOf(m).Elem() | 
|  | rf := v.FieldByName("xxx_hidden_OptionalLazyMessage") | 
|  | rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() | 
|  | return rf.Pointer() != 0 | 
|  | } | 
|  |  | 
|  | // This test ensures that a lazy field keeps being lazy when marshalling | 
|  | // even if it has required fields (as they have already been checked on | 
|  | // unmarshal) | 
|  | func TestLazyRequiredRoundtrip(t *testing.T) { | 
|  | if !impl.LazyEnabled() { | 
|  | t.Skipf("this test requires lazy decoding to be enabled") | 
|  | } | 
|  | m := fillLazyRequiredMessage() | 
|  | b, _ := proto.MarshalOptions{}.Marshal(m) | 
|  | ml := &testopaquepb.TestRequiredLazy{} | 
|  | err := proto.UnmarshalOptions{}.Unmarshal(b, ml) | 
|  | if err != nil { | 
|  | t.Fatalf("Error while unmarshaling: %v", err) | 
|  | } | 
|  | // Sanity check, we should have all unexpanded fields in the proto | 
|  | if expandedLazy(ml) { | 
|  | t.Fatalf("Proto is not lazy: %#v", ml) | 
|  | } | 
|  | // Now we marshal the lazy field. It should still be unexpanded | 
|  | _, _ = proto.MarshalOptions{}.Marshal(ml) | 
|  |  | 
|  | if expandedLazy(ml) { | 
|  | t.Errorf("Proto got expanded by marshal: %#v", ml) | 
|  | } | 
|  |  | 
|  | // The following tests the current behavior for cases where we | 
|  | // cannot guarantee the integrity of the lazy unmarshalled buffer | 
|  | // because of required fields. This would have to be updated if | 
|  | // we find another way to check required fields than simply | 
|  | // unmarshalling everything that has them when we're not sure. | 
|  |  | 
|  | ml = &testopaquepb.TestRequiredLazy{} | 
|  | err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(b, ml) | 
|  | if err != nil { | 
|  | t.Fatalf("Error while unmarshaling: %v", err) | 
|  | } | 
|  | // Sanity check, we should have all unexpanded fields in the proto. | 
|  | if expandedLazy(ml) { | 
|  | t.Fatalf("Proto is not lazy: %#v", ml) | 
|  | } | 
|  | // Now we marshal the proto. The lazy fields will be expanded to | 
|  | // check required fields. | 
|  | _, _ = proto.MarshalOptions{}.Marshal(ml) | 
|  |  | 
|  | if !expandedLazy(ml) { | 
|  | t.Errorf("Proto did not get expanded by marshal: %#v", ml) | 
|  | } | 
|  |  | 
|  | // Finally, we test to see that the fields to not get expanded | 
|  | // if we are consistently using AllowPartial both for marshal | 
|  | // and unmarshal. | 
|  | ml = &testopaquepb.TestRequiredLazy{} | 
|  | err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(b, ml) | 
|  | if err != nil { | 
|  | t.Fatalf("Error while unmarshaling: %v", err) | 
|  | } | 
|  | // Sanity check, we should have all unexpanded fields in the proto. | 
|  | if expandedLazy(ml) { | 
|  | t.Fatalf("Proto is not lazy: %#v", ml) | 
|  | } | 
|  | // Now we marshal the proto. The lazy fields will be expanded to | 
|  | // check required fields. | 
|  | _, _ = proto.MarshalOptions{AllowPartial: true}.Marshal(ml) | 
|  |  | 
|  | if expandedLazy(ml) { | 
|  | t.Errorf("Proto did not get expanded by marshal: %#v", ml) | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | func TestRoundtripMap(t *testing.T) { | 
|  | m := testopaquepb.TestAllTypes_builder{ | 
|  | OptionalLazyNestedMessage: testopaquepb.TestAllTypes_NestedMessage_builder{ | 
|  | Corecursive: testopaquepb.TestAllTypes_builder{ | 
|  | MapStringString: map[string]string{ | 
|  | "a": "b", | 
|  | }, | 
|  | }.Build(), | 
|  | }.Build(), | 
|  | }.Build() | 
|  | b, err := proto.Marshal(m) | 
|  | if err != nil { | 
|  | t.Fatalf("proto.Marshal: %v", err) | 
|  | } | 
|  | got := &testopaquepb.TestAllTypes{} | 
|  | if err := proto.Unmarshal(b, got); err != nil { | 
|  | t.Fatalf("proto.Unmarshal: %v", err) | 
|  | } | 
|  | if diff := cmp.Diff(m, got, protocmp.Transform()); diff != "" { | 
|  | t.Errorf("not the same: diff (-want +got):\n%s", diff) | 
|  | } | 
|  | } |