github.com/afking/bazel-gazelle@v0.0.0-20180301150245-c02bc0f529e8/internal/resolve/resolve_test.go (about)

     1  /* Copyright 2016 The Bazel Authors. All rights reserved.
     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     http://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  
    16  package resolve
    17  
    18  import (
    19  	"path"
    20  	"path/filepath"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/bazelbuild/bazel-gazelle/internal/config"
    26  	"github.com/bazelbuild/bazel-gazelle/internal/label"
    27  	bf "github.com/bazelbuild/buildtools/build"
    28  )
    29  
    30  func TestResolveGoIndex(t *testing.T) {
    31  	c := &config.Config{
    32  		GoPrefix: "example.com/repo",
    33  		DepMode:  config.VendorMode,
    34  	}
    35  	l := label.NewLabeler(c)
    36  
    37  	type fileSpec struct {
    38  		rel, content string
    39  	}
    40  	type testCase struct {
    41  		desc       string
    42  		buildFiles []fileSpec
    43  		imp        string
    44  		from       label.Label
    45  		wantErr    string
    46  		want       label.Label
    47  	}
    48  	for _, tc := range []testCase{
    49  		{
    50  			desc: "no_match",
    51  			imp:  "example.com/foo",
    52  			// fall back to external resolver
    53  			want: label.New("", "vendor/example.com/foo", config.DefaultLibName),
    54  		}, {
    55  			desc: "simple",
    56  			buildFiles: []fileSpec{{
    57  				rel: "foo",
    58  				content: `
    59  go_library(
    60      name = "go_default_library",
    61      importpath = "example.com/foo",
    62  )
    63  `}},
    64  			imp:  "example.com/foo",
    65  			want: label.New("", "foo", "go_default_library"),
    66  		}, {
    67  			desc: "test_and_library_not_indexed",
    68  			buildFiles: []fileSpec{{
    69  				rel: "foo",
    70  				content: `
    71  go_test(
    72      name = "go_default_test",
    73      importpath = "example.com/foo",
    74  )
    75  
    76  go_binary(
    77      name = "cmd",
    78      importpath = "example.com/foo",
    79  )
    80  `,
    81  			}},
    82  			imp: "example.com/foo",
    83  			// fall back to external resolver
    84  			want: label.New("", "vendor/example.com/foo", config.DefaultLibName),
    85  		}, {
    86  			desc: "multiple_rules_ambiguous",
    87  			buildFiles: []fileSpec{{
    88  				rel: "foo",
    89  				content: `
    90  go_library(
    91      name = "a",
    92      importpath = "example.com/foo",
    93  )
    94  
    95  go_library(
    96      name = "b",
    97      importpath = "example.com/foo",
    98  )
    99  `,
   100  			}},
   101  			imp:     "example.com/foo",
   102  			wantErr: "multiple rules",
   103  		}, {
   104  			desc: "vendor_not_visible",
   105  			buildFiles: []fileSpec{
   106  				{
   107  					rel: "",
   108  					content: `
   109  go_library(
   110      name = "root",
   111      importpath = "example.com/foo",
   112  )
   113  `,
   114  				}, {
   115  					rel: "a/vendor/foo",
   116  					content: `
   117  go_library(
   118      name = "vendored",
   119      importpath = "example.com/foo",
   120  )
   121  `,
   122  				},
   123  			},
   124  			imp:  "example.com/foo",
   125  			from: label.New("", "b", "b"),
   126  			want: label.New("", "", "root"),
   127  		}, {
   128  			desc: "vendor_supercedes_nonvendor",
   129  			buildFiles: []fileSpec{
   130  				{
   131  					rel: "",
   132  					content: `
   133  go_library(
   134      name = "root",
   135      importpath = "example.com/foo",
   136  )
   137  `,
   138  				}, {
   139  					rel: "vendor/foo",
   140  					content: `
   141  go_library(
   142      name = "vendored",
   143      importpath = "example.com/foo",
   144  )
   145  `,
   146  				},
   147  			},
   148  			imp:  "example.com/foo",
   149  			from: label.New("", "sub", "sub"),
   150  			want: label.New("", "vendor/foo", "vendored"),
   151  		}, {
   152  			desc: "deep_vendor_shallow_vendor",
   153  			buildFiles: []fileSpec{
   154  				{
   155  					rel: "shallow/vendor",
   156  					content: `
   157  go_library(
   158      name = "shallow",
   159      importpath = "example.com/foo",
   160  )
   161  `,
   162  				}, {
   163  					rel: "shallow/deep/vendor",
   164  					content: `
   165  go_library(
   166      name = "deep",
   167      importpath = "example.com/foo",
   168  )
   169  `,
   170  				},
   171  			},
   172  			imp:  "example.com/foo",
   173  			from: label.New("", "shallow/deep", "deep"),
   174  			want: label.New("", "shallow/deep/vendor", "deep"),
   175  		}, {
   176  			desc: "nested_vendor",
   177  			buildFiles: []fileSpec{
   178  				{
   179  					rel: "vendor/a",
   180  					content: `
   181  go_library(
   182      name = "a",
   183      importpath = "a",
   184  )
   185  `,
   186  				}, {
   187  					rel: "vendor/b/vendor/a",
   188  					content: `
   189  go_library(
   190      name = "a",
   191      importpath = "a",
   192  )
   193  `,
   194  				},
   195  			},
   196  			imp:  "a",
   197  			from: label.New("", "vendor/b/c", "c"),
   198  			want: label.New("", "vendor/b/vendor/a", "a"),
   199  		},
   200  	} {
   201  		t.Run(tc.desc, func(t *testing.T) {
   202  			ix := NewRuleIndex()
   203  			for _, fs := range tc.buildFiles {
   204  				f, err := bf.Parse(path.Join(fs.rel, "BUILD.bazel"), []byte(fs.content))
   205  				if err != nil {
   206  					t.Fatal(err)
   207  				}
   208  				ix.AddRulesFromFile(c, f)
   209  			}
   210  
   211  			ix.Finish()
   212  
   213  			r := NewResolver(c, l, ix, nil)
   214  			got, err := r.resolveGo(tc.imp, tc.from)
   215  			if err != nil {
   216  				if tc.wantErr == "" {
   217  					t.Fatal(err)
   218  				}
   219  				if !strings.Contains(err.Error(), tc.wantErr) {
   220  					t.Fatalf("got %q ; want %q", err.Error(), tc.wantErr)
   221  				}
   222  				return
   223  			}
   224  			if err == nil && tc.wantErr != "" {
   225  				t.Fatalf("got success ; want error %q", tc.wantErr)
   226  			}
   227  			if !reflect.DeepEqual(got, tc.want) {
   228  				t.Fatalf("got %v ; want %v", got, tc.want)
   229  			}
   230  		})
   231  	}
   232  }
   233  
   234  func TestResolveProtoIndex(t *testing.T) {
   235  	c := &config.Config{
   236  		GoPrefix: "example.com/repo",
   237  		DepMode:  config.VendorMode,
   238  	}
   239  	l := label.NewLabeler(c)
   240  
   241  	buildContent := []byte(`
   242  proto_library(
   243      name = "foo_proto",
   244      srcs = ["bar.proto"],
   245  )
   246  
   247  go_proto_library(
   248      name = "foo_go_proto",
   249      importpath = "example.com/foo",
   250      proto = ":foo_proto",
   251  )
   252  
   253  go_library(
   254      name = "embed",
   255      embed = [":foo_go_proto"],
   256      importpath = "example.com/foo",
   257  )
   258  `)
   259  	f, err := bf.Parse(filepath.Join("sub", "BUILD.bazel"), buildContent)
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  
   264  	ix := NewRuleIndex()
   265  	ix.AddRulesFromFile(c, f)
   266  	ix.Finish()
   267  	r := NewResolver(c, l, ix, nil)
   268  
   269  	wantProto := label.New("", "sub", "foo_proto")
   270  	if got, err := r.resolveProto("sub/bar.proto", label.New("", "baz", "baz")); err != nil {
   271  		t.Error(err)
   272  	} else if !reflect.DeepEqual(got, wantProto) {
   273  		t.Errorf("resolveProto: got %s ; want %s", got, wantProto)
   274  	}
   275  	_, err = r.resolveProto("sub/bar.proto", label.New("", "sub", "foo_proto"))
   276  	if _, ok := err.(selfImportError); !ok {
   277  		t.Errorf("resolveProto: got %v ; want selfImportError", err)
   278  	}
   279  
   280  	wantGoProto := label.New("", "sub", "embed")
   281  	if got, err := r.resolveGoProto("sub/bar.proto", label.New("", "baz", "baz")); err != nil {
   282  		t.Error(err)
   283  	} else if !reflect.DeepEqual(got, wantGoProto) {
   284  		t.Errorf("resolveGoProto: got %s ; want %s", got, wantGoProto)
   285  	}
   286  	_, err = r.resolveGoProto("sub/bar.proto", label.New("", "sub", "foo_go_proto"))
   287  	if _, ok := err.(selfImportError); !ok {
   288  		t.Errorf("resolveGoProto: got %v ; want selfImportError", err)
   289  	}
   290  }
   291  
   292  func TestResolveGoLocal(t *testing.T) {
   293  	for _, spec := range []struct {
   294  		importpath string
   295  		from, want label.Label
   296  	}{
   297  		{
   298  			importpath: "example.com/repo",
   299  			want:       label.New("", "", config.DefaultLibName),
   300  		}, {
   301  			importpath: "example.com/repo/lib",
   302  			want:       label.New("", "lib", config.DefaultLibName),
   303  		}, {
   304  			importpath: "example.com/repo/another",
   305  			want:       label.New("", "another", config.DefaultLibName),
   306  		}, {
   307  			importpath: "example.com/repo",
   308  			want:       label.New("", "", config.DefaultLibName),
   309  		}, {
   310  			importpath: "example.com/repo/lib/sub",
   311  			want:       label.New("", "lib/sub", config.DefaultLibName),
   312  		}, {
   313  			importpath: "example.com/repo/another",
   314  			want:       label.New("", "another", config.DefaultLibName),
   315  		}, {
   316  			importpath: "../y",
   317  			from:       label.New("", "x", "x"),
   318  			want:       label.New("", "y", config.DefaultLibName),
   319  		},
   320  	} {
   321  		c := &config.Config{GoPrefix: "example.com/repo"}
   322  		l := label.NewLabeler(c)
   323  		ix := NewRuleIndex()
   324  		r := NewResolver(c, l, ix, nil)
   325  		label, err := r.resolveGo(spec.importpath, spec.from)
   326  		if err != nil {
   327  			t.Errorf("r.resolveGo(%q) failed with %v; want success", spec.importpath, err)
   328  			continue
   329  		}
   330  		if got, want := label, spec.want; !reflect.DeepEqual(got, want) {
   331  			t.Errorf("r.resolveGo(%q) = %s; want %s", spec.importpath, got, want)
   332  		}
   333  	}
   334  }
   335  
   336  func TestResolveGoLocalError(t *testing.T) {
   337  	c := &config.Config{GoPrefix: "example.com/repo"}
   338  	l := label.NewLabeler(c)
   339  	ix := NewRuleIndex()
   340  	rc := newStubRemoteCache(nil)
   341  	r := NewResolver(c, l, ix, rc)
   342  
   343  	for _, importpath := range []string{
   344  		"fmt",
   345  		"unknown.com/another",
   346  		"unknown.com/another/sub",
   347  		"unknown.com/repo_suffix",
   348  	} {
   349  		if l, err := r.resolveGo(importpath, label.NoLabel); err == nil {
   350  			t.Errorf("r.resolveGo(%q) = %s; want error", importpath, l)
   351  		}
   352  	}
   353  
   354  	if l, err := r.resolveGo("..", label.NoLabel); err == nil {
   355  		t.Errorf("r.resolveGo(%q) = %s; want error", "..", l)
   356  	}
   357  }
   358  
   359  func TestResolveGoEmptyPrefix(t *testing.T) {
   360  	c := &config.Config{}
   361  	l := label.NewLabeler(c)
   362  	ix := NewRuleIndex()
   363  	r := NewResolver(c, l, ix, nil)
   364  
   365  	imp := "foo"
   366  	want := label.New("", "foo", config.DefaultLibName)
   367  	if got, err := r.resolveGo(imp, label.NoLabel); err != nil {
   368  		t.Errorf("r.resolveGo(%q) failed with %v; want success", imp, err)
   369  	} else if !reflect.DeepEqual(got, want) {
   370  		t.Errorf("r.resolveGo(%q) = %s; want %s", imp, got, want)
   371  	}
   372  
   373  	imp = "fmt"
   374  	if _, err := r.resolveGo(imp, label.NoLabel); err == nil {
   375  		t.Errorf("r.resolveGo(%q) succeeded; want failure")
   376  	}
   377  }
   378  
   379  func TestResolveProto(t *testing.T) {
   380  	prefix := "example.com/repo"
   381  	for _, tc := range []struct {
   382  		desc, imp              string
   383  		from                   label.Label
   384  		depMode                config.DependencyMode
   385  		wantProto, wantGoProto label.Label
   386  	}{
   387  		{
   388  			desc:        "root",
   389  			imp:         "foo.proto",
   390  			wantProto:   label.New("", "", "repo_proto"),
   391  			wantGoProto: label.New("", "", config.DefaultLibName),
   392  		}, {
   393  			desc:        "sub",
   394  			imp:         "foo/bar/bar.proto",
   395  			wantProto:   label.New("", "foo/bar", "bar_proto"),
   396  			wantGoProto: label.New("", "foo/bar", config.DefaultLibName),
   397  		}, {
   398  			desc:        "vendor",
   399  			depMode:     config.VendorMode,
   400  			imp:         "foo/bar/bar.proto",
   401  			from:        label.New("", "vendor", ""),
   402  			wantProto:   label.New("", "foo/bar", "bar_proto"),
   403  			wantGoProto: label.New("", "vendor/foo/bar", config.DefaultLibName),
   404  		}, {
   405  			desc:        "well known",
   406  			imp:         "google/protobuf/any.proto",
   407  			wantProto:   label.New("com_google_protobuf", "", "any_proto"),
   408  			wantGoProto: label.NoLabel,
   409  		}, {
   410  			desc:        "well known vendor",
   411  			depMode:     config.VendorMode,
   412  			imp:         "google/protobuf/any.proto",
   413  			wantProto:   label.New("com_google_protobuf", "", "any_proto"),
   414  			wantGoProto: label.NoLabel,
   415  		}, {
   416  			desc:        "descriptor",
   417  			imp:         "google/protobuf/descriptor.proto",
   418  			wantProto:   label.New("com_google_protobuf", "", "descriptor_proto"),
   419  			wantGoProto: label.NoLabel,
   420  		}, {
   421  			desc:        "descriptor vendor",
   422  			depMode:     config.VendorMode,
   423  			imp:         "google/protobuf/descriptor.proto",
   424  			wantProto:   label.New("com_google_protobuf", "", "descriptor_proto"),
   425  			wantGoProto: label.NoLabel,
   426  		},
   427  	} {
   428  		t.Run(tc.desc, func(t *testing.T) {
   429  			c := &config.Config{
   430  				GoPrefix: prefix,
   431  				DepMode:  tc.depMode,
   432  			}
   433  			l := label.NewLabeler(c)
   434  			ix := NewRuleIndex()
   435  			r := NewResolver(c, l, ix, nil)
   436  
   437  			got, err := r.resolveProto(tc.imp, tc.from)
   438  			if err != nil {
   439  				t.Errorf("resolveProto: got error %v; want success", err)
   440  			}
   441  			if !reflect.DeepEqual(got, tc.wantProto) {
   442  				t.Errorf("resolveProto: got %s; want %s", got, tc.wantProto)
   443  			}
   444  
   445  			got, err = r.resolveGoProto(tc.imp, tc.from)
   446  			if err != nil {
   447  				if tc.wantGoProto != label.NoLabel {
   448  					t.Errorf("resolveGoProto: got error %v; want %s", got, tc.wantGoProto)
   449  				} else if _, ok := err.(standardImportError); !ok {
   450  					t.Errorf("resolveGoProto: got error %v; want standardImportError", err)
   451  				}
   452  			}
   453  			if !got.Equal(tc.wantGoProto) {
   454  				t.Errorf("resolveGoProto: got %s; want %s", got, tc.wantGoProto)
   455  			}
   456  		})
   457  	}
   458  }