blob: b3adf7681c9a9eb91f0bf20b5f08f93b5f4a0c47 [file] [log] [blame]
// Copyright 2025 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 main
import (
"slices"
"strings"
"testing"
visibilitypb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/visibility"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/types/descriptorpb"
)
func TestVisibility(t *testing.T) {
// Verify that the visibility modifiers aren't lost when converting to descriptor protos.
fd := visibilitypb.File_cmd_protoc_gen_go_testdata_visibility_visibility_proto
fdProto := protodesc.ToFileDescriptorProto(fd)
type testCase struct {
name string
visibility descriptorpb.SymbolVisibility
}
testCases := []testCase{
{
name: "DefaultMessage",
visibility: descriptorpb.SymbolVisibility_VISIBILITY_UNSET,
},
{
name: "ExportMessage",
visibility: descriptorpb.SymbolVisibility_VISIBILITY_EXPORT,
},
{
name: "LocalMessage",
visibility: descriptorpb.SymbolVisibility_VISIBILITY_LOCAL,
},
{
name: "DefaultEnum",
visibility: descriptorpb.SymbolVisibility_VISIBILITY_UNSET,
},
{
name: "ExportEnum",
visibility: descriptorpb.SymbolVisibility_VISIBILITY_EXPORT,
},
{
name: "LocalEnum",
visibility: descriptorpb.SymbolVisibility_VISIBILITY_LOCAL,
},
}
// Instead of the boilerplate of handwritten cases for the nested types, we generate them
// algorithmically:
baseTestCases := slices.Clone(testCases)
for _, topLevelCase := range baseTestCases {
if strings.HasSuffix(topLevelCase.name, "Enum") {
continue // only messages have nested types
}
for _, nestedCase := range baseTestCases {
nestedCase.name = topLevelCase.name + ".Nested" + nestedCase.name
testCases = append(testCases, nestedCase)
}
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
path := strings.Split(tc.name, ".")
decl, visPtr := findByName(fdProto.MessageType, fdProto.EnumType, path)
if decl.GetVisibility() != tc.visibility {
t.Errorf("expected %v to have %v visibility but instead got %v", decl.GetName(), tc.visibility, decl.GetVisibility())
}
if tc.visibility == descriptorpb.SymbolVisibility_VISIBILITY_UNSET {
// For this one, we go one step further and expect the raw field to be a nil pointer.
if visPtr != nil {
t.Errorf("expected %v to have no visibility but instead got %v", decl.GetName(), visPtr)
}
}
})
}
}
func findByName(msgs []*descriptorpb.DescriptorProto, enums []*descriptorpb.EnumDescriptorProto, path []string) (hasVisibility, *descriptorpb.SymbolVisibility) {
name := path[0]
if len(path) > 1 {
// Looking for nested type.
for _, msg := range msgs {
if msg.GetName() == name {
return findByName(msg.NestedType, msg.EnumType, path[1:])
}
}
return nil, nil
}
if strings.HasSuffix(name, "Enum") {
for _, enum := range enums {
if enum.GetName() == name {
return enum, enum.Visibility
}
}
return nil, nil
}
for _, msg := range msgs {
if msg.GetName() == name {
return msg, msg.Visibility
}
}
return nil, nil
}
type hasVisibility interface {
GetName() string
GetVisibility() descriptorpb.SymbolVisibility
}