reflect/protoreflect: add ExtensionType IsValid{Interface,Value} methods
Add a way to typecheck a Value or interface{} without converting it to
the other form. This permits implementations which store field values as
a Value (such as dynamicpb, or (soon) extensions in generated messages)
to validate inputs without an unnecessary conversion.
Fixes golang/protobuf#905
Change-Id: I1b78612b22ae832efbb55f81ae420871729e3a02
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/192457
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/impl/extension_test.go b/internal/impl/extension_test.go
new file mode 100644
index 0000000..d6353ed
--- /dev/null
+++ b/internal/impl/extension_test.go
@@ -0,0 +1,130 @@
+// Copyright 2019 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 (
+ "fmt"
+ "testing"
+
+ "github.com/golang/protobuf/proto"
+ cmp "github.com/google/go-cmp/cmp"
+ testpb "google.golang.org/protobuf/internal/testprotos/test"
+ pref "google.golang.org/protobuf/reflect/protoreflect"
+)
+
+func TestExtensionType(t *testing.T) {
+ cmpOpts := cmp.Options{
+ cmp.Comparer(func(x, y proto.Message) bool {
+ return proto.Equal(x, y)
+ }),
+ }
+ for _, test := range []struct {
+ xt pref.ExtensionType
+ value interface{}
+ }{
+ {
+ xt: testpb.E_OptionalInt32Extension,
+ value: int32(0),
+ },
+ {
+ xt: testpb.E_OptionalInt64Extension,
+ value: int64(0),
+ },
+ {
+ xt: testpb.E_OptionalUint32Extension,
+ value: uint32(0),
+ },
+ {
+ xt: testpb.E_OptionalUint64Extension,
+ value: uint64(0),
+ },
+ {
+ xt: testpb.E_OptionalFloatExtension,
+ value: float32(0),
+ },
+ {
+ xt: testpb.E_OptionalDoubleExtension,
+ value: float64(0),
+ },
+ {
+ xt: testpb.E_OptionalBoolExtension,
+ value: true,
+ },
+ {
+ xt: testpb.E_OptionalStringExtension,
+ value: "",
+ },
+ {
+ xt: testpb.E_OptionalBytesExtension,
+ value: []byte{},
+ },
+ {
+ xt: testpb.E_OptionalNestedMessageExtension,
+ value: &testpb.TestAllTypes_NestedMessage{},
+ },
+ {
+ xt: testpb.E_OptionalNestedEnumExtension,
+ value: testpb.TestAllTypes_FOO,
+ },
+ {
+ xt: testpb.E_RepeatedInt32Extension,
+ value: []int32{0},
+ },
+ {
+ xt: testpb.E_RepeatedInt64Extension,
+ value: []int64{0},
+ },
+ {
+ xt: testpb.E_RepeatedUint32Extension,
+ value: []uint32{0},
+ },
+ {
+ xt: testpb.E_RepeatedUint64Extension,
+ value: []uint64{0},
+ },
+ {
+ xt: testpb.E_RepeatedFloatExtension,
+ value: []float32{0},
+ },
+ {
+ xt: testpb.E_RepeatedDoubleExtension,
+ value: []float64{0},
+ },
+ {
+ xt: testpb.E_RepeatedBoolExtension,
+ value: []bool{true},
+ },
+ {
+ xt: testpb.E_RepeatedStringExtension,
+ value: []string{""},
+ },
+ {
+ xt: testpb.E_RepeatedBytesExtension,
+ value: [][]byte{nil},
+ },
+ {
+ xt: testpb.E_RepeatedNestedMessageExtension,
+ value: []*testpb.TestAllTypes_NestedMessage{{}},
+ },
+ {
+ xt: testpb.E_RepeatedNestedEnumExtension,
+ value: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO},
+ },
+ } {
+ name := test.xt.TypeDescriptor().FullName()
+ t.Run(fmt.Sprint(name), func(t *testing.T) {
+ if !test.xt.IsValidInterface(test.value) {
+ t.Fatalf("IsValidInterface(%[1]T(%[1]v)) = false, want true", test.value)
+ }
+ v := test.xt.ValueOf(test.value)
+ if !test.xt.IsValidValue(v) {
+ t.Fatalf("IsValidValue(%[1]T(%[1]v)) = false, want true", v)
+ }
+ if got, want := test.xt.InterfaceOf(v), test.value; !cmp.Equal(got, want, cmpOpts) {
+ t.Fatalf("round trip InterfaceOf(ValueOf(x)) = %v, want %v", got, want)
+ }
+ })
+ }
+}