1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package protocmp
6
7import (
8	"math"
9	"math/rand"
10	"sort"
11	"testing"
12
13	"github.com/google/go-cmp/cmp"
14
15	"google.golang.org/protobuf/proto"
16	"google.golang.org/protobuf/reflect/protoreflect"
17	"google.golang.org/protobuf/testing/protopack"
18	"google.golang.org/protobuf/types/dynamicpb"
19
20	testpb "google.golang.org/protobuf/internal/testprotos/test"
21)
22
23func TestEqual(t *testing.T) {
24	type test struct {
25		x, y interface{}
26		opts cmp.Options
27		want bool
28	}
29	var tests []test
30
31	allTypesDesc := (*testpb.TestAllTypes)(nil).ProtoReflect().Descriptor()
32
33	// Test nil and empty messages of differing types.
34	tests = append(tests, []test{{
35		x:    (*testpb.TestAllTypes)(nil),
36		y:    (*testpb.TestAllTypes)(nil),
37		opts: cmp.Options{Transform()},
38		want: true,
39	}, {
40		x:    (*testpb.TestAllTypes)(nil),
41		y:    (*testpb.TestAllExtensions)(nil),
42		opts: cmp.Options{Transform()},
43		want: false,
44	}, {
45		x:    (*testpb.TestAllTypes)(nil),
46		y:    new(testpb.TestAllTypes),
47		opts: cmp.Options{Transform()},
48		want: false,
49	}, {
50		x:    (*testpb.TestAllTypes)(nil),
51		y:    dynamicpb.NewMessage(allTypesDesc),
52		opts: cmp.Options{Transform()},
53		want: false,
54	}, {
55		x:    (*testpb.TestAllTypes)(nil),
56		y:    new(testpb.TestAllTypes),
57		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
58		want: true,
59	}, {
60		x:    (*testpb.TestAllTypes)(nil),
61		y:    dynamicpb.NewMessage(allTypesDesc),
62		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
63		want: true,
64	}, {
65		x:    new(testpb.TestAllTypes),
66		y:    new(testpb.TestAllTypes),
67		opts: cmp.Options{Transform()},
68		want: true,
69	}, {
70		x:    new(testpb.TestAllTypes),
71		y:    dynamicpb.NewMessage(allTypesDesc),
72		opts: cmp.Options{Transform()},
73		want: true,
74	}, {
75		x:    new(testpb.TestAllTypes),
76		y:    new(testpb.TestAllExtensions),
77		opts: cmp.Options{Transform()},
78		want: false,
79	}, {
80		x:    struct{ I interface{} }{(*testpb.TestAllTypes)(nil)},
81		y:    struct{ I interface{} }{(*testpb.TestAllTypes)(nil)},
82		opts: cmp.Options{Transform()},
83		want: true,
84	}, {
85		x:    struct{ I interface{} }{(*testpb.TestAllTypes)(nil)},
86		y:    struct{ I interface{} }{new(testpb.TestAllTypes)},
87		opts: cmp.Options{Transform()},
88		want: false,
89	}, {
90		x:    struct{ I interface{} }{(*testpb.TestAllTypes)(nil)},
91		y:    struct{ I interface{} }{dynamicpb.NewMessage(allTypesDesc)},
92		opts: cmp.Options{Transform()},
93		want: false,
94	}, {
95		x:    struct{ I interface{} }{(*testpb.TestAllTypes)(nil)},
96		y:    struct{ I interface{} }{new(testpb.TestAllTypes)},
97		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
98		want: true,
99	}, {
100		x:    struct{ I interface{} }{(*testpb.TestAllTypes)(nil)},
101		y:    struct{ I interface{} }{dynamicpb.NewMessage(allTypesDesc)},
102		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
103		want: true,
104	}, {
105		x:    struct{ I interface{} }{new(testpb.TestAllTypes)},
106		y:    struct{ I interface{} }{new(testpb.TestAllTypes)},
107		opts: cmp.Options{Transform()},
108		want: true,
109	}, {
110		x:    struct{ I interface{} }{new(testpb.TestAllTypes)},
111		y:    struct{ I interface{} }{dynamicpb.NewMessage(allTypesDesc)},
112		opts: cmp.Options{Transform()},
113		want: true,
114	}, {
115		x:    struct{ M proto.Message }{(*testpb.TestAllTypes)(nil)},
116		y:    struct{ M proto.Message }{(*testpb.TestAllTypes)(nil)},
117		opts: cmp.Options{Transform()},
118		want: true,
119	}, {
120		x:    struct{ M proto.Message }{(*testpb.TestAllTypes)(nil)},
121		y:    struct{ M proto.Message }{new(testpb.TestAllTypes)},
122		opts: cmp.Options{Transform()},
123		want: false,
124	}, {
125		x:    struct{ M proto.Message }{(*testpb.TestAllTypes)(nil)},
126		y:    struct{ M proto.Message }{dynamicpb.NewMessage(allTypesDesc)},
127		opts: cmp.Options{Transform()},
128		want: false,
129	}, {
130		x:    struct{ M proto.Message }{(*testpb.TestAllTypes)(nil)},
131		y:    struct{ M proto.Message }{new(testpb.TestAllTypes)},
132		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
133		want: true,
134	}, {
135		x:    struct{ M proto.Message }{(*testpb.TestAllTypes)(nil)},
136		y:    struct{ M proto.Message }{dynamicpb.NewMessage(allTypesDesc)},
137		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
138		want: true,
139	}, {
140		x:    struct{ M proto.Message }{new(testpb.TestAllTypes)},
141		y:    struct{ M proto.Message }{new(testpb.TestAllTypes)},
142		opts: cmp.Options{Transform()},
143		want: true,
144	}, {
145		x:    struct{ M proto.Message }{new(testpb.TestAllTypes)},
146		y:    struct{ M proto.Message }{dynamicpb.NewMessage(allTypesDesc)},
147		opts: cmp.Options{Transform()},
148		want: true,
149	}}...)
150
151	// Test message values.
152	tests = append(tests, []test{{
153		x:    testpb.TestAllTypes{OptionalSint64: proto.Int64(1)},
154		y:    testpb.TestAllTypes{OptionalSint64: proto.Int64(1)},
155		opts: cmp.Options{Transform()},
156		want: true,
157	}, {
158		x:    testpb.TestAllTypes{OptionalSint64: proto.Int64(1)},
159		y:    testpb.TestAllTypes{OptionalSint64: proto.Int64(2)},
160		opts: cmp.Options{Transform()},
161		want: false,
162	}, {
163		x:    struct{ M testpb.TestAllTypes }{M: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)}},
164		y:    struct{ M testpb.TestAllTypes }{M: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)}},
165		opts: cmp.Options{Transform()},
166		want: true,
167	}, {
168		x:    struct{ M testpb.TestAllTypes }{M: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)}},
169		y:    struct{ M testpb.TestAllTypes }{M: testpb.TestAllTypes{OptionalSint64: proto.Int64(2)}},
170		opts: cmp.Options{Transform()},
171		want: false,
172	}, {
173		x:    struct{ M []testpb.TestAllTypes }{M: []testpb.TestAllTypes{{OptionalSint64: proto.Int64(1)}}},
174		y:    struct{ M []testpb.TestAllTypes }{M: []testpb.TestAllTypes{{OptionalSint64: proto.Int64(1)}}},
175		opts: cmp.Options{Transform()},
176		want: true,
177	}, {
178		x:    struct{ M []testpb.TestAllTypes }{M: []testpb.TestAllTypes{{OptionalSint64: proto.Int64(1)}}},
179		y:    struct{ M []testpb.TestAllTypes }{M: []testpb.TestAllTypes{{OptionalSint64: proto.Int64(2)}}},
180		opts: cmp.Options{Transform()},
181		want: false,
182	}, {
183		x: struct {
184			M map[string]testpb.TestAllTypes
185		}{
186			M: map[string]testpb.TestAllTypes{"k": {OptionalSint64: proto.Int64(1)}},
187		},
188		y: struct {
189			M map[string]testpb.TestAllTypes
190		}{
191			M: map[string]testpb.TestAllTypes{"k": {OptionalSint64: proto.Int64(1)}},
192		},
193		opts: cmp.Options{Transform()},
194		want: true,
195	}, {
196		x: struct {
197			M map[string]testpb.TestAllTypes
198		}{
199			M: map[string]testpb.TestAllTypes{"k": {OptionalSint64: proto.Int64(1)}},
200		},
201		y: struct {
202			M map[string]testpb.TestAllTypes
203		}{
204			M: map[string]testpb.TestAllTypes{"k": {OptionalSint64: proto.Int64(2)}},
205		},
206		opts: cmp.Options{Transform()},
207		want: false,
208	}}...)
209
210	// Test IgnoreUnknown.
211	raw := protopack.Message{
212		protopack.Tag{1, protopack.BytesType}, protopack.String("Hello, goodbye!"),
213	}.Marshal()
214	tests = append(tests, []test{{
215		x:    apply(&testpb.TestAllTypes{OptionalSint64: proto.Int64(5)}, setUnknown{raw}),
216		y:    &testpb.TestAllTypes{OptionalSint64: proto.Int64(5)},
217		opts: cmp.Options{Transform()},
218		want: false,
219	}, {
220		x:    apply(&testpb.TestAllTypes{OptionalSint64: proto.Int64(5)}, setUnknown{raw}),
221		y:    &testpb.TestAllTypes{OptionalSint64: proto.Int64(5)},
222		opts: cmp.Options{Transform(), IgnoreUnknown()},
223		want: true,
224	}, {
225		x:    apply(&testpb.TestAllTypes{OptionalSint64: proto.Int64(5)}, setUnknown{raw}),
226		y:    &testpb.TestAllTypes{OptionalSint64: proto.Int64(6)},
227		opts: cmp.Options{Transform(), IgnoreUnknown()},
228		want: false,
229	}, {
230		x:    apply(&testpb.TestAllTypes{OptionalSint64: proto.Int64(5)}, setUnknown{raw}),
231		y:    apply(dynamicpb.NewMessage(allTypesDesc), setField{6, int64(5)}),
232		opts: cmp.Options{Transform()},
233		want: false,
234	}, {
235		x:    apply(&testpb.TestAllTypes{OptionalSint64: proto.Int64(5)}, setUnknown{raw}),
236		y:    apply(dynamicpb.NewMessage(allTypesDesc), setField{6, int64(5)}),
237		opts: cmp.Options{Transform(), IgnoreUnknown()},
238		want: true,
239	}}...)
240
241	// Test IgnoreDefaultScalars.
242	tests = append(tests, []test{{
243		x: &testpb.TestAllTypes{
244			DefaultInt32:  proto.Int32(81),
245			DefaultUint32: proto.Uint32(83),
246			DefaultFloat:  proto.Float32(91.5),
247			DefaultBool:   proto.Bool(true),
248			DefaultBytes:  []byte("world"),
249		},
250		y: &testpb.TestAllTypes{
251			DefaultInt64:       proto.Int64(82),
252			DefaultUint64:      proto.Uint64(84),
253			DefaultDouble:      proto.Float64(92e3),
254			DefaultString:      proto.String("hello"),
255			DefaultForeignEnum: testpb.ForeignEnum_FOREIGN_BAR.Enum(),
256		},
257		opts: cmp.Options{Transform()},
258		want: false,
259	}, {
260		x: &testpb.TestAllTypes{
261			DefaultInt32:  proto.Int32(81),
262			DefaultUint32: proto.Uint32(83),
263			DefaultFloat:  proto.Float32(91.5),
264			DefaultBool:   proto.Bool(true),
265			DefaultBytes:  []byte("world"),
266		},
267		y: &testpb.TestAllTypes{
268			DefaultInt64:       proto.Int64(82),
269			DefaultUint64:      proto.Uint64(84),
270			DefaultDouble:      proto.Float64(92e3),
271			DefaultString:      proto.String("hello"),
272			DefaultForeignEnum: testpb.ForeignEnum_FOREIGN_BAR.Enum(),
273		},
274		opts: cmp.Options{Transform(), IgnoreDefaultScalars()},
275		want: true,
276	}, {
277		x: &testpb.TestAllTypes{
278			OptionalInt32:  proto.Int32(81),
279			OptionalUint32: proto.Uint32(83),
280			OptionalFloat:  proto.Float32(91.5),
281			OptionalBool:   proto.Bool(true),
282			OptionalBytes:  []byte("world"),
283		},
284		y: &testpb.TestAllTypes{
285			OptionalInt64:       proto.Int64(82),
286			OptionalUint64:      proto.Uint64(84),
287			OptionalDouble:      proto.Float64(92e3),
288			OptionalString:      proto.String("hello"),
289			OptionalForeignEnum: testpb.ForeignEnum_FOREIGN_BAR.Enum(),
290		},
291		opts: cmp.Options{Transform(), IgnoreDefaultScalars()},
292		want: false,
293	}, {
294		x: &testpb.TestAllTypes{
295			OptionalInt32:  proto.Int32(0),
296			OptionalUint32: proto.Uint32(0),
297			OptionalFloat:  proto.Float32(0),
298			OptionalBool:   proto.Bool(false),
299			OptionalBytes:  []byte(""),
300		},
301		y: &testpb.TestAllTypes{
302			OptionalInt64:       proto.Int64(0),
303			OptionalUint64:      proto.Uint64(0),
304			OptionalDouble:      proto.Float64(0),
305			OptionalString:      proto.String(""),
306			OptionalForeignEnum: testpb.ForeignEnum_FOREIGN_FOO.Enum(),
307		},
308		opts: cmp.Options{Transform(), IgnoreDefaultScalars()},
309		want: true,
310	}, {
311		x: apply(new(testpb.TestAllExtensions),
312			setExtension{testpb.E_DefaultInt32, int32(81)},
313			setExtension{testpb.E_DefaultUint32, uint32(83)},
314			setExtension{testpb.E_DefaultFloat, float32(91.5)},
315			setExtension{testpb.E_DefaultBool, bool(true)},
316			setExtension{testpb.E_DefaultBytes, []byte("world")}),
317		y: apply(new(testpb.TestAllExtensions),
318			setExtension{testpb.E_DefaultInt64, int64(82)},
319			setExtension{testpb.E_DefaultUint64, uint64(84)},
320			setExtension{testpb.E_DefaultDouble, float64(92e3)},
321			setExtension{testpb.E_DefaultString, string("hello")}),
322		opts: cmp.Options{Transform()},
323		want: false,
324	}, {
325		x: apply(new(testpb.TestAllExtensions),
326			setExtension{testpb.E_DefaultInt32, int32(81)},
327			setExtension{testpb.E_DefaultUint32, uint32(83)},
328			setExtension{testpb.E_DefaultFloat, float32(91.5)},
329			setExtension{testpb.E_DefaultBool, bool(true)},
330			setExtension{testpb.E_DefaultBytes, []byte("world")}),
331		y: apply(new(testpb.TestAllExtensions),
332			setExtension{testpb.E_DefaultInt64, int64(82)},
333			setExtension{testpb.E_DefaultUint64, uint64(84)},
334			setExtension{testpb.E_DefaultDouble, float64(92e3)},
335			setExtension{testpb.E_DefaultString, string("hello")}),
336		opts: cmp.Options{Transform(), IgnoreDefaultScalars()},
337		want: true,
338	}, {
339		x: apply(new(testpb.TestAllExtensions),
340			setExtension{testpb.E_OptionalInt32, int32(0)},
341			setExtension{testpb.E_OptionalUint32, uint32(0)},
342			setExtension{testpb.E_OptionalFloat, float32(0)},
343			setExtension{testpb.E_OptionalBool, bool(false)},
344			setExtension{testpb.E_OptionalBytes, []byte("")}),
345		y: apply(new(testpb.TestAllExtensions),
346			setExtension{testpb.E_OptionalInt64, int64(0)},
347			setExtension{testpb.E_OptionalUint64, uint64(0)},
348			setExtension{testpb.E_OptionalDouble, float64(0)},
349			setExtension{testpb.E_OptionalString, string("")}),
350		opts: cmp.Options{Transform()},
351		want: false,
352	}, {
353		x: apply(new(testpb.TestAllExtensions),
354			setExtension{testpb.E_OptionalInt32, int32(0)},
355			setExtension{testpb.E_OptionalUint32, uint32(0)},
356			setExtension{testpb.E_OptionalFloat, float32(0)},
357			setExtension{testpb.E_OptionalBool, bool(false)},
358			setExtension{testpb.E_OptionalBytes, []byte("")}),
359		y: apply(new(testpb.TestAllExtensions),
360			setExtension{testpb.E_OptionalInt64, int64(0)},
361			setExtension{testpb.E_OptionalUint64, uint64(0)},
362			setExtension{testpb.E_OptionalDouble, float64(0)},
363			setExtension{testpb.E_OptionalString, string("")}),
364		opts: cmp.Options{Transform(), IgnoreDefaultScalars()},
365		want: true,
366	}, {
367		x: &testpb.TestAllTypes{
368			DefaultFloat: proto.Float32(91.6),
369		},
370		y:    &testpb.TestAllTypes{},
371		opts: cmp.Options{Transform(), IgnoreDefaultScalars()},
372		want: false,
373	}, {
374		x: &testpb.TestAllTypes{
375			OptionalForeignMessage: &testpb.ForeignMessage{},
376		},
377		y:    &testpb.TestAllTypes{},
378		opts: cmp.Options{Transform(), IgnoreDefaultScalars()},
379		want: false,
380	}}...)
381
382	// Test IgnoreEmptyMessages.
383	tests = append(tests, []test{{
384		x:    []*testpb.TestAllTypes{nil, {}, {OptionalInt32: proto.Int32(5)}},
385		y:    []*testpb.TestAllTypes{nil, {}, {OptionalInt32: proto.Int32(5)}},
386		opts: cmp.Options{Transform()},
387		want: true,
388	}, {
389		x:    []*testpb.TestAllTypes{nil, {}, {OptionalInt32: proto.Int32(5)}},
390		y:    []*testpb.TestAllTypes{{OptionalInt32: proto.Int32(5)}},
391		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
392		want: false,
393	}, {
394		x:    &testpb.TestAllTypes{OptionalForeignMessage: &testpb.ForeignMessage{}},
395		y:    &testpb.TestAllTypes{OptionalForeignMessage: nil},
396		opts: cmp.Options{Transform()},
397		want: false,
398	}, {
399		x:    &testpb.TestAllTypes{OptionalForeignMessage: &testpb.ForeignMessage{}},
400		y:    &testpb.TestAllTypes{OptionalForeignMessage: nil},
401		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
402		want: true,
403	}, {
404		x:    &testpb.TestAllTypes{OptionalForeignMessage: &testpb.ForeignMessage{C: proto.Int32(5)}},
405		y:    &testpb.TestAllTypes{OptionalForeignMessage: nil},
406		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
407		want: false,
408	}, {
409		x:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{}},
410		y:    &testpb.TestAllTypes{RepeatedForeignMessage: nil},
411		opts: cmp.Options{Transform()},
412		want: true,
413	}, {
414		x:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {}}},
415		y:    &testpb.TestAllTypes{RepeatedForeignMessage: nil},
416		opts: cmp.Options{Transform()},
417		want: false,
418	}, {
419		x:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {}}},
420		y:    &testpb.TestAllTypes{RepeatedForeignMessage: nil},
421		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
422		want: true,
423	}, {
424		x:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(5)}, {}}},
425		y:    &testpb.TestAllTypes{RepeatedForeignMessage: nil},
426		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
427		want: false,
428	}, {
429		x:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(5)}, {}}},
430		y:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {}, nil, {}, {C: proto.Int32(5)}, {}}},
431		opts: cmp.Options{Transform()},
432		want: false,
433	}, {
434		x:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(5)}, {}}},
435		y:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {}, nil, {}, {C: proto.Int32(5)}, {}}},
436		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
437		want: true,
438	}, {
439		x:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{}},
440		y:    &testpb.TestAllTypes{MapStringNestedMessage: nil},
441		opts: cmp.Options{Transform()},
442		want: true,
443	}, {
444		x:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"1": nil, "2": {}}},
445		y:    &testpb.TestAllTypes{MapStringNestedMessage: nil},
446		opts: cmp.Options{Transform()},
447		want: false,
448	}, {
449		x:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"1": nil, "2": {}}},
450		y:    &testpb.TestAllTypes{MapStringNestedMessage: nil},
451		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
452		want: true,
453	}, {
454		x:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"1": nil, "2": {A: proto.Int32(5)}, "3": {}}},
455		y:    &testpb.TestAllTypes{MapStringNestedMessage: nil},
456		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
457		want: false,
458	}, {
459		x:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"1": nil, "2": {A: proto.Int32(5)}, "3": {}}},
460		y:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"1": {}, "1a": {}, "1b": nil, "2": {A: proto.Int32(5)}, "4": {}}},
461		opts: cmp.Options{Transform()},
462		want: false,
463	}, {
464		x:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"1": nil, "2": {A: proto.Int32(5)}, "3": {}}},
465		y:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"1": {}, "1a": {}, "1b": nil, "2": {A: proto.Int32(5)}, "4": {}}},
466		opts: cmp.Options{Transform(), IgnoreEmptyMessages()},
467		want: true,
468	}}...)
469
470	// Test IgnoreEnums and IgnoreMessages.
471	tests = append(tests, []test{{
472		x: &testpb.TestAllTypes{
473			OptionalNestedMessage:  &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)},
474			RepeatedNestedMessage:  []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}},
475			MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"3": {A: proto.Int32(3)}},
476		},
477		y:    &testpb.TestAllTypes{},
478		opts: cmp.Options{Transform()},
479		want: false,
480	}, {
481		x: &testpb.TestAllTypes{
482			OptionalNestedMessage:  &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)},
483			RepeatedNestedMessage:  []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}},
484			MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"3": {A: proto.Int32(3)}},
485		},
486		y:    &testpb.TestAllTypes{},
487		opts: cmp.Options{Transform(), IgnoreMessages(&testpb.TestAllTypes{})},
488		want: true,
489	}, {
490		x: &testpb.TestAllTypes{
491			OptionalNestedEnum:  testpb.TestAllTypes_FOO.Enum(),
492			RepeatedNestedEnum:  []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR},
493			MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"baz": testpb.TestAllTypes_BAZ},
494		},
495		y:    &testpb.TestAllTypes{},
496		opts: cmp.Options{Transform()},
497		want: false,
498	}, {
499		x: &testpb.TestAllTypes{
500			OptionalNestedEnum:  testpb.TestAllTypes_FOO.Enum(),
501			RepeatedNestedEnum:  []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR},
502			MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"baz": testpb.TestAllTypes_BAZ},
503		},
504		y:    &testpb.TestAllTypes{},
505		opts: cmp.Options{Transform(), IgnoreEnums(testpb.TestAllTypes_NestedEnum(0))},
506		want: true,
507	}, {
508		x: &testpb.TestAllTypes{
509			OptionalNestedEnum:  testpb.TestAllTypes_FOO.Enum(),
510			RepeatedNestedEnum:  []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR},
511			MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"baz": testpb.TestAllTypes_BAZ},
512
513			OptionalNestedMessage:  &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)},
514			RepeatedNestedMessage:  []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}},
515			MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"3": {A: proto.Int32(3)}},
516		},
517		y: &testpb.TestAllTypes{},
518		opts: cmp.Options{Transform(),
519			IgnoreMessages(&testpb.TestAllExtensions{}),
520			IgnoreEnums(testpb.ForeignEnum(0)),
521		},
522		want: false,
523	}}...)
524
525	// Test IgnoreFields and IgnoreOneofs.
526	tests = append(tests, []test{{
527		x:    &testpb.TestAllTypes{OptionalInt32: proto.Int32(5)},
528		y:    &testpb.TestAllTypes{OptionalInt32: proto.Int32(6)},
529		opts: cmp.Options{Transform()},
530		want: false,
531	}, {
532		x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(5)},
533		y: &testpb.TestAllTypes{},
534		opts: cmp.Options{Transform(),
535			IgnoreFields(&testpb.TestAllTypes{}, "optional_int32")},
536		want: true,
537	}, {
538		x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(5)},
539		y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(6)},
540		opts: cmp.Options{Transform(),
541			IgnoreFields(&testpb.TestAllTypes{}, "optional_int32")},
542		want: true,
543	}, {
544		x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(5)},
545		y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(6)},
546		opts: cmp.Options{Transform(),
547			IgnoreFields(&testpb.TestAllTypes{}, "optional_int64")},
548		want: false,
549	}, {
550		x:    &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{5}},
551		y:    &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofString{"5"}},
552		opts: cmp.Options{Transform()},
553		want: false,
554	}, {
555		x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{5}},
556		y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofString{"5"}},
557		opts: cmp.Options{Transform(),
558			IgnoreFields(&testpb.TestAllTypes{}, "oneof_uint32"),
559			IgnoreFields(&testpb.TestAllTypes{}, "oneof_string")},
560		want: true,
561	}, {
562		x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{5}},
563		y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofString{"5"}},
564		opts: cmp.Options{Transform(),
565			IgnoreOneofs(&testpb.TestAllTypes{}, "oneof_field")},
566		want: true,
567	}, {
568		x: apply(new(testpb.TestAllExtensions),
569			setExtension{testpb.E_OptionalString, "hello"}),
570		y: apply(new(testpb.TestAllExtensions),
571			setExtension{testpb.E_OptionalString, "goodbye"}),
572		opts: cmp.Options{Transform()},
573		want: false,
574	}, {
575		x: apply(new(testpb.TestAllExtensions),
576			setExtension{testpb.E_OptionalString, "hello"}),
577		y: new(testpb.TestAllExtensions),
578		opts: cmp.Options{Transform(),
579			IgnoreDescriptors(testpb.E_OptionalString.TypeDescriptor())},
580		want: true,
581	}, {
582		x: apply(new(testpb.TestAllExtensions),
583			setExtension{testpb.E_OptionalString, "hello"}),
584		y: apply(new(testpb.TestAllExtensions),
585			setExtension{testpb.E_OptionalString, "goodbye"}),
586		opts: cmp.Options{Transform(),
587			IgnoreDescriptors(testpb.E_OptionalString.TypeDescriptor())},
588		want: true,
589	}, {
590		x: apply(new(testpb.TestAllExtensions),
591			setExtension{testpb.E_OptionalString, "hello"}),
592		y: apply(new(testpb.TestAllExtensions),
593			setExtension{testpb.E_OptionalString, "goodbye"}),
594		opts: cmp.Options{Transform(),
595			IgnoreDescriptors(testpb.E_OptionalInt32.TypeDescriptor())},
596		want: false,
597	}}...)
598
599	// Test FilterEnum.
600	tests = append(tests, []test{{
601		x:    &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
602		y:    &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()},
603		opts: cmp.Options{Transform()},
604		want: false,
605	}, {
606		x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
607		y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()},
608		opts: cmp.Options{
609			Transform(),
610			FilterEnum(testpb.ForeignEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })),
611		},
612		want: false, // mismatching filter type
613	}, {
614		x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
615		y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()},
616		opts: cmp.Options{
617			Transform(),
618			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y int) bool { return true })),
619		},
620		want: false, // matching filter type, but mismatching comparer type
621	}, {
622		x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
623		y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()},
624		opts: cmp.Options{
625			Transform(),
626			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y testpb.TestAllTypes_NestedEnum) bool { return true })),
627		},
628		want: false, // matching filter type, but mismatching comparer type
629	}, {
630		x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
631		y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()},
632		opts: cmp.Options{
633			Transform(),
634			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })),
635		},
636		want: true,
637	}, {
638		x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
639		y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()},
640		opts: cmp.Options{
641			Transform(),
642			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y Enum) bool { return true })),
643		},
644		want: true,
645	}, {
646		x:    &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}},
647		y:    &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}},
648		opts: cmp.Options{Transform()},
649		want: false,
650	}, {
651		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}},
652		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}},
653		opts: cmp.Options{
654			Transform(),
655			FilterEnum(testpb.ForeignEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })),
656		},
657		want: false, // mismatching filter type
658	}, {
659		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}},
660		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}},
661		opts: cmp.Options{
662			Transform(),
663			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y int) bool { return true })),
664		},
665		want: false, // matching filter type, but mismatching comparer type
666	}, {
667		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}},
668		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}},
669		opts: cmp.Options{
670			Transform(),
671			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y []testpb.TestAllTypes_NestedEnum) bool { return true })),
672		},
673		want: false, // matching filter type, but mismatching comparer type
674	}, {
675		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}},
676		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}},
677		opts: cmp.Options{
678			Transform(),
679			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })),
680		},
681		want: true,
682	}, {
683		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}},
684		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}},
685		opts: cmp.Options{
686			Transform(),
687			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y []Enum) bool { return true })),
688		},
689		want: true,
690	}, {
691		x:    &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}},
692		y:    &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}},
693		opts: cmp.Options{Transform()},
694		want: false,
695	}, {
696		x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}},
697		y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}},
698		opts: cmp.Options{
699			Transform(),
700			FilterEnum(testpb.ForeignEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })),
701		},
702		want: false, // mismatching filter type
703	}, {
704		x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}},
705		y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}},
706		opts: cmp.Options{
707			Transform(),
708			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y int) bool { return true })),
709		},
710		want: false, // matching filter type, but mismatching comparer type
711	}, {
712		x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}},
713		y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}},
714		opts: cmp.Options{
715			Transform(),
716			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y map[string]testpb.TestAllTypes_NestedEnum) bool { return true })),
717		},
718		want: false, // matching filter type, but mismatching comparer type
719	}, {
720		x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}},
721		y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}},
722		opts: cmp.Options{
723			Transform(),
724			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })),
725		},
726		want: true,
727	}, {
728		x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}},
729		y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}},
730		opts: cmp.Options{
731			Transform(),
732			FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y map[string]Enum) bool { return true })),
733		},
734		want: true,
735	}}...)
736
737	// Test FilterMessage.
738	tests = append(tests, []test{{
739		x:    &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}},
740		y:    &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}},
741		opts: cmp.Options{Transform()},
742		want: false,
743	}, {
744		x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}},
745		y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}},
746		opts: cmp.Options{
747			Transform(),
748			FilterMessage(new(testpb.TestAllExtensions), cmp.Comparer(func(x, y interface{}) bool { return true })),
749		},
750		want: false, // mismatching filter type
751	}, {
752		x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}},
753		y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}},
754		opts: cmp.Options{
755			Transform(),
756			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y int) bool { return true })),
757		},
758		want: false, // matching filter type, but mismatching comparer type
759	}, {
760		x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}},
761		y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}},
762		opts: cmp.Options{
763			Transform(),
764			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y *testpb.TestAllTypes_NestedMessage) bool { return true })),
765		},
766		want: false, // matching filter type, but mismatching comparer type
767	}, {
768		x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}},
769		y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}},
770		opts: cmp.Options{
771			Transform(),
772			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y interface{}) bool { return true })),
773		},
774		want: true,
775	}, {
776		x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}},
777		y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}},
778		opts: cmp.Options{
779			Transform(),
780			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y Message) bool { return true })),
781		},
782		want: true,
783	}, {
784		x:    &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}},
785		y:    &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}},
786		opts: cmp.Options{Transform()},
787		want: false,
788	}, {
789		x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}},
790		y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}},
791		opts: cmp.Options{
792			Transform(),
793			FilterMessage(new(testpb.TestAllExtensions), cmp.Comparer(func(x, y interface{}) bool { return true })),
794		},
795		want: false, // mismatching filter type
796	}, {
797		x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}},
798		y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}},
799		opts: cmp.Options{
800			Transform(),
801			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y int) bool { return true })),
802		},
803		want: false, // matching filter type, but mismatching comparer type
804	}, {
805		x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}},
806		y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}},
807		opts: cmp.Options{
808			Transform(),
809			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y []*testpb.TestAllTypes_NestedMessage) bool { return true })),
810		},
811		want: false, // matching filter type, but mismatching comparer type
812	}, {
813		x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}},
814		y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}},
815		opts: cmp.Options{
816			Transform(),
817			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y interface{}) bool { return true })),
818		},
819		want: true,
820	}, {
821		x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}},
822		y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}},
823		opts: cmp.Options{
824			Transform(),
825			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y []Message) bool { return true })),
826		},
827		want: true,
828	}, {
829		x:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}},
830		y:    &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}},
831		opts: cmp.Options{Transform()},
832		want: false,
833	}, {
834		x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}},
835		y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}},
836		opts: cmp.Options{
837			Transform(),
838			FilterMessage(new(testpb.TestAllExtensions), cmp.Comparer(func(x, y interface{}) bool { return true })),
839		},
840		want: false, // mismatching filter type
841	}, {
842		x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}},
843		y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}},
844		opts: cmp.Options{
845			Transform(),
846			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y int) bool { return true })),
847		},
848		want: false, // matching filter type, but mismatching comparer type
849	}, {
850		x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}},
851		y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}},
852		opts: cmp.Options{
853			Transform(),
854			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y map[string]*testpb.TestAllTypes_NestedMessage) bool { return true })),
855		},
856		want: false, // matching filter type, but mismatching comparer type
857	}, {
858		x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}},
859		y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}},
860		opts: cmp.Options{
861			Transform(),
862			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y interface{}) bool { return true })),
863		},
864		want: true,
865	}, {
866		x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}},
867		y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}},
868		opts: cmp.Options{
869			Transform(),
870			FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y map[string]Message) bool { return true })),
871		},
872		want: true,
873	}}...)
874
875	// Test FilterField.
876	tests = append(tests, []test{{
877		x:    &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)},
878		y:    &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)},
879		opts: cmp.Options{Transform()},
880		want: false,
881	}, {
882		x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)},
883		y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)},
884		opts: cmp.Options{
885			Transform(),
886			FilterField(new(testpb.TestAllTypes), "optional_int64", cmp.Comparer(func(x, y interface{}) bool { return true })),
887		},
888		want: false, // mismatching filter name
889	}, {
890		x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)},
891		y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)},
892		opts: cmp.Options{
893			Transform(),
894			FilterField(new(testpb.TestAllTypes), "optional_int32", cmp.Comparer(func(x, y int64) bool { return true })),
895		},
896		want: false, // matching filter name, but mismatching comparer type
897	}, {
898		x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)},
899		y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)},
900		opts: cmp.Options{
901			Transform(),
902			FilterField(new(testpb.TestAllTypes), "optional_int32", cmp.Comparer(func(x, y interface{}) bool { return true })),
903		},
904		want: true,
905	}, {
906		x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)},
907		y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)},
908		opts: cmp.Options{
909			Transform(),
910			FilterField(new(testpb.TestAllTypes), "optional_int32", cmp.Comparer(func(x, y int32) bool { return true })),
911		},
912		want: true,
913	}, {
914		x:    &testpb.TestAllTypes{RepeatedInt32: []int32{1}},
915		y:    &testpb.TestAllTypes{RepeatedInt32: []int32{2}},
916		opts: cmp.Options{Transform()},
917		want: false,
918	}, {
919		x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}},
920		y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}},
921		opts: cmp.Options{
922			Transform(),
923			FilterField(new(testpb.TestAllTypes), "repeated_int64", cmp.Comparer(func(x, y interface{}) bool { return true })),
924		},
925		want: false, // mismatching filter name
926	}, {
927		x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}},
928		y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}},
929		opts: cmp.Options{
930			Transform(),
931			FilterField(new(testpb.TestAllTypes), "repeated_int32", cmp.Comparer(func(x, y []int64) bool { return true })),
932		},
933		want: false, // matching filter name, but mismatching comparer type
934	}, {
935		x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}},
936		y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}},
937		opts: cmp.Options{
938			Transform(),
939			FilterField(new(testpb.TestAllTypes), "repeated_int32", cmp.Comparer(func(x, y interface{}) bool { return true })),
940		},
941		want: true,
942	}, {
943		x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}},
944		y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}},
945		opts: cmp.Options{
946			Transform(),
947			FilterField(new(testpb.TestAllTypes), "repeated_int32", cmp.Comparer(func(x, y []int32) bool { return true })),
948		},
949		want: true,
950	}, {
951		x:    &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}},
952		y:    &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}},
953		opts: cmp.Options{Transform()},
954		want: false,
955	}, {
956		x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}},
957		y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}},
958		opts: cmp.Options{
959			Transform(),
960			FilterField(new(testpb.TestAllTypes), "map_int64_int64", cmp.Comparer(func(x, y interface{}) bool { return true })),
961		},
962		want: false, // mismatching filter name
963	}, {
964		x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}},
965		y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}},
966		opts: cmp.Options{
967			Transform(),
968			FilterField(new(testpb.TestAllTypes), "map_int32_int32", cmp.Comparer(func(x, y map[int64]int64) bool { return true })),
969		},
970		want: false, // matching filter name, but mismatching comparer type
971	}, {
972		x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}},
973		y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}},
974		opts: cmp.Options{
975			Transform(),
976			FilterField(new(testpb.TestAllTypes), "map_int32_int32", cmp.Comparer(func(x, y interface{}) bool { return true })),
977		},
978		want: true,
979	}, {
980		x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}},
981		y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}},
982		opts: cmp.Options{
983			Transform(),
984			FilterField(new(testpb.TestAllTypes), "map_int32_int32", cmp.Comparer(func(x, y map[int32]int32) bool { return true })),
985		},
986		want: true,
987	}}...)
988
989	// Test FilterOneof
990	tests = append(tests, []test{{
991		x:    &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}},
992		y:    &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}},
993		opts: cmp.Options{Transform()},
994		want: false,
995	}, {
996		x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}},
997		y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}},
998		opts: cmp.Options{
999			Transform(),
1000			FilterOneof(new(testpb.TestAllTypes), "oneof_optional", cmp.Comparer(func(x, y interface{}) bool { return true })),
1001		},
1002		want: false, // mismatching filter name
1003	}, {
1004		x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}},
1005		y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}},
1006		opts: cmp.Options{
1007			Transform(),
1008			FilterOneof(new(testpb.TestAllTypes), "oneof_field", cmp.Comparer(func(x, y string) bool { return true })),
1009		},
1010		want: false, // matching filter name, but mismatching comparer type
1011	}, {
1012		x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}},
1013		y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}},
1014		opts: cmp.Options{
1015			Transform(),
1016			FilterOneof(new(testpb.TestAllTypes), "oneof_field", cmp.Comparer(func(x, y uint32) bool { return true })),
1017		},
1018		want: true,
1019	}, {
1020		x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}},
1021		y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}},
1022		opts: cmp.Options{
1023			Transform(),
1024			FilterOneof(new(testpb.TestAllTypes), "oneof_field", cmp.Comparer(func(x, y interface{}) bool { return true })),
1025		},
1026		want: true,
1027	}}...)
1028
1029	// Test SortRepeated.
1030	type higherOrderType struct {
1031		M    *testpb.TestAllTypes
1032		I32s []int32
1033		Es   []testpb.TestAllTypes_NestedEnum
1034		Ms   []*testpb.ForeignMessage
1035	}
1036	tests = append(tests, []test{{
1037		x:    &testpb.TestAllTypes{RepeatedInt32: []int32{3, 2, 1, 2, 3, 3}},
1038		y:    &testpb.TestAllTypes{RepeatedInt32: []int32{2, 3, 3, 2, 1, 3}},
1039		opts: cmp.Options{Transform()},
1040		want: false,
1041	}, {
1042		x: &testpb.TestAllTypes{RepeatedInt32: []int32{3, 2, 1, 2, 3, 3}},
1043		y: &testpb.TestAllTypes{RepeatedInt32: []int32{2, 3, 3, 2, 1, 3}},
1044		opts: cmp.Options{
1045			Transform(),
1046			SortRepeated(func(x, y int32) bool { return x < y }),
1047		},
1048		want: true,
1049	}, {
1050		x: higherOrderType{
1051			M:    &testpb.TestAllTypes{RepeatedInt32: []int32{3, 2, 1, 2, 3, 3}},
1052			I32s: []int32{3, 2, 1, 2, 3, 3},
1053		},
1054		y: higherOrderType{
1055			M:    &testpb.TestAllTypes{RepeatedInt32: []int32{2, 3, 3, 2, 1, 3}},
1056			I32s: []int32{2, 3, 3, 2, 1, 3},
1057		},
1058		opts: cmp.Options{
1059			Transform(),
1060			SortRepeated(func(x, y int32) bool { return x < y }),
1061		},
1062		want: false, // sort does not apply to []int32 outside of a message
1063	}, {
1064		x: &testpb.TestAllTypes{RepeatedInt32: []int32{3, 2, 1, 2, 3, 3}},
1065		y: &testpb.TestAllTypes{RepeatedInt32: []int32{2, 3, 3, 2, 1, 3}},
1066		opts: cmp.Options{
1067			Transform(),
1068			SortRepeated(func(x, y int64) bool { return x < y }),
1069		},
1070		want: false, // wrong sort type: int32 != int64
1071	}, {
1072		x:    &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAR, testpb.TestAllTypes_BAZ}},
1073		y:    &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR, testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAZ}},
1074		opts: cmp.Options{Transform()},
1075		want: false,
1076	}, {
1077		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAR, testpb.TestAllTypes_BAZ}},
1078		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR, testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAZ}},
1079		opts: cmp.Options{
1080			Transform(),
1081			SortRepeated(func(x, y testpb.TestAllTypes_NestedEnum) bool { return x < y }),
1082		},
1083		want: true,
1084	}, {
1085		x: higherOrderType{
1086			M:  &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAR, testpb.TestAllTypes_BAZ}},
1087			Es: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAR, testpb.TestAllTypes_BAZ},
1088		},
1089		y: higherOrderType{
1090			M:  &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR, testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAZ}},
1091			Es: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR, testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAZ},
1092		},
1093		opts: cmp.Options{
1094			Transform(),
1095			SortRepeated(func(x, y testpb.TestAllTypes_NestedEnum) bool { return x < y }),
1096		},
1097		want: false, // sort does not apply to []testpb.TestAllTypes_NestedEnum outside of a message
1098	}, {
1099		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAR, testpb.TestAllTypes_BAZ}},
1100		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR, testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAZ}},
1101		opts: cmp.Options{
1102			Transform(),
1103			SortRepeated(func(x, y testpb.ForeignEnum) bool { return x < y }),
1104		},
1105		want: false, // wrong sort type: testpb.TestAllTypes_NestedEnum != testpb.ForeignEnum
1106	}, {
1107		x:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {C: proto.Int32(3)}, nil, {C: proto.Int32(3)}, {C: proto.Int32(5)}, {C: proto.Int32(4)}}},
1108		y:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(3)}, {}, {C: proto.Int32(4)}, {C: proto.Int32(3)}, {C: proto.Int32(5)}}},
1109		opts: cmp.Options{Transform()},
1110		want: false,
1111	}, {
1112		x: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {C: proto.Int32(3)}, nil, {C: proto.Int32(3)}, {C: proto.Int32(5)}, {C: proto.Int32(4)}}},
1113		y: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(3)}, {}, {C: proto.Int32(4)}, {C: proto.Int32(3)}, {C: proto.Int32(5)}}},
1114		opts: cmp.Options{
1115			Transform(),
1116			SortRepeated(func(x, y *testpb.ForeignMessage) bool { return x.GetC() < y.GetC() }),
1117		},
1118		want: true,
1119	}, {
1120		x: higherOrderType{
1121			M:  &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {C: proto.Int32(3)}, nil, {C: proto.Int32(3)}, {C: proto.Int32(5)}, {C: proto.Int32(4)}}},
1122			Ms: []*testpb.ForeignMessage{{}, {C: proto.Int32(3)}, nil, {C: proto.Int32(3)}, {C: proto.Int32(5)}, {C: proto.Int32(4)}},
1123		},
1124		y: higherOrderType{
1125			M:  &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(3)}, {}, {C: proto.Int32(4)}, {C: proto.Int32(3)}, {C: proto.Int32(5)}}},
1126			Ms: []*testpb.ForeignMessage{nil, {C: proto.Int32(3)}, {}, {C: proto.Int32(4)}, {C: proto.Int32(3)}, {C: proto.Int32(5)}},
1127		},
1128		opts: cmp.Options{
1129			Transform(),
1130			SortRepeated(func(x, y *testpb.ForeignMessage) bool { return x.GetC() < y.GetC() }),
1131		},
1132		want: false, // sort does not apply to []*testpb.ForeignMessage outside of a message
1133	}, {
1134		x: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {C: proto.Int32(3)}, nil, {C: proto.Int32(3)}, {C: proto.Int32(5)}, {C: proto.Int32(4)}}},
1135		y: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(3)}, {}, {C: proto.Int32(4)}, {C: proto.Int32(3)}, {C: proto.Int32(5)}}},
1136		opts: cmp.Options{
1137			Transform(),
1138			SortRepeated(func(x, y *testpb.TestAllTypes_NestedMessage) bool { return x.GetA() < y.GetA() }),
1139		},
1140		want: false, // wrong sort type: *testpb.ForeignMessage != *testpb.TestAllTypes_NestedMessage
1141	}, {
1142		x: &testpb.TestAllTypes{
1143			RepeatedInt32:    []int32{-32, +32},
1144			RepeatedSint32:   []int32{-32, +32},
1145			RepeatedSfixed32: []int32{-32, +32},
1146			RepeatedInt64:    []int64{-64, +64},
1147			RepeatedSint64:   []int64{-64, +64},
1148			RepeatedSfixed64: []int64{-64, +64},
1149			RepeatedUint32:   []uint32{0, 32},
1150			RepeatedFixed32:  []uint32{0, 32},
1151			RepeatedUint64:   []uint64{0, 64},
1152			RepeatedFixed64:  []uint64{0, 64},
1153		},
1154		y: &testpb.TestAllTypes{
1155			RepeatedInt32:    []int32{+32, -32},
1156			RepeatedSint32:   []int32{+32, -32},
1157			RepeatedSfixed32: []int32{+32, -32},
1158			RepeatedInt64:    []int64{+64, -64},
1159			RepeatedSint64:   []int64{+64, -64},
1160			RepeatedSfixed64: []int64{+64, -64},
1161			RepeatedUint32:   []uint32{32, 0},
1162			RepeatedFixed32:  []uint32{32, 0},
1163			RepeatedUint64:   []uint64{64, 0},
1164			RepeatedFixed64:  []uint64{64, 0},
1165		},
1166		opts: cmp.Options{
1167			Transform(),
1168			SortRepeated(func(x, y int32) bool { return x < y }),
1169			SortRepeated(func(x, y int64) bool { return x < y }),
1170			SortRepeated(func(x, y uint32) bool { return x < y }),
1171			SortRepeated(func(x, y uint64) bool { return x < y }),
1172		},
1173		want: true,
1174	}}...)
1175
1176	// Test SortRepeatedFields.
1177	tests = append(tests, []test{{
1178		x:    &testpb.TestAllTypes{RepeatedInt32: []int32{3, 2, 1, 2, 3, 3}},
1179		y:    &testpb.TestAllTypes{RepeatedInt32: []int32{2, 3, 3, 2, 1, 3}},
1180		opts: cmp.Options{Transform()},
1181		want: false,
1182	}, {
1183		x: &testpb.TestAllTypes{RepeatedInt32: []int32{3, 2, 1, 2, 3, 3}},
1184		y: &testpb.TestAllTypes{RepeatedInt32: []int32{2, 3, 3, 2, 1, 3}},
1185		opts: cmp.Options{
1186			Transform(),
1187			SortRepeatedFields(new(testpb.TestAllTypes), "repeated_int32"),
1188		},
1189		want: true,
1190	}, {
1191		x: &testpb.TestAllTypes{RepeatedInt32: []int32{3, 2, 1, 2, 3, 3}},
1192		y: &testpb.TestAllTypes{RepeatedInt32: []int32{2, 3, 3, 2, 1, 3}},
1193		opts: cmp.Options{
1194			Transform(),
1195			SortRepeatedFields(new(testpb.TestAllTypes), "repeated_int64"),
1196		},
1197		want: false, // wrong field: repeated_int32 != repeated_int64
1198	}, {
1199		x:    &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAR, testpb.TestAllTypes_BAZ}},
1200		y:    &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR, testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAZ}},
1201		opts: cmp.Options{Transform()},
1202		want: false,
1203	}, {
1204		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAR, testpb.TestAllTypes_BAZ}},
1205		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR, testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAZ}},
1206		opts: cmp.Options{
1207			Transform(),
1208			SortRepeatedFields(new(testpb.TestAllTypes), "repeated_nested_enum"),
1209		},
1210		want: true,
1211	}, {
1212		x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAR, testpb.TestAllTypes_BAZ}},
1213		y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR, testpb.TestAllTypes_FOO, testpb.TestAllTypes_BAZ}},
1214		opts: cmp.Options{
1215			Transform(),
1216			SortRepeatedFields(new(testpb.TestAllTypes), "repeated_foreign_enum"),
1217		},
1218		want: false, // wrong field: repeated_nested_enum != repeated_foreign_enum
1219	}, {
1220		x:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {C: proto.Int32(3)}, nil, {C: proto.Int32(3)}, {C: proto.Int32(5)}, {C: proto.Int32(4)}}},
1221		y:    &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(3)}, {}, {C: proto.Int32(4)}, {C: proto.Int32(3)}, {C: proto.Int32(5)}}},
1222		opts: cmp.Options{Transform()},
1223		want: false,
1224	}, {
1225		x: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {C: proto.Int32(3)}, nil, {C: proto.Int32(3)}, {C: proto.Int32(5)}, {C: proto.Int32(4)}}},
1226		y: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(3)}, {}, {C: proto.Int32(4)}, {C: proto.Int32(3)}, {C: proto.Int32(5)}}},
1227		opts: cmp.Options{
1228			Transform(),
1229			SortRepeatedFields(new(testpb.TestAllTypes), "repeated_foreign_message"),
1230		},
1231		want: true,
1232	}, {
1233		x: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {C: proto.Int32(3)}, nil, {C: proto.Int32(3)}, {C: proto.Int32(5)}, {C: proto.Int32(4)}}},
1234		y: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{nil, {C: proto.Int32(3)}, {}, {C: proto.Int32(4)}, {C: proto.Int32(3)}, {C: proto.Int32(5)}}},
1235		opts: cmp.Options{
1236			Transform(),
1237			SortRepeatedFields(new(testpb.TestAllTypes), "repeated_nested_message"),
1238		},
1239		want: false, // wrong field: repeated_foreign_message != repeated_nested_message
1240	}, {
1241		x: &testpb.TestAllTypes{
1242			RepeatedBool:           []bool{false, true},
1243			RepeatedInt32:          []int32{-32, +32},
1244			RepeatedInt64:          []int64{-64, +64},
1245			RepeatedUint32:         []uint32{0, 32},
1246			RepeatedUint64:         []uint64{0, 64},
1247			RepeatedFloat:          []float32{-32.32, +32.32},
1248			RepeatedDouble:         []float64{-64.64, +64.64},
1249			RepeatedString:         []string{"hello", "world"},
1250			RepeatedBytes:          [][]byte{[]byte("hello"), []byte("world")},
1251			RepeatedForeignEnum:    []testpb.ForeignEnum{testpb.ForeignEnum_FOREIGN_FOO, testpb.ForeignEnum_FOREIGN_BAR},
1252			RepeatedForeignMessage: []*testpb.ForeignMessage{{C: proto.Int32(-1)}, {C: proto.Int32(+1)}},
1253		},
1254		y: &testpb.TestAllTypes{
1255			RepeatedBool:           []bool{true, false},
1256			RepeatedInt32:          []int32{+32, -32},
1257			RepeatedInt64:          []int64{+64, -64},
1258			RepeatedUint32:         []uint32{32, 0},
1259			RepeatedUint64:         []uint64{64, 0},
1260			RepeatedFloat:          []float32{+32.32, -32.32},
1261			RepeatedDouble:         []float64{+64.64, -64.64},
1262			RepeatedString:         []string{"world", "hello"},
1263			RepeatedBytes:          [][]byte{[]byte("world"), []byte("hello")},
1264			RepeatedForeignEnum:    []testpb.ForeignEnum{testpb.ForeignEnum_FOREIGN_BAR, testpb.ForeignEnum_FOREIGN_FOO},
1265			RepeatedForeignMessage: []*testpb.ForeignMessage{{C: proto.Int32(+1)}, {C: proto.Int32(-1)}},
1266		},
1267		opts: cmp.Options{
1268			Transform(),
1269			SortRepeatedFields(new(testpb.TestAllTypes),
1270				"repeated_bool",
1271				"repeated_int32",
1272				"repeated_int64",
1273				"repeated_uint32",
1274				"repeated_uint64",
1275				"repeated_float",
1276				"repeated_double",
1277				"repeated_string",
1278				"repeated_bytes",
1279				"repeated_foreign_enum",
1280				"repeated_foreign_message",
1281			),
1282		},
1283		want: true,
1284	}}...)
1285
1286	for _, tt := range tests {
1287		t.Run("", func(t *testing.T) {
1288			got := cmp.Equal(tt.x, tt.y, tt.opts)
1289			if got != tt.want {
1290				if !got {
1291					t.Errorf("cmp.Equal = false, want true; diff:\n%v", cmp.Diff(tt.x, tt.y, tt.opts))
1292				} else {
1293					t.Errorf("cmp.Equal = true, want false")
1294				}
1295			}
1296		})
1297	}
1298}
1299
1300type setField struct {
1301	num protoreflect.FieldNumber
1302	val interface{}
1303}
1304type setUnknown struct {
1305	raw protoreflect.RawFields
1306}
1307type setExtension struct {
1308	typ protoreflect.ExtensionType
1309	val interface{}
1310}
1311
1312// apply applies a sequence of mutating operations to m.
1313func apply(m proto.Message, ops ...interface{}) proto.Message {
1314	mr := m.ProtoReflect()
1315	md := mr.Descriptor()
1316	for _, op := range ops {
1317		switch op := op.(type) {
1318		case setField:
1319			fd := md.Fields().ByNumber(op.num)
1320			mr.Set(fd, protoreflect.ValueOf(op.val))
1321		case setUnknown:
1322			mr.SetUnknown(op.raw)
1323		case setExtension:
1324			mr.Set(op.typ.TypeDescriptor(), protoreflect.ValueOf(op.val))
1325		}
1326	}
1327	return m
1328}
1329
1330func TestSort(t *testing.T) {
1331	t.Run("F32", func(t *testing.T) {
1332		want := []float32{
1333			float32(math.Float32frombits(0xffc00000)), // -NaN
1334			float32(math.Inf(-1)),
1335			float32(-math.MaxFloat32),
1336			float32(-123.456),
1337			float32(-math.SmallestNonzeroFloat32),
1338			float32(math.Copysign(0, -1)),
1339			float32(math.Copysign(0, +1)),
1340			float32(+math.SmallestNonzeroFloat32),
1341			float32(+123.456),
1342			float32(+math.MaxFloat32),
1343			float32(math.Inf(+1)),
1344			float32(math.Float32frombits(0x7fc00000)), // +NaN
1345		}
1346		for i := 0; i < 10; i++ {
1347			t.Run("", func(t *testing.T) {
1348				got := append([]float32(nil), want...)
1349				rn := rand.New(rand.NewSource(int64(i)))
1350				for i, j := range rn.Perm(len(got)) {
1351					got[i], got[j] = got[j], got[i]
1352				}
1353				sort.Slice(got, func(i, j int) bool {
1354					return lessF32(got[i], got[j])
1355				})
1356				cmpF32s := cmp.Comparer(func(x, y float32) bool {
1357					return math.Float32bits(x) == math.Float32bits(y)
1358				})
1359				if diff := cmp.Diff(want, got, cmpF32s); diff != "" {
1360					t.Errorf("Sort mismatch (-want +got):\n%s", diff)
1361				}
1362			})
1363		}
1364	})
1365	t.Run("F64", func(t *testing.T) {
1366		want := []float64{
1367			float64(math.Float64frombits(0xfff8000000000001)), // -NaN
1368			float64(math.Inf(-1)),
1369			float64(-math.MaxFloat64),
1370			float64(-123.456),
1371			float64(-math.SmallestNonzeroFloat64),
1372			float64(math.Copysign(0, -1)),
1373			float64(math.Copysign(0, +1)),
1374			float64(+math.SmallestNonzeroFloat64),
1375			float64(+123.456),
1376			float64(+math.MaxFloat64),
1377			float64(math.Inf(+1)),
1378			float64(math.Float64frombits(0x7ff8000000000001)), // +NaN
1379		}
1380		for i := 0; i < 10; i++ {
1381			t.Run("", func(t *testing.T) {
1382				got := append([]float64(nil), want...)
1383				rn := rand.New(rand.NewSource(int64(i)))
1384				for i, j := range rn.Perm(len(got)) {
1385					got[i], got[j] = got[j], got[i]
1386				}
1387				sort.Slice(got, func(i, j int) bool {
1388					return lessF64(got[i], got[j])
1389				})
1390				cmpF64s := cmp.Comparer(func(x, y float64) bool {
1391					return math.Float64bits(x) == math.Float64bits(y)
1392				})
1393				if diff := cmp.Diff(want, got, cmpF64s); diff != "" {
1394					t.Errorf("Sort mismatch (-want +got):\n%s", diff)
1395				}
1396			})
1397		}
1398	})
1399}
1400