internal/filedesc, internal/filetype: initial commit

The internal/fileinit package is split apart into two packages:
* internal/filedesc constructs descriptors from the raw proto.
It is very similar to the previous internal/fileinit package.
* internal/filetype wraps descriptors with Go type information

Overview:
* The internal/fileinit package will be deleted in a future CL.
It is kept around since the v1 repo currently depends on it.
* The internal/prototype package is deleted. All former usages of it
are now using internal/filedesc instead. Most significantly,
the reflect/protodesc package was almost entirely re-written.
* The internal/impl package drops support for messages that do not
have a Descriptor method (pre-2016). This removes a significant amount
of technical debt.
filedesc.Builder to parse raw descriptors.
* The internal/encoding/defval package now handles enum values by name.

Change-Id: I3957bcc8588a70470fd6c7de1122216b80615ab7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/182360
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/filedesc/desc_test.go b/internal/filedesc/desc_test.go
new file mode 100644
index 0000000..b6e0f7d
--- /dev/null
+++ b/internal/filedesc/desc_test.go
@@ -0,0 +1,772 @@
+// Copyright 2018 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 filedesc_test
+
+import (
+	"fmt"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	detrand "google.golang.org/protobuf/internal/detrand"
+	"google.golang.org/protobuf/internal/filedesc"
+	scalar "google.golang.org/protobuf/internal/scalar"
+	"google.golang.org/protobuf/proto"
+	pdesc "google.golang.org/protobuf/reflect/protodesc"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+
+	"google.golang.org/protobuf/types/descriptorpb"
+)
+
+func init() {
+	// Disable detrand to enable direct comparisons on outputs.
+	detrand.Disable()
+}
+
+// TODO: Test protodesc.NewFile with imported files.
+
+func TestFile(t *testing.T) {
+	f1 := &descriptorpb.FileDescriptorProto{
+		Syntax:  scalar.String("proto2"),
+		Name:    scalar.String("path/to/file.proto"),
+		Package: scalar.String("test"),
+		Options: &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
+		MessageType: []*descriptorpb.DescriptorProto{{
+			Name: scalar.String("A"),
+			Options: &descriptorpb.MessageOptions{
+				MapEntry:   scalar.Bool(true),
+				Deprecated: scalar.Bool(true),
+			},
+			Field: []*descriptorpb.FieldDescriptorProto{{
+				Name:    scalar.String("key"),
+				Number:  scalar.Int32(1),
+				Options: &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
+				Label:   descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:    descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+			}, {
+				Name:     scalar.String("value"),
+				Number:   scalar.Int32(2),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: scalar.String(".test.B"),
+			}},
+		}, {
+			Name: scalar.String("B"),
+			Field: []*descriptorpb.FieldDescriptorProto{{
+				Name:         scalar.String("field_one"),
+				Number:       scalar.Int32(1),
+				Label:        descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:         descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+				DefaultValue: scalar.String("hello, \"world!\"\n"),
+				OneofIndex:   scalar.Int32(0),
+			}, {
+				Name:         scalar.String("field_two"),
+				JsonName:     scalar.String("Field2"),
+				Number:       scalar.Int32(2),
+				Label:        descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:         descriptorpb.FieldDescriptorProto_Type(pref.EnumKind).Enum(),
+				DefaultValue: scalar.String("BAR"),
+				TypeName:     scalar.String(".test.E1"),
+				OneofIndex:   scalar.Int32(1),
+			}, {
+				Name:       scalar.String("field_three"),
+				Number:     scalar.Int32(3),
+				Label:      descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:       descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName:   scalar.String(".test.C"),
+				OneofIndex: scalar.Int32(1),
+			}, {
+				Name:     scalar.String("field_four"),
+				JsonName: scalar.String("Field4"),
+				Number:   scalar.Int32(4),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: scalar.String(".test.A"),
+			}, {
+				Name:    scalar.String("field_five"),
+				Number:  scalar.Int32(5),
+				Label:   descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:    descriptorpb.FieldDescriptorProto_Type(pref.Int32Kind).Enum(),
+				Options: &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+			}, {
+				Name:   scalar.String("field_six"),
+				Number: scalar.Int32(6),
+				Label:  descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(),
+				Type:   descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+			}},
+			OneofDecl: []*descriptorpb.OneofDescriptorProto{
+				{
+					Name: scalar.String("O1"),
+					Options: &descriptorpb.OneofOptions{
+						UninterpretedOption: []*descriptorpb.UninterpretedOption{
+							{StringValue: []byte("option")},
+						},
+					},
+				},
+				{Name: scalar.String("O2")},
+			},
+			ReservedName: []string{"fizz", "buzz"},
+			ReservedRange: []*descriptorpb.DescriptorProto_ReservedRange{
+				{Start: scalar.Int32(100), End: scalar.Int32(200)},
+				{Start: scalar.Int32(300), End: scalar.Int32(301)},
+			},
+			ExtensionRange: []*descriptorpb.DescriptorProto_ExtensionRange{
+				{Start: scalar.Int32(1000), End: scalar.Int32(2000)},
+				{Start: scalar.Int32(3000), End: scalar.Int32(3001), Options: new(descriptorpb.ExtensionRangeOptions)},
+			},
+		}, {
+			Name: scalar.String("C"),
+			NestedType: []*descriptorpb.DescriptorProto{{
+				Name: scalar.String("A"),
+				Field: []*descriptorpb.FieldDescriptorProto{{
+					Name:         scalar.String("F"),
+					Number:       scalar.Int32(1),
+					Label:        descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(),
+					Type:         descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+					DefaultValue: scalar.String(`dead\276\357`),
+				}},
+			}},
+			EnumType: []*descriptorpb.EnumDescriptorProto{{
+				Name: scalar.String("E1"),
+				Value: []*descriptorpb.EnumValueDescriptorProto{
+					{Name: scalar.String("FOO"), Number: scalar.Int32(0)},
+					{Name: scalar.String("BAR"), Number: scalar.Int32(1)},
+				},
+			}},
+			Extension: []*descriptorpb.FieldDescriptorProto{{
+				Name:     scalar.String("X"),
+				Number:   scalar.Int32(1000),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				TypeName: scalar.String(".test.C"),
+				Extendee: scalar.String(".test.B"),
+			}},
+		}},
+		EnumType: []*descriptorpb.EnumDescriptorProto{{
+			Name:    scalar.String("E1"),
+			Options: &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
+			Value: []*descriptorpb.EnumValueDescriptorProto{
+				{
+					Name:    scalar.String("FOO"),
+					Number:  scalar.Int32(0),
+					Options: &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
+				},
+				{Name: scalar.String("BAR"), Number: scalar.Int32(1)},
+			},
+			ReservedName: []string{"FIZZ", "BUZZ"},
+			ReservedRange: []*descriptorpb.EnumDescriptorProto_EnumReservedRange{
+				{Start: scalar.Int32(10), End: scalar.Int32(19)},
+				{Start: scalar.Int32(30), End: scalar.Int32(30)},
+			},
+		}},
+		Extension: []*descriptorpb.FieldDescriptorProto{{
+			Name:     scalar.String("X"),
+			Number:   scalar.Int32(1000),
+			Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+			Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+			Options:  &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+			TypeName: scalar.String(".test.C"),
+			Extendee: scalar.String(".test.B"),
+		}},
+		Service: []*descriptorpb.ServiceDescriptorProto{{
+			Name:    scalar.String("S"),
+			Options: &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
+			Method: []*descriptorpb.MethodDescriptorProto{{
+				Name:            scalar.String("M"),
+				InputType:       scalar.String(".test.A"),
+				OutputType:      scalar.String(".test.C.A"),
+				ClientStreaming: scalar.Bool(true),
+				ServerStreaming: scalar.Bool(true),
+				Options:         &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
+			}},
+		}},
+	}
+	fd1, err := pdesc.NewFile(f1, nil)
+	if err != nil {
+		t.Fatalf("protodesc.NewFile() error: %v", err)
+	}
+
+	b, err := proto.Marshal(f1)
+	if err != nil {
+		t.Fatalf("proto.Marshal() error: %v", err)
+	}
+	fd2 := filedesc.DescBuilder{RawDescriptor: b}.Build().File
+
+	tests := []struct {
+		name string
+		desc pref.FileDescriptor
+	}{
+		{"protodesc.NewFile", fd1},
+		{"filedesc.DescBuilder.Build", fd2},
+	}
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			// Run sub-tests in parallel to induce potential races.
+			for i := 0; i < 2; i++ {
+				t.Run("Accessors", func(t *testing.T) { t.Parallel(); testFileAccessors(t, tt.desc) })
+				t.Run("Format", func(t *testing.T) { t.Parallel(); testFileFormat(t, tt.desc) })
+			}
+		})
+	}
+}
+
+func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
+	// Represent the descriptor as a map where each key is an accessor method
+	// and the value is either the wanted tail value or another accessor map.
+	type M = map[string]interface{}
+	want := M{
+		"Parent":        nil,
+		"Index":         0,
+		"Syntax":        pref.Proto2,
+		"Name":          pref.Name("test"),
+		"FullName":      pref.FullName("test"),
+		"Path":          "path/to/file.proto",
+		"Package":       pref.FullName("test"),
+		"IsPlaceholder": false,
+		"Options":       &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
+		"Messages": M{
+			"Len": 3,
+			"Get:0": M{
+				"Parent":        M{"FullName": pref.FullName("test")},
+				"Index":         0,
+				"Syntax":        pref.Proto2,
+				"Name":          pref.Name("A"),
+				"FullName":      pref.FullName("test.A"),
+				"IsPlaceholder": false,
+				"IsMapEntry":    true,
+				"Options": &descriptorpb.MessageOptions{
+					MapEntry:   scalar.Bool(true),
+					Deprecated: scalar.Bool(true),
+				},
+				"Fields": M{
+					"Len": 2,
+					"ByNumber:1": M{
+						"Parent":            M{"FullName": pref.FullName("test.A")},
+						"Index":             0,
+						"Name":              pref.Name("key"),
+						"FullName":          pref.FullName("test.A.key"),
+						"Number":            pref.FieldNumber(1),
+						"Cardinality":       pref.Optional,
+						"Kind":              pref.StringKind,
+						"Options":           &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
+						"HasJSONName":       false,
+						"JSONName":          "key",
+						"IsPacked":          false,
+						"IsList":            false,
+						"IsMap":             false,
+						"IsExtension":       false,
+						"IsWeak":            false,
+						"Default":           "",
+						"ContainingOneof":   nil,
+						"ContainingMessage": M{"FullName": pref.FullName("test.A")},
+						"Message":           nil,
+						"Enum":              nil,
+					},
+					"ByNumber:2": M{
+						"Parent":            M{"FullName": pref.FullName("test.A")},
+						"Index":             1,
+						"Name":              pref.Name("value"),
+						"FullName":          pref.FullName("test.A.value"),
+						"Number":            pref.FieldNumber(2),
+						"Cardinality":       pref.Optional,
+						"Kind":              pref.MessageKind,
+						"JSONName":          "value",
+						"IsPacked":          false,
+						"IsList":            false,
+						"IsMap":             false,
+						"IsExtension":       false,
+						"IsWeak":            false,
+						"Default":           nil,
+						"ContainingOneof":   nil,
+						"ContainingMessage": M{"FullName": pref.FullName("test.A")},
+						"Message":           M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
+						"Enum":              nil,
+					},
+					"ByNumber:3": nil,
+				},
+				"Oneofs":          M{"Len": 0},
+				"RequiredNumbers": M{"Len": 0},
+				"ExtensionRanges": M{"Len": 0},
+				"Messages":        M{"Len": 0},
+				"Enums":           M{"Len": 0},
+				"Extensions":      M{"Len": 0},
+			},
+			"ByName:B": M{
+				"Name":  pref.Name("B"),
+				"Index": 1,
+				"Fields": M{
+					"Len":                  6,
+					"ByJSONName:field_one": nil,
+					"ByJSONName:fieldOne": M{
+						"Name":              pref.Name("field_one"),
+						"Index":             0,
+						"JSONName":          "fieldOne",
+						"Default":           "hello, \"world!\"\n",
+						"ContainingOneof":   M{"Name": pref.Name("O1"), "IsPlaceholder": false},
+						"ContainingMessage": M{"FullName": pref.FullName("test.B")},
+					},
+					"ByJSONName:fieldTwo": nil,
+					"ByJSONName:Field2": M{
+						"Name":            pref.Name("field_two"),
+						"Index":           1,
+						"HasJSONName":     true,
+						"JSONName":        "Field2",
+						"Default":         pref.EnumNumber(1),
+						"ContainingOneof": M{"Name": pref.Name("O2"), "IsPlaceholder": false},
+					},
+					"ByName:fieldThree": nil,
+					"ByName:field_three": M{
+						"IsExtension":       false,
+						"IsMap":             false,
+						"MapKey":            nil,
+						"MapValue":          nil,
+						"Message":           M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
+						"ContainingOneof":   M{"Name": pref.Name("O2"), "IsPlaceholder": false},
+						"ContainingMessage": M{"FullName": pref.FullName("test.B")},
+					},
+					"ByNumber:12": nil,
+					"ByNumber:4": M{
+						"Cardinality": pref.Repeated,
+						"IsExtension": false,
+						"IsList":      false,
+						"IsMap":       true,
+						"MapKey":      M{"Kind": pref.StringKind},
+						"MapValue":    M{"Kind": pref.MessageKind, "Message": M{"FullName": pref.FullName("test.B")}},
+						"Default":     nil,
+						"Message":     M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
+					},
+					"ByNumber:5": M{
+						"Cardinality": pref.Repeated,
+						"Kind":        pref.Int32Kind,
+						"IsPacked":    true,
+						"IsList":      true,
+						"IsMap":       false,
+						"Default":     int32(0),
+					},
+					"ByNumber:6": M{
+						"Cardinality":     pref.Required,
+						"Default":         []byte(nil),
+						"ContainingOneof": nil,
+					},
+				},
+				"Oneofs": M{
+					"Len":       2,
+					"ByName:O0": nil,
+					"ByName:O1": M{
+						"FullName": pref.FullName("test.B.O1"),
+						"Index":    0,
+						"Options": &descriptorpb.OneofOptions{
+							UninterpretedOption: []*descriptorpb.UninterpretedOption{
+								{StringValue: []byte("option")},
+							},
+						},
+						"Fields": M{
+							"Len":   1,
+							"Get:0": M{"FullName": pref.FullName("test.B.field_one")},
+						},
+					},
+					"Get:1": M{
+						"FullName": pref.FullName("test.B.O2"),
+						"Index":    1,
+						"Fields": M{
+							"Len":              2,
+							"ByName:field_two": M{"Name": pref.Name("field_two")},
+							"Get:1":            M{"Name": pref.Name("field_three")},
+						},
+					},
+				},
+				"ReservedNames": M{
+					"Len":         2,
+					"Get:0":       pref.Name("fizz"),
+					"Has:buzz":    true,
+					"Has:noexist": false,
+				},
+				"ReservedRanges": M{
+					"Len":     2,
+					"Get:0":   [2]pref.FieldNumber{100, 200},
+					"Has:99":  false,
+					"Has:100": true,
+					"Has:150": true,
+					"Has:199": true,
+					"Has:200": false,
+					"Has:300": true,
+					"Has:301": false,
+				},
+				"RequiredNumbers": M{
+					"Len":   1,
+					"Get:0": pref.FieldNumber(6),
+					"Has:1": false,
+					"Has:6": true,
+				},
+				"ExtensionRanges": M{
+					"Len":      2,
+					"Get:0":    [2]pref.FieldNumber{1000, 2000},
+					"Has:999":  false,
+					"Has:1000": true,
+					"Has:1500": true,
+					"Has:1999": true,
+					"Has:2000": false,
+					"Has:3000": true,
+					"Has:3001": false,
+				},
+				"ExtensionRangeOptions:0": (*descriptorpb.ExtensionRangeOptions)(nil),
+				"ExtensionRangeOptions:1": new(descriptorpb.ExtensionRangeOptions),
+			},
+			"Get:2": M{
+				"Name":  pref.Name("C"),
+				"Index": 2,
+				"Messages": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.A")},
+				},
+				"Enums": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.E1")},
+				},
+				"Extensions": M{
+					"Len":   1,
+					"Get:0": M{"FullName": pref.FullName("test.C.X")},
+				},
+			},
+		},
+		"Enums": M{
+			"Len": 1,
+			"Get:0": M{
+				"Name":    pref.Name("E1"),
+				"Options": &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
+				"Values": M{
+					"Len":        2,
+					"ByName:Foo": nil,
+					"ByName:FOO": M{
+						"FullName": pref.FullName("test.FOO"),
+						"Options":  &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
+					},
+					"ByNumber:2": nil,
+					"ByNumber:1": M{"FullName": pref.FullName("test.BAR")},
+				},
+				"ReservedNames": M{
+					"Len":         2,
+					"Get:0":       pref.Name("FIZZ"),
+					"Has:BUZZ":    true,
+					"Has:NOEXIST": false,
+				},
+				"ReservedRanges": M{
+					"Len":    2,
+					"Get:0":  [2]pref.EnumNumber{10, 19},
+					"Has:9":  false,
+					"Has:10": true,
+					"Has:15": true,
+					"Has:19": true,
+					"Has:20": false,
+					"Has:30": true,
+					"Has:31": false,
+				},
+			},
+		},
+		"Extensions": M{
+			"Len": 1,
+			"ByName:X": M{
+				"Name":              pref.Name("X"),
+				"Number":            pref.FieldNumber(1000),
+				"Cardinality":       pref.Repeated,
+				"Kind":              pref.MessageKind,
+				"IsExtension":       true,
+				"IsPacked":          true,
+				"IsList":            true,
+				"IsMap":             false,
+				"MapKey":            nil,
+				"MapValue":          nil,
+				"ContainingOneof":   nil,
+				"ContainingMessage": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
+				"Message":           M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
+				"Options":           &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+			},
+		},
+		"Services": M{
+			"Len":      1,
+			"ByName:s": nil,
+			"ByName:S": M{
+				"Parent":   M{"FullName": pref.FullName("test")},
+				"Name":     pref.Name("S"),
+				"FullName": pref.FullName("test.S"),
+				"Options":  &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
+				"Methods": M{
+					"Len": 1,
+					"Get:0": M{
+						"Parent":            M{"FullName": pref.FullName("test.S")},
+						"Name":              pref.Name("M"),
+						"FullName":          pref.FullName("test.S.M"),
+						"Input":             M{"FullName": pref.FullName("test.A"), "IsPlaceholder": false},
+						"Output":            M{"FullName": pref.FullName("test.C.A"), "IsPlaceholder": false},
+						"IsStreamingClient": true,
+						"IsStreamingServer": true,
+						"Options":           &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
+					},
+				},
+			},
+		},
+	}
+	checkAccessors(t, "", reflect.ValueOf(fd), want)
+}
+func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) {
+	p0 := p
+	defer func() {
+		if ex := recover(); ex != nil {
+			t.Errorf("panic at %v: %v", p, ex)
+		}
+	}()
+
+	if rv.Interface() == nil {
+		t.Errorf("%v is nil, want non-nil", p)
+		return
+	}
+	for s, v := range want {
+		// Call the accessor method.
+		p = p0 + "." + s
+		var rets []reflect.Value
+		if i := strings.IndexByte(s, ':'); i >= 0 {
+			// Accessor method takes in a single argument, which is encoded
+			// after the accessor name, separated by a ':' delimiter.
+			fnc := rv.MethodByName(s[:i])
+			arg := reflect.New(fnc.Type().In(0)).Elem()
+			s = s[i+len(":"):]
+			switch arg.Kind() {
+			case reflect.String:
+				arg.SetString(s)
+			case reflect.Int32, reflect.Int:
+				n, _ := strconv.ParseInt(s, 0, 64)
+				arg.SetInt(n)
+			}
+			rets = fnc.Call([]reflect.Value{arg})
+		} else {
+			rets = rv.MethodByName(s).Call(nil)
+		}
+
+		// Check that (val, ok) pattern is internally consistent.
+		if len(rets) == 2 {
+			if rets[0].IsNil() && rets[1].Bool() {
+				t.Errorf("%v = (nil, true), want (nil, false)", p)
+			}
+			if !rets[0].IsNil() && !rets[1].Bool() {
+				t.Errorf("%v = (non-nil, false), want (non-nil, true)", p)
+			}
+		}
+
+		// Check that the accessor output matches.
+		if want, ok := v.(map[string]interface{}); ok {
+			checkAccessors(t, p, rets[0], want)
+			continue
+		}
+
+		got := rets[0].Interface()
+		if pv, ok := got.(pref.Value); ok {
+			got = pv.Interface()
+		}
+
+		// Compare with proto.Equal if possible.
+		gotMsg, gotMsgOK := got.(proto.Message)
+		wantMsg, wantMsgOK := v.(proto.Message)
+		if gotMsgOK && wantMsgOK {
+			gotNil := reflect.ValueOf(gotMsg).IsNil()
+			wantNil := reflect.ValueOf(wantMsg).IsNil()
+			switch {
+			case !gotNil && wantNil:
+				t.Errorf("%v = non-nil, want nil", p)
+			case gotNil && !wantNil:
+				t.Errorf("%v = nil, want non-nil", p)
+			case !proto.Equal(gotMsg, wantMsg):
+				t.Errorf("%v = %v, want %v", p, gotMsg, wantMsg)
+			}
+			continue
+		}
+
+		if want := v; !reflect.DeepEqual(got, want) {
+			t.Errorf("%v = %T(%v), want %T(%v)", p, got, got, want, want)
+		}
+	}
+}
+
+func testFileFormat(t *testing.T, fd pref.FileDescriptor) {
+	const want = `FileDescriptor{
+	Syntax:  proto2
+	Path:    "path/to/file.proto"
+	Package: test
+	Messages: [{
+		Name:       A
+		IsMapEntry: true
+		Fields: [{
+			Name:        key
+			Number:      1
+			Cardinality: optional
+			Kind:        string
+			JSONName:    "key"
+		}, {
+			Name:        value
+			Number:      2
+			Cardinality: optional
+			Kind:        message
+			JSONName:    "value"
+			Message:     test.B
+		}]
+	}, {
+		Name: B
+		Fields: [{
+			Name:        field_one
+			Number:      1
+			Cardinality: optional
+			Kind:        string
+			JSONName:    "fieldOne"
+			HasDefault:  true
+			Default:     "hello, \"world!\"\n"
+			Oneof:       O1
+		}, {
+			Name:        field_two
+			Number:      2
+			Cardinality: optional
+			Kind:        enum
+			HasJSONName: true
+			JSONName:    "Field2"
+			HasDefault:  true
+			Default:     1
+			Oneof:       O2
+			Enum:        test.E1
+		}, {
+			Name:        field_three
+			Number:      3
+			Cardinality: optional
+			Kind:        message
+			JSONName:    "fieldThree"
+			Oneof:       O2
+			Message:     test.C
+		}, {
+			Name:        field_four
+			Number:      4
+			Cardinality: repeated
+			Kind:        message
+			HasJSONName: true
+			JSONName:    "Field4"
+			IsMap:       true
+			MapKey:      string
+			MapValue:    test.B
+		}, {
+			Name:        field_five
+			Number:      5
+			Cardinality: repeated
+			Kind:        int32
+			JSONName:    "fieldFive"
+			IsPacked:    true
+			IsList:      true
+		}, {
+			Name:        field_six
+			Number:      6
+			Cardinality: required
+			Kind:        bytes
+			JSONName:    "fieldSix"
+		}]
+		Oneofs: [{
+			Name:   O1
+			Fields: [field_one]
+		}, {
+			Name:   O2
+			Fields: [field_two, field_three]
+		}]
+		ReservedNames:   [fizz, buzz]
+		ReservedRanges:  [100:200, 300]
+		RequiredNumbers: [6]
+		ExtensionRanges: [1000:2000, 3000]
+	}, {
+		Name: C
+		Messages: [{
+			Name: A
+			Fields: [{
+				Name:        F
+				Number:      1
+				Cardinality: required
+				Kind:        bytes
+				JSONName:    "F"
+				HasDefault:  true
+				Default:     "dead\xbe\xef"
+			}]
+			RequiredNumbers: [1]
+		}]
+		Enums: [{
+			Name: E1
+			Values: [
+				{Name: FOO}
+				{Name: BAR, Number: 1}
+			]
+		}]
+		Extensions: [{
+			Name:        X
+			Number:      1000
+			Cardinality: repeated
+			Kind:        message
+			JSONName:    "X"
+			IsExtension: true
+			IsList:      true
+			Extendee:    test.B
+			Message:     test.C
+		}]
+	}]
+	Enums: [{
+		Name: E1
+		Values: [
+			{Name: FOO}
+			{Name: BAR, Number: 1}
+		]
+		ReservedNames:  [FIZZ, BUZZ]
+		ReservedRanges: [10:20, 30]
+	}]
+	Extensions: [{
+		Name:        X
+		Number:      1000
+		Cardinality: repeated
+		Kind:        message
+		JSONName:    "X"
+		IsPacked:    true
+		IsExtension: true
+		IsList:      true
+		Extendee:    test.B
+		Message:     test.C
+	}]
+	Services: [{
+		Name: S
+		Methods: [{
+			Name:              M
+			Input:             test.A
+			Output:            test.C.A
+			IsStreamingClient: true
+			IsStreamingServer: true
+		}]
+	}]
+}`
+	tests := []struct{ fmt, want string }{{"%v", compactMultiFormat(want)}, {"%+v", want}}
+	for _, tt := range tests {
+		got := fmt.Sprintf(tt.fmt, fd)
+		if diff := cmp.Diff(got, tt.want); diff != "" {
+			t.Errorf("fmt.Sprintf(%q, fd) mismatch (-got +want):\n%s", tt.fmt, diff)
+		}
+	}
+}
+
+// compactMultiFormat returns the single line form of a multi line output.
+func compactMultiFormat(s string) string {
+	var b []byte
+	for _, s := range strings.Split(s, "\n") {
+		s = strings.TrimSpace(s)
+		s = regexp.MustCompile(": +").ReplaceAllString(s, ": ")
+		prevWord := len(b) > 0 && b[len(b)-1] != '[' && b[len(b)-1] != '{'
+		nextWord := len(s) > 0 && s[0] != ']' && s[0] != '}'
+		if prevWord && nextWord {
+			b = append(b, ", "...)
+		}
+		b = append(b, s...)
+	}
+	return string(b)
+}