proto: never return nil []byte from Marshal when successful
It is a sufficiently common pattern to do something like the following:
m.Proto2BytesField, ... = proto.Marshal(m2)
where the user is relying on Marshal to never return a nil byte slice,
otherwise it subtly changes the semantics of how the generated API
handles whether a proto2 "optional bytes" fields is populated or not.
Change-Id: Ie7508dbcdcc5f3295885609a91907c6eb4f04c1e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/228838
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/proto/encode_test.go b/proto/encode_test.go
index d8aabd9..1cdbabc 100644
--- a/proto/encode_test.go
+++ b/proto/encode_test.go
@@ -247,3 +247,28 @@
t.Errorf("after round-trip marshal, got len(m.OptionalBytes) = %v, want %v", got, want)
}
}
+
+// TestEncodeEmpty tests for boundary conditions when producing an empty output.
+// These tests are not necessarily a statement of proper behavior,
+// but exist to detect accidental changes in behavior.
+func TestEncodeEmpty(t *testing.T) {
+ for _, m := range []proto.Message{nil, (*testpb.TestAllTypes)(nil), &testpb.TestAllTypes{}} {
+ isValid := m != nil && m.ProtoReflect().IsValid()
+
+ b, err := proto.Marshal(m)
+ if err != nil {
+ t.Errorf("proto.Marshal() = %v", err)
+ }
+ if isNil := b == nil; isNil == isValid {
+ t.Errorf("proto.Marshal() == nil: %v, want %v", isNil, !isValid)
+ }
+
+ b, err = proto.MarshalOptions{}.Marshal(m)
+ if err != nil {
+ t.Errorf("proto.MarshalOptions{}.Marshal() = %v", err)
+ }
+ if isNil := b == nil; isNil == isValid {
+ t.Errorf("proto.MarshalOptions{}.Marshal() = %v, want %v", isNil, !isValid)
+ }
+ }
+}