github.com/stackb/rules_proto@v0.0.0-20240221195024-5428336c51f1/pkg/protoc/resolver_test.go (about)

     1  package protoc
     2  
     3  import (
     4  	"bytes"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/bazelbuild/bazel-gazelle/label"
     9  	"github.com/bazelbuild/bazel-gazelle/resolve"
    10  	"github.com/google/go-cmp/cmp"
    11  	"github.com/google/go-cmp/cmp/cmpopts"
    12  )
    13  
    14  func TestLoadResolver(t *testing.T) {
    15  	for name, tc := range map[string]struct {
    16  		in    string
    17  		known map[string]importLabels
    18  	}{
    19  		"empty string": {
    20  			in:    "",
    21  			known: map[string]importLabels{},
    22  		},
    23  		"comment": {
    24  			in:    "# ignored",
    25  			known: map[string]importLabels{},
    26  		},
    27  		"proto resolve": {
    28  			in: "proto,proto,google/protobuf/any.proto,@com_google_protobuf//:any_proto",
    29  			known: map[string]importLabels{
    30  				"proto proto": map[string][]label.Label{
    31  					"google/protobuf/any.proto": {label.New("com_google_protobuf", "", "any_proto")},
    32  				},
    33  			},
    34  		},
    35  	} {
    36  		t.Run(name, func(t *testing.T) {
    37  			resolver := &resolver{
    38  				options: &ImportResolverOptions{
    39  					Debug:  false,
    40  					Printf: t.Logf,
    41  				},
    42  				known: make(map[string]importLabels),
    43  			}
    44  			if err := resolver.Load(strings.NewReader(tc.in)); err != nil {
    45  				t.Fatal(err)
    46  			}
    47  			if diff := cmp.Diff(tc.known, resolver.known); diff != "" {
    48  				t.Error("unexpected diff:", diff)
    49  			}
    50  		})
    51  	}
    52  }
    53  
    54  func TestSaveResolver(t *testing.T) {
    55  	for name, tc := range map[string]struct {
    56  		known    map[string]importLabels
    57  		repoName string
    58  		out      string
    59  	}{
    60  		"empty string": {
    61  			known: map[string]importLabels{},
    62  			out:   "",
    63  		},
    64  		"does not write external labels": {
    65  			// the resolver generally loads resolves from other csv files, so we
    66  			// don't want to "transitively" emit them.  Saved output should onlt
    67  			// reflect the current workspace.
    68  			known: map[string]importLabels{
    69  				"proto proto": map[string][]label.Label{
    70  					"google/protobuf/any.proto": {label.New("com_google_protobuf", "", "any_proto")},
    71  				},
    72  			},
    73  			out: "",
    74  		},
    75  		"rewrites labels with repoName": {
    76  			repoName: "com_google_protobuf",
    77  			known: map[string]importLabels{
    78  				"proto proto": map[string][]label.Label{
    79  					"google/protobuf/any.proto": {label.New("", "", "any_proto")},
    80  				},
    81  			},
    82  			out: "proto,proto,google/protobuf/any.proto,@com_google_protobuf//:any_proto\n",
    83  		},
    84  	} {
    85  		t.Run(name, func(t *testing.T) {
    86  			resolver := &resolver{
    87  				options: &ImportResolverOptions{
    88  					Debug:  false,
    89  					Printf: t.Logf,
    90  				},
    91  				known: tc.known,
    92  			}
    93  			var out bytes.Buffer
    94  			resolver.Save(&out, tc.repoName)
    95  			if diff := cmp.Diff(tc.out, out.String()); diff != "" {
    96  				t.Error("unexpected diff:", diff)
    97  			}
    98  		})
    99  	}
   100  }
   101  
   102  func TestProvide(t *testing.T) {
   103  	for name, tc := range map[string]struct {
   104  		lang, impLang, imp string
   105  		from               label.Label
   106  		known              map[string]importLabels
   107  	}{
   108  		"empty case": {
   109  			known: map[string]importLabels{
   110  				" ": map[string][]label.Label{
   111  					"": {label.NoLabel},
   112  				},
   113  			},
   114  		},
   115  		"typical usage": {
   116  			lang:    "proto",
   117  			impLang: "proto",
   118  			imp:     "google/protobuf/any.proto",
   119  			from:    label.New("com_google_protobuf", "", "any_proto"),
   120  			known: map[string]importLabels{
   121  				"proto proto": map[string][]label.Label{
   122  					"google/protobuf/any.proto": {label.New("com_google_protobuf", "", "any_proto")},
   123  				},
   124  			},
   125  		},
   126  	} {
   127  		t.Run(name, func(t *testing.T) {
   128  			resolver := &resolver{
   129  				options: &ImportResolverOptions{
   130  					Debug:  false,
   131  					Printf: t.Logf,
   132  				},
   133  				known: make(map[string]importLabels),
   134  			}
   135  			resolver.Provide(tc.lang, tc.impLang, tc.imp, tc.from)
   136  			if diff := cmp.Diff(tc.known, resolver.known); diff != "" {
   137  				t.Error("unexpected diff:", diff)
   138  			}
   139  		})
   140  	}
   141  }
   142  
   143  func TestResolve(t *testing.T) {
   144  	for name, tc := range map[string]struct {
   145  		lang, impLang, imp string
   146  		want               []resolve.FindResult
   147  		known              map[string]importLabels
   148  	}{
   149  		"empty case - matches a single empty result": {
   150  			known: map[string]importLabels{
   151  				" ": map[string][]label.Label{
   152  					"": {label.NoLabel},
   153  				},
   154  			},
   155  			want: []resolve.FindResult{{}},
   156  		},
   157  		"typical usage": {
   158  			lang:    "proto",
   159  			impLang: "proto",
   160  			imp:     "google/protobuf/any.proto",
   161  			want: []resolve.FindResult{
   162  				{
   163  					Label: label.New("com_google_protobuf", "", "any_proto"),
   164  				},
   165  			},
   166  			known: map[string]importLabels{
   167  				"proto proto": map[string][]label.Label{
   168  					"google/protobuf/any.proto": {label.New("com_google_protobuf", "", "any_proto")},
   169  				},
   170  			},
   171  		},
   172  	} {
   173  		t.Run(name, func(t *testing.T) {
   174  			resolver := &resolver{
   175  				options: &ImportResolverOptions{
   176  					Debug:  false,
   177  					Printf: t.Logf,
   178  				},
   179  				known: tc.known,
   180  			}
   181  			got := resolver.Resolve(tc.lang, tc.impLang, tc.imp)
   182  			if diff := cmp.Diff(tc.want, got); diff != "" {
   183  				t.Errorf("Resolve() mismatch (-want +got):\n%s", diff)
   184  			}
   185  		})
   186  	}
   187  }
   188  
   189  func TestProvided(t *testing.T) {
   190  	for name, tc := range map[string]struct {
   191  		lang, impLang string
   192  		want          map[label.Label][]string
   193  		known         map[string]importLabels
   194  	}{
   195  		"empty case - nothing known": {},
   196  		"typical usage": {
   197  			lang:    "proto",
   198  			impLang: "proto",
   199  			known: map[string]importLabels{
   200  				"proto proto": map[string][]label.Label{
   201  					"google/protobuf/any.proto":        {label.New("com_google_protobuf", "", "any_proto")},
   202  					"google/protobuf/any_helper.proto": {label.New("com_google_protobuf", "", "any_proto")},
   203  					"google/protobuf/duration.proto":   {label.New("com_google_protobuf", "", "duration_proto")},
   204  				},
   205  			},
   206  			want: map[label.Label][]string{
   207  				label.New("com_google_protobuf", "", "any_proto"): {
   208  					"google/protobuf/any.proto",
   209  					"google/protobuf/any_helper.proto",
   210  				},
   211  				label.New("com_google_protobuf", "", "duration_proto"): {
   212  					"google/protobuf/duration.proto",
   213  				},
   214  			},
   215  		},
   216  	} {
   217  		t.Run(name, func(t *testing.T) {
   218  			resolver := &resolver{
   219  				options: &ImportResolverOptions{
   220  					Debug:  false,
   221  					Printf: t.Logf,
   222  				},
   223  				known: tc.known,
   224  			}
   225  			got := resolver.Provided(tc.lang, tc.impLang)
   226  			if diff := cmp.Diff(tc.want, got, cmpopts.SortMaps(compareImportLabels)); diff != "" {
   227  				t.Errorf("Resolve() mismatch (-want +got):\n%s", diff)
   228  			}
   229  		})
   230  	}
   231  }
   232  
   233  func compareImportLabels(x, y importLabels) bool {
   234  	if len(x) != len(y) {
   235  		return len(x) < len(y)
   236  	}
   237  	a := importLabelEntries(x)
   238  	b := importLabelEntries(y)
   239  
   240  	for i := 0; i < len(a); i++ {
   241  		r := a[i]
   242  		s := b[i]
   243  		if r.key != s.key {
   244  			return r.key < s.key
   245  		}
   246  		for j := 0; j < len(r.values); j++ {
   247  			if r.values[j] != s.values[j] {
   248  				return r.values[j] < s.values[j]
   249  			}
   250  		}
   251  	}
   252  
   253  	return true
   254  }
   255  
   256  func importLabelEntries(x importLabels) []importLabelEntry {
   257  	var a []importLabelEntry
   258  	for k, v := range x {
   259  		deps := make([]string, len(v))
   260  		for i, d := range v {
   261  			deps[i] = d.String()
   262  		}
   263  		a = append(a, importLabelEntry{k, deps})
   264  	}
   265  	return a
   266  }
   267  
   268  type importLabelEntry struct {
   269  	key    string
   270  	values []string
   271  }