// 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 proto_test import ( "fmt" "math" "testing" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/internal/pragma" "google.golang.org/protobuf/internal/protobuild" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protopack" testpb "google.golang.org/protobuf/internal/testprotos/test" test3pb "google.golang.org/protobuf/internal/testprotos/test3" testeditionspb "google.golang.org/protobuf/internal/testprotos/testeditions" ) func TestEqual(t *testing.T) { identicalPtrPb := &testpb.TestAllTypes{MapStringString: map[string]string{"a": "b", "c": "d"}} type incomparableMessage struct { *testpb.TestAllTypes pragma.DoNotCompare } type test struct { desc string x, y proto.Message eq bool } tests := []test{ { x: nil, y: nil, eq: true, }, { x: (*testpb.TestAllTypes)(nil), y: nil, eq: false, }, { x: (*testpb.TestAllTypes)(nil), y: (*testpb.TestAllTypes)(nil), eq: true, }, { x: new(testpb.TestAllTypes), y: (*testpb.TestAllTypes)(nil), eq: false, }, { x: new(testpb.TestAllTypes), y: new(testpb.TestAllTypes), eq: true, }, { x: (*testpb.TestAllTypes)(nil), y: (*testpb.TestAllExtensions)(nil), eq: false, }, { x: (*testpb.TestAllTypes)(nil), y: new(testpb.TestAllExtensions), eq: false, }, { x: new(testpb.TestAllTypes), y: new(testpb.TestAllExtensions), eq: false, }, // Identical input pointers { x: identicalPtrPb, y: identicalPtrPb, eq: true, }, // Incomparable types. The top-level types are not actually directly // compared (which would panic), but rather the comparison happens on the // objects returned by ProtoReflect(). These tests are here just to ensure // that any short-circuit checks do not accidentally try to compare // incomparable top-level types. { x: incomparableMessage{TestAllTypes: identicalPtrPb}, y: incomparableMessage{TestAllTypes: identicalPtrPb}, eq: true, }, { x: identicalPtrPb, y: incomparableMessage{TestAllTypes: identicalPtrPb}, eq: true, }, { x: identicalPtrPb, y: &incomparableMessage{TestAllTypes: identicalPtrPb}, eq: true, }, } type xy struct { X proto.Message Y proto.Message } cloneAll := func(in []proto.Message) []proto.Message { out := make([]proto.Message, len(in)) for idx, msg := range in { out[idx] = proto.Clone(msg) } return out } makeXY := func(x protobuild.Message, y protobuild.Message, messages ...proto.Message) []xy { xs := makeMessages(x, cloneAll(messages)...) ys := makeMessages(y, cloneAll(messages)...) result := make([]xy, len(xs)) for idx := range xs { result[idx] = xy{ X: xs[idx], Y: ys[idx], } } return result } makeTest := func(tmpl test, msgs []xy) []test { result := make([]test, 0, len(msgs)) for _, msg := range msgs { tmpl.x = msg.X tmpl.y = msg.Y result = append(result, tmpl) } return result } // Scalars. tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_int32": 1}, protobuild.Message{"optional_int32": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_int64": 1}, protobuild.Message{"optional_int64": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_uint32": 1}, protobuild.Message{"optional_uint32": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_uint64": 1}, protobuild.Message{"optional_uint64": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_sint32": 1}, protobuild.Message{"optional_sint32": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_sint64": 1}, protobuild.Message{"optional_sint64": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_fixed32": 1}, protobuild.Message{"optional_fixed32": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_fixed64": 1}, protobuild.Message{"optional_fixed64": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_sfixed32": 1}, protobuild.Message{"optional_sfixed32": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_sfixed64": 1}, protobuild.Message{"optional_sfixed64": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_float": 1}, protobuild.Message{"optional_float": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_double": 1}, protobuild.Message{"optional_double": 2}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_float": math.NaN()}, protobuild.Message{"optional_float": 0}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_double": math.NaN()}, protobuild.Message{"optional_double": 0}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_bool": true}, protobuild.Message{"optional_bool": false}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_string": "a"}, protobuild.Message{"optional_string": "b"}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_bytes": []byte("a")}, protobuild.Message{"optional_bytes": []byte("b")}, ))...) tests = append(tests, makeTest(test{desc: "Scalars"}, makeXY( protobuild.Message{"optional_nested_enum": "FOO"}, protobuild.Message{"optional_nested_enum": "BAR"}, ))...) // Scalars (equal). tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_int32": 2}, protobuild.Message{"optional_int32": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_int64": 2}, protobuild.Message{"optional_int64": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_uint32": 2}, protobuild.Message{"optional_uint32": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_uint64": 2}, protobuild.Message{"optional_uint64": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_sint32": 2}, protobuild.Message{"optional_sint32": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_sint64": 2}, protobuild.Message{"optional_sint64": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_fixed32": 2}, protobuild.Message{"optional_fixed32": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_fixed64": 2}, protobuild.Message{"optional_fixed64": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_sfixed32": 2}, protobuild.Message{"optional_sfixed32": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_sfixed64": 2}, protobuild.Message{"optional_sfixed64": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_float": 2}, protobuild.Message{"optional_float": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_double": 2}, protobuild.Message{"optional_double": 2}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_float": math.NaN()}, protobuild.Message{"optional_float": math.NaN()}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_double": math.NaN()}, protobuild.Message{"optional_double": math.NaN()}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_bool": true}, protobuild.Message{"optional_bool": true}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_string": "abc"}, protobuild.Message{"optional_string": "abc"}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_bytes": []byte("abc")}, protobuild.Message{"optional_bytes": []byte("abc")}, ))...) tests = append(tests, makeTest(test{ desc: "Scalars", eq: true, }, makeXY( protobuild.Message{"optional_nested_enum": "FOO"}, protobuild.Message{"optional_nested_enum": "FOO"}, ))...) // Presence. tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_int32": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_int64": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_uint32": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_uint64": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_sint32": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_sint64": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_fixed32": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_fixed64": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_sfixed32": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_sfixed64": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_float": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_double": 0}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_bool": false}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_string": ""}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_bytes": []byte{}}, ))...) tests = append(tests, makeTest(test{desc: "Presence"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_nested_enum": "FOO"}, ))...) // Proto2 default values are not considered by Equal, so the following are still unequal. allTypesNoProto3 := []proto.Message{ &testpb.TestAllTypes{}, // test3pb.TestAllTypes is intentionally missing: // proto3 does not support default values. // proto3 does not support groups. &testpb.TestAllExtensions{}, &testeditionspb.TestAllTypes{}, } tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{"default_int32": 81}, protobuild.Message{}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_int32": 81}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_int64": 82}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_uint32": 83}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_uint64": 84}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_sint32": -85}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_sint64": 86}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_fixed32": 87}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_fixed64": 87}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_sfixed32": 89}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_sfixed64": -90}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_float": 91.5}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_double": 92e3}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_bool": true}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_string": "hello"}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_bytes": []byte("world")}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Default"}, makeXY( protobuild.Message{}, protobuild.Message{"default_nested_enum": "BAR"}, &testpb.TestAllTypes{}, // test3pb.TestAllTypes is intentionally missing: // proto3 does not support default values. // testpb.TestAllExtensions is intentionally missing: // extensions cannot declare nested enums. &testeditionspb.TestAllTypes{}, ))...) // Groups. tests = append(tests, makeTest(test{desc: "Groups"}, makeXY( protobuild.Message{"optionalgroup": protobuild.Message{ "a": 1, }}, protobuild.Message{"optionalgroup": protobuild.Message{ "a": 2, }}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Groups"}, makeXY( protobuild.Message{}, protobuild.Message{"optionalgroup": protobuild.Message{}}, allTypesNoProto3..., ))...) // Messages. tests = append(tests, makeTest(test{desc: "Messages"}, makeXY( protobuild.Message{"optional_nested_message": protobuild.Message{ "a": 1, }}, protobuild.Message{"optional_nested_message": protobuild.Message{ "a": 2, }}, ))...) tests = append(tests, makeTest(test{desc: "Messages"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_nested_message": protobuild.Message{}}, ))...) // Lists. tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_int32": []int32{1}}, protobuild.Message{"repeated_int32": []int32{1, 2}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_int32": []int32{1, 2}}, protobuild.Message{"repeated_int32": []int32{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_int64": []int64{1, 2}}, protobuild.Message{"repeated_int64": []int64{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_uint32": []uint32{1, 2}}, protobuild.Message{"repeated_uint32": []uint32{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_uint64": []uint64{1, 2}}, protobuild.Message{"repeated_uint64": []uint64{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_sint32": []int32{1, 2}}, protobuild.Message{"repeated_sint32": []int32{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_sint64": []int64{1, 2}}, protobuild.Message{"repeated_sint64": []int64{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_fixed32": []uint32{1, 2}}, protobuild.Message{"repeated_fixed32": []uint32{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_fixed64": []uint64{1, 2}}, protobuild.Message{"repeated_fixed64": []uint64{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_sfixed32": []int32{1, 2}}, protobuild.Message{"repeated_sfixed32": []int32{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_sfixed64": []int32{1, 2}}, protobuild.Message{"repeated_sfixed64": []int32{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_float": []float32{1, 2}}, protobuild.Message{"repeated_float": []float32{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_double": []float64{1, 2}}, protobuild.Message{"repeated_double": []float64{1, 3}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_bool": []bool{true, false}}, protobuild.Message{"repeated_bool": []bool{true, true}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_string": []string{"a", "b"}}, protobuild.Message{"repeated_string": []string{"a", "c"}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_bytes": [][]byte{[]byte("a"), []byte("b")}}, protobuild.Message{"repeated_bytes": [][]byte{[]byte("a"), []byte("c")}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_nested_enum": []string{"FOO"}}, protobuild.Message{"repeated_nested_enum": []string{"BAR"}}, ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeatedgroup": []protobuild.Message{ {"a": 1}, {"a": 2}, }}, protobuild.Message{"repeatedgroup": []protobuild.Message{ {"a": 1}, {"a": 3}, }}, allTypesNoProto3..., ))...) tests = append(tests, makeTest(test{desc: "Lists"}, makeXY( protobuild.Message{"repeated_nested_message": []protobuild.Message{ {"a": 1}, {"a": 2}, }}, protobuild.Message{"repeated_nested_message": []protobuild.Message{ {"a": 1}, {"a": 3}, }}, ))...) // Maps: various configurations. allTypesNoExt := []proto.Message{ &testpb.TestAllTypes{}, &test3pb.TestAllTypes{}, // TestAllExtensions is intentionally missing: // protoc prevents adding a map field to an extension: // Map fields are not allowed to be extensions. &testeditionspb.TestAllTypes{}, } tests = append(tests, makeTest(test{desc: "MapsDifferent"}, makeXY( protobuild.Message{"map_int32_int32": map[int32]int32{1: 2}}, protobuild.Message{"map_int32_int32": map[int32]int32{3: 4}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsAdditionalY"}, makeXY( protobuild.Message{"map_int32_int32": map[int32]int32{1: 2}}, protobuild.Message{"map_int32_int32": map[int32]int32{1: 2, 3: 4}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsAdditionalX"}, makeXY( protobuild.Message{"map_int32_int32": map[int32]int32{1: 2, 3: 4}}, protobuild.Message{"map_int32_int32": map[int32]int32{1: 2}}, allTypesNoExt..., ))...) // Maps: various types. tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_int32_int32": map[int32]int32{1: 2, 3: 4}}, protobuild.Message{"map_int32_int32": map[int32]int32{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_int64_int64": map[int64]int64{1: 2, 3: 4}}, protobuild.Message{"map_int64_int64": map[int64]int64{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_uint32_uint32": map[uint32]uint32{1: 2, 3: 4}}, protobuild.Message{"map_uint32_uint32": map[uint32]uint32{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_uint64_uint64": map[uint64]uint64{1: 2, 3: 4}}, protobuild.Message{"map_uint64_uint64": map[uint64]uint64{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_sint32_sint32": map[int32]int32{1: 2, 3: 4}}, protobuild.Message{"map_sint32_sint32": map[int32]int32{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_sint64_sint64": map[int64]int64{1: 2, 3: 4}}, protobuild.Message{"map_sint64_sint64": map[int64]int64{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_fixed32_fixed32": map[uint32]uint32{1: 2, 3: 4}}, protobuild.Message{"map_fixed32_fixed32": map[uint32]uint32{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_fixed64_fixed64": map[uint64]uint64{1: 2, 3: 4}}, protobuild.Message{"map_fixed64_fixed64": map[uint64]uint64{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_sfixed32_sfixed32": map[int32]int32{1: 2, 3: 4}}, protobuild.Message{"map_sfixed32_sfixed32": map[int32]int32{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_sfixed64_sfixed64": map[int64]int64{1: 2, 3: 4}}, protobuild.Message{"map_sfixed64_sfixed64": map[int64]int64{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_int32_float": map[int32]float32{1: 2, 3: 4}}, protobuild.Message{"map_int32_float": map[int32]float32{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_int32_double": map[int32]float64{1: 2, 3: 4}}, protobuild.Message{"map_int32_double": map[int32]float64{1: 2, 3: 5}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_bool_bool": map[bool]bool{true: false, false: true}}, protobuild.Message{"map_bool_bool": map[bool]bool{true: false, false: false}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_string_string": map[string]string{"a": "b", "c": "d"}}, protobuild.Message{"map_string_string": map[string]string{"a": "b", "c": "e"}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_string_bytes": map[string][]byte{"a": []byte("b"), "c": []byte("d")}}, protobuild.Message{"map_string_bytes": map[string][]byte{"a": []byte("b"), "c": []byte("e")}}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_string_nested_message": map[string]protobuild.Message{ "a": {"a": int32(1)}, "b": {"a": int32(2)}, }}, protobuild.Message{"map_string_nested_message": map[string]protobuild.Message{ "a": {"a": int32(1)}, "b": {"a": int32(3)}, }}, allTypesNoExt..., ))...) tests = append(tests, makeTest(test{desc: "MapsTypes"}, makeXY( protobuild.Message{"map_string_nested_enum": map[string]string{ "a": "FOO", "b": "BAR", }}, protobuild.Message{"map_string_nested_enum": map[string]string{ "a": "FOO", "b": "BAZ", }}, allTypesNoExt..., ))...) // Unknown fields. tests = append(tests, makeTest(test{desc: "Unknown"}, makeXY( protobuild.Message{protobuild.Unknown: protopack.Message{ protopack.Tag{100000, protopack.VarintType}, protopack.Varint(1), }.Marshal()}, protobuild.Message{protobuild.Unknown: protopack.Message{ protopack.Tag{100000, protopack.VarintType}, protopack.Varint(2), }.Marshal()}, ))...) tests = append(tests, makeTest(test{desc: "Unknown"}, makeXY( protobuild.Message{protobuild.Unknown: protopack.Message{ protopack.Tag{100000, protopack.VarintType}, protopack.Varint(1), }.Marshal()}, protobuild.Message{}, ))...) // Extensions. onlyExts := []proto.Message{ &testpb.TestAllExtensions{}, &testeditionspb.TestAllExtensions{}, } tests = append(tests, makeTest(test{desc: "Extensions"}, makeXY( protobuild.Message{"optional_int32": 1}, protobuild.Message{"optional_int32": 2}, onlyExts..., ))...) tests = append(tests, makeTest(test{desc: "Extensions"}, makeXY( protobuild.Message{}, protobuild.Message{"optional_int32": 2}, onlyExts..., ))...) for _, tt := range tests { desc := tt.desc if desc == "" { desc = "Untitled" } desc += fmt.Sprintf("/x=%T y=%T", tt.x, tt.y) t.Run(desc, func(t *testing.T) { if !tt.eq && !proto.Equal(tt.x, tt.x) { t.Errorf("Equal(x, x) = false, want true\n==== x ====\n%v", prototext.Format(tt.x)) } if !tt.eq && !proto.Equal(tt.y, tt.y) { t.Errorf("Equal(y, y) = false, want true\n==== y ====\n%v", prototext.Format(tt.y)) } if eq := proto.Equal(tt.x, tt.y); eq != tt.eq { t.Errorf("Equal(x, y) = %v, want %v\n==== x ====\n%v==== y ====\n%v", eq, tt.eq, prototext.Format(tt.x), prototext.Format(tt.y)) } }) } } func BenchmarkEqualWithSmallEmpty(b *testing.B) { b.ReportAllocs() x := &testpb.ForeignMessage{} y := &testpb.ForeignMessage{} b.ResetTimer() for i := 0; i < b.N; i++ { proto.Equal(x, y) } } func BenchmarkEqualWithIdenticalPtrEmpty(b *testing.B) { b.ReportAllocs() x := &testpb.ForeignMessage{} b.ResetTimer() for i := 0; i < b.N; i++ { proto.Equal(x, x) } } func BenchmarkEqualWithLargeEmpty(b *testing.B) { b.ReportAllocs() x := &testpb.TestManyMessageFieldsMessage{ F1: makeNested(2), F10: makeNested(2), F20: makeNested(2), F30: makeNested(2), F40: makeNested(2), F50: makeNested(2), F60: makeNested(2), F70: makeNested(2), F80: makeNested(2), F90: makeNested(2), } y := &testpb.TestManyMessageFieldsMessage{ F1: makeNested(2), F10: makeNested(2), F20: makeNested(2), F30: makeNested(2), F40: makeNested(2), F50: makeNested(2), F60: makeNested(2), F70: makeNested(2), F80: makeNested(2), F90: makeNested(2), } b.ResetTimer() for i := 0; i < b.N; i++ { proto.Equal(x, y) } } func makeNested(depth int) *testpb.TestAllTypes { if depth <= 0 { return nil } return &testpb.TestAllTypes{ OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{ Corecursive: makeNested(depth - 1), }, } } func BenchmarkEqualWithDeeplyNestedEqual(b *testing.B) { b.ReportAllocs() x := makeNested(20) y := makeNested(20) b.ResetTimer() for i := 0; i < b.N; i++ { proto.Equal(x, y) } } func BenchmarkEqualWithDeeplyNestedDifferent(b *testing.B) { b.ReportAllocs() x := makeNested(20) y := makeNested(21) b.ResetTimer() for i := 0; i < b.N; i++ { proto.Equal(x, y) } } func BenchmarkEqualWithDeeplyNestedIdenticalPtr(b *testing.B) { b.ReportAllocs() x := makeNested(20) b.ResetTimer() for i := 0; i < b.N; i++ { proto.Equal(x, x) } }