protobuf-go/cmd/protoc-gen-go/name_clash_test/name_clash_test.go
Michael Stapelberg eb7b468655 all: Release the Opaque API
For golang/protobuf#1657

Change-Id: I7b2b0c30506706015ce278e6054439c9ad9ef727
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/634815
TryBot-Bypass: Michael Stapelberg <stapelberg@google.com>
Reviewed-by: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Damien Neil <dneil@google.com>
2024-12-11 03:16:51 -08:00

899 lines
31 KiB
Go

// Copyright 2024 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 name_clash_test
import (
"reflect"
"testing"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/internal/genid"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protodesc"
descpb "google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/gofeaturespb"
"google.golang.org/protobuf/types/pluginpb"
hpb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/nameclash/test_name_clash_hybrid"
opb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/nameclash/test_name_clash_opaque"
pb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/nameclash/test_name_clash_open"
)
// TestOpenMangling tests the backwards compatible mangling of fields
// who clashes with the getters. The expected behavior, which is
// somewhat surprising, is documented in the proto
// test_name_clash_open.proto itself.
func TestOpenMangling(t *testing.T) {
m1 := &pb.M1{
Foo: proto.Int32(1),
GetFoo_: proto.Int32(2),
GetGetFoo: proto.Int32(3),
}
if m1.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m1.GetFoo(), m1)
}
if m1.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m1.GetGetFoo_(), m1)
}
if m1.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m1.GetGetGetFoo(), m1)
}
m2 := &pb.M2{
Foo: proto.Int32(1),
GetFoo_: proto.Int32(2),
GetGetFoo: proto.Int32(3),
}
if m2.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m2.GetFoo(), m2)
}
if m2.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m2.GetGetFoo_(), m2)
}
if m2.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m2.GetGetGetFoo(), m2)
}
m3 := &pb.M3{
Foo_: proto.Int32(1),
GetFoo: proto.Int32(2),
GetGetFoo_: proto.Int32(3),
}
if m3.GetFoo_() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m3.GetFoo_(), m3)
}
if m3.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m3.GetGetFoo(), m3)
}
if m3.GetGetGetFoo_() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m3.GetGetGetFoo_(), m3)
}
m4 := &pb.M4{
GetFoo: proto.Int32(2),
GetGetFoo_: &pb.M4_GetGetGetFoo{GetGetGetFoo: 3},
Foo_: proto.Int32(1),
}
if m4.GetFoo_() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m4.GetFoo_(), m4)
}
if m4.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m4.GetGetFoo(), m4)
}
if m4.GetGetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m4.GetGetGetGetFoo(), m4)
}
m5 := &pb.M5{
GetFoo: proto.Int32(2),
GetGetGetFoo: &pb.M5_GetGetFoo_{GetGetFoo_: 3},
Foo_: proto.Int32(1),
}
if m5.GetFoo_() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m5.GetFoo_(), m5)
}
if m5.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m5.GetGetFoo(), m5)
}
if m5.GetGetGetFoo_() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m5.GetGetGetFoo_(), m5)
}
m6 := &pb.M6{
GetGetFoo: &pb.M6_GetGetGetFoo{GetGetGetFoo: 3},
GetFoo_: proto.Int32(2),
Foo: proto.Int32(1),
}
if m6.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m6.GetFoo(), m6)
}
if m6.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m6.GetGetFoo_(), m6)
}
if m6.GetGetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m6.GetGetGetGetFoo(), m6)
}
m7 := &pb.M7{
GetGetFoo: &pb.M7_GetFoo_{GetFoo_: 3},
Foo: proto.Int32(1),
}
if m7.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m7.GetFoo(), m7)
}
if m7.GetGetFoo_() != 3 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m7.GetGetFoo_(), m7)
}
m7.GetGetFoo = &pb.M7_Bar{Bar: true}
if !m7.GetBar() {
t.Errorf("Proto field 'bar' has unexpected value %v for %T (expected 3)", m7.GetBar(), m7)
}
m8 := &pb.M8{
GetGetGetFoo_: &pb.M8_GetGetFoo{GetGetFoo: 3},
GetFoo_: proto.Int32(2),
Foo: proto.Int32(1),
}
if m8.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m8.GetFoo(), m8)
}
if m8.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m8.GetGetFoo_(), m8)
}
if m8.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m8.GetGetGetFoo(), m8)
}
m9 := &pb.M9{
GetGetGetFoo_: &pb.M9_GetGetFoo{GetGetFoo: 3},
Foo: proto.Int32(1),
}
if m9.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m9.GetFoo(), m9)
}
if m9.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m9.GetGetGetFoo(), m9)
}
m9.GetGetGetFoo_ = &pb.M9_GetFoo_{GetFoo_: 2}
if m9.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m9.GetGetFoo_(), m9)
}
}
// TestHybridMangling tests the backwards compatible mangling as well
// as new style mangling of fields who clashes with the getters. The
// expected behavior, which is somewhat surprising, is documented in
// the proto test_name_clash_hybrid.proto itself.
func TestHybridMangling(t *testing.T) {
m1 := hpb.M1_builder{
Foo: proto.Int32(1),
GetFoo: proto.Int32(2),
GetGetFoo: proto.Int32(3),
}.Build()
if m1.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m1.GetFoo(), m1)
}
if m1.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m1.GetFoo(), m1)
}
if m1.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m1.GetGetFoo_(), m1)
}
if m1.Get_GetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m1.GetGetFoo_(), m1)
}
if m1.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m1.GetGetGetFoo(), m1)
}
checkNameConsistency(t, m1)
m2 := hpb.M2_builder{
Foo: proto.Int32(1),
GetFoo: proto.Int32(2),
GetGetFoo: proto.Int32(3),
}.Build()
if m2.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m2.GetFoo(), m2)
}
if m2.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m2.GetFoo(), m2)
}
if m2.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m2.GetGetFoo_(), m2)
}
if m2.Get_GetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m2.GetGetFoo_(), m2)
}
if m2.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m2.GetGetGetFoo(), m2)
}
checkNameConsistency(t, m2)
m3 := hpb.M3_builder{
Foo: proto.Int32(1),
GetFoo: proto.Int32(2),
GetGetFoo: proto.Int32(3),
}.Build()
if m3.GetFoo_() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m3.GetFoo_(), m3)
}
if m3.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m3.GetFoo_(), m3)
}
if m3.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m3.GetGetFoo(), m3)
}
if m3.Get_GetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m3.GetGetFoo(), m3)
}
if m3.GetGetGetFoo_() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m3.GetGetGetFoo_(), m3)
}
checkNameConsistency(t, m3)
m4 := hpb.M4_builder{
GetFoo: proto.Int32(2),
GetGetGetFoo: proto.Int32(3),
Foo: proto.Int32(1),
}.Build()
if m4.GetFoo_() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m4.GetFoo_(), m4)
}
if m4.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m4.Get_Foo(), m4)
}
if m4.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m4.GetGetFoo(), m4)
}
if m4.Get_GetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m4.Get_GetFoo(), m4)
}
if m4.GetGetGetFoo_().(*hpb.M4_GetGetGetFoo).GetGetGetFoo != 3 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 3)", m4.GetGetGetFoo_(), m4)
}
if !m4.HasGetGetFoo() {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected true)", m4.HasGetGetFoo(), m4)
}
if m4.GetGetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m4.GetGetGetGetFoo(), m4)
}
checkNameConsistency(t, m4)
m5 := hpb.M5_builder{
GetFoo: proto.Int32(2),
GetGetFoo: proto.Int32(3),
Foo: proto.Int32(1),
}.Build()
if m5.GetFoo_() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m5.GetFoo_(), m5)
}
if m5.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m5.Get_Foo(), m4)
}
if m5.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m5.GetGetFoo(), m5)
}
if m5.Get_GetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m5.Get_GetFoo(), m4)
}
if m5.GetGetGetFoo_() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m5.GetGetGetFoo_(), m5)
}
if m5.Get_GetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m5.Get_GetGetFoo(), m5)
}
checkNameConsistency(t, m5)
m6 := hpb.M6_builder{
GetGetGetFoo: proto.Int32(3),
GetFoo: proto.Int32(2),
Foo: proto.Int32(1),
}.Build()
if m6.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m6.GetFoo(), m6)
}
if m6.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m6.Get_Foo(), m6)
}
if m6.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m6.GetGetFoo_(), m6)
}
if m6.Get_GetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m6.Get_GetFoo(), m6)
}
if m6.GetGetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m6.GetGetGetGetFoo(), m6)
}
checkNameConsistency(t, m6)
m7 := hpb.M7_builder{
GetFoo: proto.Int32(3),
Foo: proto.Int32(1),
}.Build()
if m7.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m7.GetFoo(), m7)
}
if m7.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m7.Get_Foo(), m7)
}
if m7.GetGetFoo_() != 3 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 3)", m7.GetGetFoo_(), m7)
}
if m7.Get_GetFoo() != 3 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 3)", m7.Get_GetFoo(), m7)
}
m7.SetBar(true)
if !m7.GetBar() {
t.Errorf("Proto field 'bar' has unexpected value %v for %T (expected 3)", m7.GetBar(), m7)
}
checkNameConsistency(t, m7)
m8 := hpb.M8_builder{
GetGetFoo: proto.Int32(3),
GetFoo: proto.Int32(2),
Foo: proto.Int32(1),
}.Build()
if m8.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m8.GetFoo(), m8)
}
if m8.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m8.Get_Foo(), m8)
}
if m8.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m8.GetGetFoo_(), m8)
}
if m8.Get_GetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m8.Get_GetFoo(), m8)
}
if m8.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m8.GetGetGetFoo(), m8)
}
checkNameConsistency(t, m8)
m9 := hpb.M9_builder{
GetGetFoo: proto.Int32(3),
Foo: proto.Int32(1),
}.Build()
if m9.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m9.GetFoo(), m9)
}
if m9.Get_Foo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m9.Get_Foo(), m9)
}
if m9.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m9.GetGetGetFoo(), m9)
}
if m9.Get_GetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m9.Get_GetGetFoo(), m9)
}
m9.Set_GetFoo(2)
if m9.GetGetFoo_() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m9.GetGetFoo_(), m9)
}
if m9.Get_GetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m9.Get_GetFoo(), m9)
}
checkNameConsistency(t, m9)
m10 := hpb.M10_builder{
Foo: proto.Int32(1),
SetFoo: proto.Int32(2),
}.Build()
m10.Set_Foo(47)
if m10.Get_Foo() != 47 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 47)", m10.Get_Foo(), m10)
}
m10.SetSetFoo(11)
if m10.GetSetFoo() != 11 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m10.GetSetFoo(), m10)
}
checkNameConsistency(t, m10)
m11 := hpb.M11_builder{
Foo: proto.Int32(1),
SetSetFoo: proto.Int32(2),
}.Build()
m11.Set_Foo(47)
if m11.Get_Foo() != 47 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 47)", m11.Get_Foo(), m11)
}
m11.SetSetSetFoo(11)
if m11.GetSetSetFoo() != 11 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m11.GetSetSetFoo(), m11)
}
checkNameConsistency(t, m11)
m12 := hpb.M12_builder{
Foo: proto.Int32(1),
SetFoo: proto.Int32(2),
}.Build()
m12.Set_Foo(47)
if m12.Get_Foo() != 47 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 47)", m12.Get_Foo(), m12)
}
m12.Set_SetFoo(11)
if m12.Get_SetFoo() != 11 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m12.Get_SetFoo(), m12)
}
checkNameConsistency(t, m12)
m13 := hpb.M13_builder{
Foo: proto.Int32(1),
HasFoo: proto.Int32(2),
}.Build()
if !m13.Has_Foo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m13.Has_Foo(), m13)
}
if !m13.HasHasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m13.HasHasFoo(), m13)
}
checkNameConsistency(t, m13)
m14 := hpb.M14_builder{
Foo: proto.Int32(1),
HasHasFoo: proto.Int32(2),
}.Build()
if !m14.Has_Foo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.Has_Foo(), m14)
}
if !m14.Has_HasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.Has_HasFoo(), m14)
}
if !m14.HasHasHasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.HasHasHasFoo(), m14)
}
checkNameConsistency(t, m14)
m15 := hpb.M15_builder{
Foo: proto.Int32(1),
HasFoo: proto.Int32(2),
}.Build()
if !m15.Has_Foo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.Has_Foo(), m15)
}
if !m15.Has_HasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.Has_HasFoo(), m15)
}
if !m15.HasHasHasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.HasHasHasFoo(), m15)
}
checkNameConsistency(t, m15)
m16 := hpb.M16_builder{
Foo: proto.Int32(1),
ClearFoo: proto.Int32(2),
}.Build()
m16.Clear_Foo()
if m16.Has_Foo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m16.Has_Foo(), m16)
}
m16.ClearClearFoo()
if m16.HasClearFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m16.HasClearFoo(), m16)
}
checkNameConsistency(t, m16)
m17 := hpb.M17_builder{
Foo: proto.Int32(1),
ClearClearFoo: proto.Int32(2),
}.Build()
m17.Clear_Foo()
if m17.Has_Foo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m17.Has_Foo(), m17)
}
m17.ClearClearClearFoo()
if m17.HasClearClearFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m17.HasClearClearFoo(), m17)
}
checkNameConsistency(t, m17)
m18 := hpb.M18_builder{
Foo: proto.Int32(1),
ClearFoo: proto.Int32(2),
}.Build()
m18.Clear_Foo()
if m18.Has_Foo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m18.Has_Foo(), m18)
}
m18.Clear_ClearFoo()
if m18.Has_ClearFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m18.Has_ClearFoo(), m18)
}
checkNameConsistency(t, m18)
m19 := hpb.M19_builder{
Foo: proto.Int32(1),
WhichFoo: proto.Int32(2),
}.Build()
if m19.WhichWhichWhichFoo() != hpb.M19_WhichFoo_case {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected M19_ClearFoo_case)", m19.WhichWhichWhichFoo(), m19)
}
checkNameConsistency(t, m19)
m20 := hpb.M20_builder{
Foo: proto.Int32(1),
WhichWhichFoo: proto.Int32(2),
}.Build()
if m20.Which_WhichFoo() != hpb.M20_WhichWhichFoo_case {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected M20_WhichWhichFoo_case)", m20.Which_WhichFoo(), m20)
}
checkNameConsistency(t, m20)
}
// TestOpaqueMangling tests the backwards compatible mangling as well
// as new style mangling of fields who clashes with the getters. The
// expected behavior, which is somewhat surprising, is documented in
// the proto test_name_clash_opaque.proto itself.
func TestOpaqueMangling(t *testing.T) {
m1 := opb.M1_builder{
Foo: proto.Int32(1),
GetFoo: proto.Int32(2),
GetGetFoo: proto.Int32(3),
}.Build()
if m1.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m1.GetFoo(), m1)
}
if m1.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m1.GetGetFoo(), m1)
}
if m1.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m1.GetGetGetFoo(), m1)
}
checkNameConsistency(t, m1)
m2 := opb.M2_builder{
Foo: proto.Int32(1),
GetFoo: proto.Int32(2),
GetGetFoo: proto.Int32(3),
}.Build()
if m2.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m2.GetFoo(), m2)
}
if m2.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m2.GetGetFoo(), m2)
}
if m2.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m2.GetGetGetFoo(), m2)
}
checkNameConsistency(t, m2)
m3 := opb.M3_builder{
Foo: proto.Int32(1),
GetFoo: proto.Int32(2),
GetGetFoo: proto.Int32(3),
}.Build()
if m3.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m3.GetFoo(), m3)
}
if m3.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m3.GetGetFoo(), m3)
}
if m3.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m3.GetGetGetFoo(), m3)
}
checkNameConsistency(t, m3)
m4 := opb.M4_builder{
GetFoo: proto.Int32(2),
GetGetGetFoo: proto.Int32(3),
Foo: proto.Int32(1),
}.Build()
if m4.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m4.GetFoo(), m4)
}
if m4.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m4.GetGetFoo(), m4)
}
if !m4.HasGetGetFoo() {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected true)", m4.HasGetGetFoo(), m4)
}
if m4.GetGetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m4.GetGetGetGetFoo(), m4)
}
checkNameConsistency(t, m4)
m5 := opb.M5_builder{
GetFoo: proto.Int32(2),
GetGetFoo: proto.Int32(3),
Foo: proto.Int32(1),
}.Build()
if m5.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m5.GetFoo(), m5)
}
if m5.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m5.GetGetFoo(), m5)
}
if m5.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m5.GetGetGetFoo(), m5)
}
checkNameConsistency(t, m5)
m6 := opb.M6_builder{
GetGetGetFoo: proto.Int32(3),
GetFoo: proto.Int32(2),
Foo: proto.Int32(1),
}.Build()
if m6.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m6.GetFoo(), m6)
}
if m6.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m6.GetGetFoo(), m6)
}
if m6.GetGetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_get_foo' has unexpected value %v for %T (expected 3)", m6.GetGetGetGetFoo(), m6)
}
checkNameConsistency(t, m6)
m7 := opb.M7_builder{
GetFoo: proto.Int32(3),
Foo: proto.Int32(1),
}.Build()
if m7.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m7.GetFoo(), m7)
}
if m7.GetGetFoo() != 3 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 3)", m7.GetGetFoo(), m7)
}
m7.SetBar(true)
if !m7.GetBar() {
t.Errorf("Proto field 'bar' has unexpected value %v for %T (expected true)", m7.GetBar(), m7)
}
checkNameConsistency(t, m7)
m8 := opb.M8_builder{
GetGetFoo: proto.Int32(3),
GetFoo: proto.Int32(2),
Foo: proto.Int32(1),
}.Build()
if m8.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m8.GetFoo(), m8)
}
if m8.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m8.GetGetFoo(), m8)
}
if m8.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m8.GetGetGetFoo(), m8)
}
checkNameConsistency(t, m8)
m9 := opb.M9_builder{
GetGetFoo: proto.Int32(3),
Foo: proto.Int32(1),
}.Build()
if m9.GetFoo() != 1 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 1)", m9.GetFoo(), m9)
}
if m9.GetGetGetFoo() != 3 {
t.Errorf("Proto field 'get_get_foo' has unexpected value %v for %T (expected 3)", m9.GetGetGetFoo(), m9)
}
m9.SetGetFoo(2)
if m9.GetGetFoo() != 2 {
t.Errorf("Proto field 'get_foo' has unexpected value %v for %T (expected 2)", m9.GetGetFoo(), m9)
}
checkNameConsistency(t, m9)
m10 := opb.M10_builder{
Foo: proto.Int32(1),
SetFoo: proto.Int32(2),
}.Build()
m10.SetFoo(48)
if m10.GetFoo() != 48 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 48)", m10.GetFoo(), m10)
}
m10.SetSetFoo(11)
if m10.GetSetFoo() != 11 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m10.GetSetFoo(), m10)
}
checkNameConsistency(t, m10)
m11 := opb.M11_builder{
Foo: proto.Int32(1),
SetSetFoo: proto.Int32(2),
}.Build()
m11.SetFoo(48)
if m11.GetFoo() != 48 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 48)", m11.GetFoo(), m11)
}
m11.SetSetSetFoo(11)
if m11.GetSetSetFoo() != 11 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 11)", m11.GetSetSetFoo(), m11)
}
checkNameConsistency(t, m11)
m12 := opb.M12_builder{
Foo: proto.Int32(1),
SetFoo: proto.Int32(2),
}.Build()
m12.SetFoo(48)
if m12.GetFoo() != 48 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 48)", m12.GetFoo(), m12)
}
m12.SetSetFoo(12)
if m12.GetSetFoo() != 12 {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected 12)", m12.GetSetFoo(), m12)
}
checkNameConsistency(t, m12)
m13 := opb.M13_builder{
Foo: proto.Int32(1),
HasFoo: proto.Int32(2),
}.Build()
if !m13.HasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m13.HasFoo(), m13)
}
if !m13.HasHasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m13.HasHasFoo(), m13)
}
checkNameConsistency(t, m13)
m14 := opb.M14_builder{
Foo: proto.Int32(1),
HasHasFoo: proto.Int32(2),
}.Build()
if !m14.HasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.HasFoo(), m14)
}
if !m14.HasHasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.HasHasFoo(), m14)
}
if !m14.HasHasHasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m14.HasHasHasFoo(), m14)
}
checkNameConsistency(t, m14)
m15 := opb.M15_builder{
Foo: proto.Int32(1),
HasFoo: proto.Int32(2),
}.Build()
if !m15.HasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.HasFoo(), m15)
}
if !m15.HasHasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.HasHasFoo(), m15)
}
if !m15.HasHasHasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected true)", m15.HasHasHasFoo(), m15)
}
checkNameConsistency(t, m15)
m16 := opb.M16_builder{
Foo: proto.Int32(1),
ClearFoo: proto.Int32(2),
}.Build()
m16.SetFoo(4711)
m16.ClearFoo()
if m16.HasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m16.HasFoo(), m16)
}
m16.ClearClearFoo()
if m16.HasClearFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m16.HasClearFoo(), m16)
}
checkNameConsistency(t, m16)
m17 := opb.M17_builder{
Foo: proto.Int32(1),
ClearClearFoo: proto.Int32(2),
}.Build()
m17.SetFoo(4711)
m17.ClearFoo()
if m17.HasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m17.HasFoo(), m17)
}
m17.ClearClearClearFoo()
if m17.HasClearClearFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m17.HasClearClearFoo(), m17)
}
checkNameConsistency(t, m17)
m18 := opb.M18_builder{
Foo: proto.Int32(1),
ClearFoo: proto.Int32(2),
}.Build()
m18.SetFoo(4711)
m18.ClearFoo()
if m18.HasFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m18.HasFoo(), m18)
}
m18.SetClearFoo(13)
m18.ClearClearFoo()
if m18.HasClearFoo() {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected false)", m18.HasClearFoo(), m18)
}
checkNameConsistency(t, m18)
m19 := opb.M19_builder{
Foo: proto.Int32(1),
WhichFoo: proto.Int32(2),
}.Build()
if m19.WhichWhichWhichFoo() != opb.M19_WhichFoo_case {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected M19_ClearFoo_case)", m19.WhichWhichWhichFoo(), m19)
}
checkNameConsistency(t, m19)
m20 := opb.M20_builder{
Foo: proto.Int32(1),
WhichWhichFoo: proto.Int32(2),
}.Build()
if m20.WhichWhichFoo() != opb.M20_WhichWhichFoo_case {
t.Errorf("Proto field 'foo' has unexpected value %v for %T (expected M20_WhichWhichFoo_case)", m20.WhichWhichFoo(), m20)
}
checkNameConsistency(t, m20)
}
func protogenFor(t *testing.T, m proto.Message) *protogen.Message {
t.Helper()
md := m.ProtoReflect().Descriptor()
// Construct a Protobuf plugin code generation request based on the
// transitive closure of dependencies of message m.
req := &pluginpb.CodeGeneratorRequest{
ProtoFile: []*descpb.FileDescriptorProto{
protodesc.ToFileDescriptorProto(descpb.File_google_protobuf_descriptor_proto),
protodesc.ToFileDescriptorProto(gofeaturespb.File_google_protobuf_go_features_proto),
protodesc.ToFileDescriptorProto(md.ParentFile()),
},
}
plugin, err := protogen.Options{}.New(req)
if err != nil {
t.Fatalf("protogen.Options.New: %v", err)
}
if got, want := len(plugin.Files), len(req.ProtoFile); got != want {
t.Fatalf("protogen returned %d plugin.Files entries, expected %d", got, want)
}
file := plugin.Files[len(plugin.Files)-1]
for _, msg := range file.Messages {
if msg.Desc.FullName() != md.FullName() {
continue
}
return msg
}
t.Fatalf("BUG: message %q not found in protogen response", md.FullName())
return nil
}
// checkNameConsistency will go through the fields (deliberately avoiding
// protoreflect; querying protogen instead), and for each field check that one
// of the two naming schemes is used consistently, so that if you find
// e.g. Has_Foo, the setter will be Set_Foo and not SetFoo. It also checks that
// at least one of the naming schemes apply.
func checkNameConsistency(t *testing.T, m proto.Message) {
t.Helper()
// It's wrong to use Go reflection on a Message, but in this
// case, we're specifically looking at the implementation
typ := reflect.TypeOf(m)
// The info we need for one field
type fi struct {
name string
prefixes []string
}
// The method prefixes for different kinds of fields
repeatedPrefixes := []string{"Get", "Set"}
oneofPrefixes := []string{"Has", "Clear", "Which"}
optionalPrefixes := []string{"Get", "Set", "Has", "Clear"}
fields := []fi{}
msg := protogenFor(t, m)
for _, f := range msg.Fields {
prefixes := optionalPrefixes
if f.Desc.Cardinality() == genid.Field_CARDINALITY_REPEATED_enum_value {
prefixes = repeatedPrefixes
}
fields = append(fields, fi{name: f.GoName, prefixes: prefixes})
}
for _, o := range msg.Oneofs {
fields = append(fields, fi{name: o.GoName, prefixes: oneofPrefixes})
}
if len(fields) == 0 {
t.Errorf("Message %v checked for consistency has no fields to check", reflect.TypeOf(m))
}
// Check method names for all fields
for _, f := range fields {
// Remove trailing underscores added by old name mangling algorithm
for f.name[len(f.name)-1] == '_' {
f.name = f.name[:len(f.name)-1]
}
// Check consistency of either "underscored" methods or "non underscored"
found := ""
for _, infix := range []string{"_", ""} {
for _, prefix := range f.prefixes {
if m, ok := typ.MethodByName(prefix + infix + f.name); ok {
found = m.Name
break
}
}
if found != "" {
for _, prefix := range f.prefixes {
if _, ok := typ.MethodByName(prefix + infix + f.name); !ok {
t.Errorf("Field %s has inconsistent method names - found %s, but not %s", f.name, found, prefix+infix+f.name)
}
}
break
}
}
// If we found neither, something is wrong
if found == "" {
t.Errorf("Field %s has neither plain nor underscored methods.", f.name)
}
}
}