github.com/jd-ly/tools@v0.5.7/internal/lsp/cache/view_test.go (about)

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  package cache
     5  
     6  import (
     7  	"context"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"testing"
    13  
    14  	"golang.org/x/mod/modfile"
    15  	"github.com/jd-ly/tools/internal/lsp/fake"
    16  	"github.com/jd-ly/tools/internal/lsp/protocol"
    17  	"github.com/jd-ly/tools/internal/lsp/source"
    18  	"github.com/jd-ly/tools/internal/span"
    19  )
    20  
    21  func TestCaseInsensitiveFilesystem(t *testing.T) {
    22  	base, err := ioutil.TempDir("", t.Name())
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  
    27  	inner := filepath.Join(base, "a/B/c/DEFgh")
    28  	if err := os.MkdirAll(inner, 0777); err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	file := filepath.Join(inner, "f.go")
    32  	if err := ioutil.WriteFile(file, []byte("hi"), 0777); err != nil {
    33  		t.Fatal(err)
    34  	}
    35  	if _, err := os.Stat(filepath.Join(inner, "F.go")); err != nil {
    36  		t.Skip("filesystem is case-sensitive")
    37  	}
    38  
    39  	tests := []struct {
    40  		path string
    41  		err  bool
    42  	}{
    43  		{file, false},
    44  		{filepath.Join(inner, "F.go"), true},
    45  		{filepath.Join(base, "a/b/c/defgh/f.go"), true},
    46  	}
    47  	for _, tt := range tests {
    48  		err := checkPathCase(tt.path)
    49  		if err != nil != tt.err {
    50  			t.Errorf("checkPathCase(%q) = %v, wanted error: %v", tt.path, err, tt.err)
    51  		}
    52  	}
    53  }
    54  
    55  func TestFindWorkspaceRoot(t *testing.T) {
    56  	workspace := `
    57  -- a/go.mod --
    58  module a
    59  -- a/x/x.go
    60  package x
    61  -- b/go.mod --
    62  module b
    63  -- b/c/go.mod --
    64  module bc
    65  -- d/gopls.mod --
    66  module d-goplsworkspace
    67  -- d/e/go.mod --
    68  module de
    69  -- f/g/go.mod --
    70  module fg
    71  `
    72  	dir, err := fake.Tempdir(workspace)
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  	defer os.RemoveAll(dir)
    77  
    78  	tests := []struct {
    79  		folder, want string
    80  		experimental bool
    81  	}{
    82  		{"", "", false}, // no module at root, and more than one nested module
    83  		{"a", "a", false},
    84  		{"a/x", "a", false},
    85  		{"b/c", "b/c", false},
    86  		{"d", "d/e", false},
    87  		{"d", "d", true},
    88  		{"d/e", "d/e", false},
    89  		{"d/e", "d", true},
    90  		{"f", "f/g", false},
    91  		{"f", "f", true},
    92  	}
    93  
    94  	for _, test := range tests {
    95  		ctx := context.Background()
    96  		rel := fake.RelativeTo(dir)
    97  		folderURI := span.URIFromPath(rel.AbsPath(test.folder))
    98  		got, err := findWorkspaceRoot(ctx, folderURI, osFileSource{}, test.experimental)
    99  		if err != nil {
   100  			t.Fatal(err)
   101  		}
   102  		if gotf, wantf := filepath.Clean(got.Filename()), rel.AbsPath(test.want); gotf != wantf {
   103  			t.Errorf("findWorkspaceRoot(%q, %t) = %q, want %q", test.folder, test.experimental, gotf, wantf)
   104  		}
   105  	}
   106  }
   107  
   108  // This tests the logic used to extract positions from parse and other Go
   109  // command errors.
   110  func TestExtractPositionFromError(t *testing.T) {
   111  	workspace := `
   112  -- a/go.mod --
   113  modul a.com
   114  -- b/go.mod --
   115  module b.com
   116  
   117  go 1.12.hello
   118  -- c/go.mod --
   119  module c.com
   120  
   121  require a.com master
   122  `
   123  	dir, err := fake.Tempdir(workspace)
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	defer os.RemoveAll(dir)
   128  
   129  	tests := []struct {
   130  		filename string
   131  		wantRng  protocol.Range
   132  	}{
   133  		{
   134  			filename: "a/go.mod",
   135  			wantRng:  protocol.Range{},
   136  		},
   137  		{
   138  			filename: "b/go.mod",
   139  			wantRng: protocol.Range{
   140  				Start: protocol.Position{Line: 2},
   141  				End:   protocol.Position{Line: 2},
   142  			},
   143  		},
   144  		{
   145  			filename: "c/go.mod",
   146  			wantRng: protocol.Range{
   147  				Start: protocol.Position{Line: 2},
   148  				End:   protocol.Position{Line: 2},
   149  			},
   150  		},
   151  	}
   152  	for _, test := range tests {
   153  		ctx := context.Background()
   154  		rel := fake.RelativeTo(dir)
   155  		uri := span.URIFromPath(rel.AbsPath(test.filename))
   156  		if source.DetectLanguage("", uri.Filename()) != source.Mod {
   157  			t.Fatalf("expected only go.mod files")
   158  		}
   159  		// Try directly parsing the given, invalid go.mod file. Then, extract a
   160  		// position from the error message.
   161  		src := osFileSource{}
   162  		modFH, err := src.GetFile(ctx, uri)
   163  		if err != nil {
   164  			t.Fatal(err)
   165  		}
   166  		content, err := modFH.Read()
   167  		if err != nil {
   168  			t.Fatal(err)
   169  		}
   170  		_, parseErr := modfile.Parse(uri.Filename(), content, nil)
   171  		if parseErr == nil {
   172  			t.Fatalf("%s: expected an unparseable go.mod file", uri.Filename())
   173  		}
   174  		srcErr, err := extractErrorWithPosition(ctx, parseErr.Error(), src)
   175  		if err != nil {
   176  			t.Fatal(err)
   177  		}
   178  		if srcErr.URI != uri {
   179  			t.Errorf("unexpected URI: got %s, wanted %s", srcErr.URI, uri)
   180  		}
   181  		if protocol.CompareRange(test.wantRng, srcErr.Range) != 0 {
   182  			t.Errorf("unexpected range: got %s, wanted %s", srcErr.Range, test.wantRng)
   183  		}
   184  		if !strings.HasSuffix(parseErr.Error(), srcErr.Message) {
   185  			t.Errorf("unexpected message: got %s, wanted %s", srcErr.Message, parseErr)
   186  		}
   187  	}
   188  }
   189  
   190  func TestInVendor(t *testing.T) {
   191  	for _, tt := range []struct {
   192  		path     string
   193  		inVendor bool
   194  	}{
   195  		{
   196  			path:     "foo/vendor/x.go",
   197  			inVendor: false,
   198  		},
   199  		{
   200  			path:     "foo/vendor/x/x.go",
   201  			inVendor: true,
   202  		},
   203  		{
   204  			path:     "foo/x.go",
   205  			inVendor: false,
   206  		},
   207  	} {
   208  		if got := inVendor(span.URIFromPath(tt.path)); got != tt.inVendor {
   209  			t.Errorf("expected %s inVendor %v, got %v", tt.path, tt.inVendor, got)
   210  		}
   211  	}
   212  }