github.com/googleapis/api-linter@v1.65.2/rules/internal/testutils/parse.go (about)

     1  // Copyright 2019 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // 		https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package testutils
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"strings"
    21  	"testing"
    22  	"text/template"
    23  
    24  	"github.com/jhump/protoreflect/desc"
    25  	"github.com/jhump/protoreflect/desc/protoparse"
    26  	"github.com/lithammer/dedent"
    27  
    28  	// These imports cause the common protos to be registered with
    29  	// the protocol buffer registry, and therefore make the call to
    30  	// `proto.FileDescriptor` work for the imported files.
    31  	_ "cloud.google.com/go/longrunning/autogen/longrunningpb"
    32  	_ "google.golang.org/genproto/googleapis/api/annotations"
    33  	_ "google.golang.org/genproto/googleapis/type/date"
    34  	_ "google.golang.org/genproto/googleapis/type/datetime"
    35  	_ "google.golang.org/genproto/googleapis/type/timeofday"
    36  )
    37  
    38  // ParseProtoStrings parses a map representing a proto files, and returns
    39  // a slice of FileDescriptors.
    40  //
    41  // It dedents the string before parsing.
    42  func ParseProtoStrings(t *testing.T, src map[string]string) map[string]*desc.FileDescriptor {
    43  	filenames := []string{}
    44  	for k, v := range src {
    45  		filenames = append(filenames, k)
    46  		src[k] = strings.TrimSpace(dedent.Dedent(v))
    47  	}
    48  
    49  	// Parse the file.
    50  	parser := protoparse.Parser{
    51  		Accessor:              protoparse.FileContentsFromMap(src),
    52  		IncludeSourceCodeInfo: true,
    53  		LookupImport:          desc.LoadFileDescriptor,
    54  	}
    55  	fds, err := parser.ParseFiles(filenames...)
    56  	if err != nil {
    57  		t.Fatalf("%v", err)
    58  	}
    59  	answer := map[string]*desc.FileDescriptor{}
    60  	for _, fd := range fds {
    61  		answer[fd.GetName()] = fd
    62  	}
    63  	return answer
    64  }
    65  
    66  // ParseProto3String parses a string representing a proto file, and returns
    67  // a FileDescriptor.
    68  //
    69  // It adds the `syntax = "proto3";` line to the beginning of the file and
    70  // chooses a filename, and then calls ParseProtoStrings.
    71  func ParseProto3String(t *testing.T, src string) *desc.FileDescriptor {
    72  	return ParseProtoStrings(t, map[string]string{
    73  		"test.proto": fmt.Sprintf(
    74  			"syntax = \"proto3\";\n\n%s",
    75  			strings.TrimSpace(dedent.Dedent(src)),
    76  		),
    77  	})["test.proto"]
    78  }
    79  
    80  // ParseProto3Tmpl parses a template string representing a proto file, and
    81  // returns a FileDescriptor.
    82  //
    83  // It parses the template using Go's text/template Parse function, and then
    84  // calls ParseProto3String.
    85  func ParseProto3Tmpl(t *testing.T, src string, data interface{}) *desc.FileDescriptor {
    86  	return ParseProto3Tmpls(t, map[string]string{
    87  		"test.proto": src,
    88  	}, data)["test.proto"]
    89  }
    90  
    91  // ParseProto3Tmpls parses template strings representing a proto file,
    92  // and returns FileDescriptors.
    93  //
    94  // It parses the template using Go's text/template Parse function, and then
    95  // calls ParseProto3Strings.
    96  func ParseProto3Tmpls(t *testing.T, srcs map[string]string, data interface{}) map[string]*desc.FileDescriptor {
    97  	strs := map[string]string{}
    98  	for fn, src := range srcs {
    99  		// Create a new template object.
   100  		tmpl, err := template.New("test").Parse(src)
   101  		if err != nil {
   102  			t.Fatalf("Unable to parse Go template: %v", err)
   103  		}
   104  
   105  		// Execute the template and write the results to a bytes representing
   106  		// the desired proto.
   107  		var protoBytes bytes.Buffer
   108  		err = tmpl.Execute(&protoBytes, data)
   109  		if err != nil {
   110  			t.Fatalf("Unable to execute Go template: %v", err)
   111  		}
   112  
   113  		// Add the proto to the map to send to parse strings.
   114  		strs[fn] = fmt.Sprintf("syntax = %q;\n\n%s", "proto3", protoBytes.String())
   115  	}
   116  
   117  	// Parse the proto as a string.
   118  	return ParseProtoStrings(t, strs)
   119  }