.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / internal / lsp / cache / workspace_test.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/internal/lsp/cache/workspace_test.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/internal/lsp/cache/workspace_test.go
new file mode 100644 (file)
index 0000000..fd9cb8d
--- /dev/null
@@ -0,0 +1,355 @@
+// Copyright 2020 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 cache
+
+import (
+       "context"
+       "os"
+       "strings"
+       "testing"
+
+       "golang.org/x/tools/internal/lsp/fake"
+       "golang.org/x/tools/internal/lsp/source"
+       "golang.org/x/tools/internal/span"
+)
+
+// osFileSource is a fileSource that just reads from the operating system.
+type osFileSource struct {
+       overlays map[span.URI]fakeOverlay
+}
+
+type fakeOverlay struct {
+       source.VersionedFileHandle
+       uri     span.URI
+       content string
+       err     error
+       saved   bool
+}
+
+func (o fakeOverlay) Saved() bool { return o.saved }
+
+func (o fakeOverlay) Read() ([]byte, error) {
+       if o.err != nil {
+               return nil, o.err
+       }
+       return []byte(o.content), nil
+}
+
+func (o fakeOverlay) URI() span.URI {
+       return o.uri
+}
+
+// change updates the file source with the given file content. For convenience,
+// empty content signals a deletion. If saved is true, these changes are
+// persisted to disk.
+func (s *osFileSource) change(ctx context.Context, uri span.URI, content string, saved bool) (*fileChange, error) {
+       if content == "" {
+               delete(s.overlays, uri)
+               if saved {
+                       if err := os.Remove(uri.Filename()); err != nil {
+                               return nil, err
+                       }
+               }
+               fh, err := s.GetFile(ctx, uri)
+               if err != nil {
+                       return nil, err
+               }
+               data, err := fh.Read()
+               return &fileChange{exists: err == nil, content: data, fileHandle: &closedFile{fh}}, nil
+       }
+       if s.overlays == nil {
+               s.overlays = map[span.URI]fakeOverlay{}
+       }
+       s.overlays[uri] = fakeOverlay{uri: uri, content: content, saved: saved}
+       return &fileChange{
+               exists:     content != "",
+               content:    []byte(content),
+               fileHandle: s.overlays[uri],
+       }, nil
+}
+
+func (s *osFileSource) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
+       if overlay, ok := s.overlays[uri]; ok {
+               return overlay, nil
+       }
+       fi, statErr := os.Stat(uri.Filename())
+       if statErr != nil {
+               return &fileHandle{
+                       err: statErr,
+                       uri: uri,
+               }, nil
+       }
+       fh, err := readFile(ctx, uri, fi)
+       if err != nil {
+               return nil, err
+       }
+       return fh, nil
+}
+
+type wsState struct {
+       source  workspaceSource
+       modules []string
+       dirs    []string
+       sum     string
+}
+
+type wsChange struct {
+       content string
+       saved   bool
+}
+
+func TestWorkspaceModule(t *testing.T) {
+       tests := []struct {
+               desc         string
+               initial      string // txtar-encoded
+               legacyMode   bool
+               initialState wsState
+               updates      map[string]wsChange
+               wantChanged  bool
+               wantReload   bool
+               finalState   wsState
+       }{
+               {
+                       desc: "legacy mode",
+                       initial: `
+-- go.mod --
+module mod.com
+-- go.sum --
+golang.org/x/mod v0.3.0 h1:deadbeef
+-- a/go.mod --
+module moda.com`,
+                       legacyMode: true,
+                       initialState: wsState{
+                               modules: []string{"./go.mod"},
+                               source:  legacyWorkspace,
+                               dirs:    []string{"."},
+                               sum:     "golang.org/x/mod v0.3.0 h1:deadbeef\n",
+                       },
+               },
+               {
+                       desc: "nested module",
+                       initial: `
+-- go.mod --
+module mod.com
+-- a/go.mod --
+module moda.com`,
+                       initialState: wsState{
+                               modules: []string{"./go.mod", "a/go.mod"},
+                               source:  fileSystemWorkspace,
+                               dirs:    []string{".", "a"},
+                       },
+               },
+               {
+                       desc: "removing module",
+                       initial: `
+-- a/go.mod --
+module moda.com
+-- a/go.sum --
+golang.org/x/mod v0.3.0 h1:deadbeef
+-- b/go.mod --
+module modb.com
+-- b/go.sum --
+golang.org/x/mod v0.3.0 h1:beefdead`,
+                       initialState: wsState{
+                               modules: []string{"a/go.mod", "b/go.mod"},
+                               source:  fileSystemWorkspace,
+                               dirs:    []string{".", "a", "b"},
+                               sum:     "golang.org/x/mod v0.3.0 h1:beefdead\ngolang.org/x/mod v0.3.0 h1:deadbeef\n",
+                       },
+                       updates: map[string]wsChange{
+                               "gopls.mod": {`module gopls-workspace
+
+require moda.com v0.0.0-goplsworkspace
+replace moda.com => $SANDBOX_WORKDIR/a`, true},
+                       },
+                       wantChanged: true,
+                       wantReload:  true,
+                       finalState: wsState{
+                               modules: []string{"a/go.mod"},
+                               source:  goplsModWorkspace,
+                               dirs:    []string{".", "a"},
+                               sum:     "golang.org/x/mod v0.3.0 h1:deadbeef\n",
+                       },
+               },
+               {
+                       desc: "adding module",
+                       initial: `
+-- gopls.mod --
+require moda.com v0.0.0-goplsworkspace
+replace moda.com => $SANDBOX_WORKDIR/a
+-- a/go.mod --
+module moda.com
+-- b/go.mod --
+module modb.com`,
+                       initialState: wsState{
+                               modules: []string{"a/go.mod"},
+                               source:  goplsModWorkspace,
+                               dirs:    []string{".", "a"},
+                       },
+                       updates: map[string]wsChange{
+                               "gopls.mod": {`module gopls-workspace
+
+require moda.com v0.0.0-goplsworkspace
+require modb.com v0.0.0-goplsworkspace
+
+replace moda.com => $SANDBOX_WORKDIR/a
+replace modb.com => $SANDBOX_WORKDIR/b`, true},
+                       },
+                       wantChanged: true,
+                       wantReload:  true,
+                       finalState: wsState{
+                               modules: []string{"a/go.mod", "b/go.mod"},
+                               source:  goplsModWorkspace,
+                               dirs:    []string{".", "a", "b"},
+                       },
+               },
+               {
+                       desc: "deleting gopls.mod",
+                       initial: `
+-- gopls.mod --
+module gopls-workspace
+
+require moda.com v0.0.0-goplsworkspace
+replace moda.com => $SANDBOX_WORKDIR/a
+-- a/go.mod --
+module moda.com
+-- b/go.mod --
+module modb.com`,
+                       initialState: wsState{
+                               modules: []string{"a/go.mod"},
+                               source:  goplsModWorkspace,
+                               dirs:    []string{".", "a"},
+                       },
+                       updates: map[string]wsChange{
+                               "gopls.mod": {"", true},
+                       },
+                       wantChanged: true,
+                       wantReload:  true,
+                       finalState: wsState{
+                               modules: []string{"a/go.mod", "b/go.mod"},
+                               source:  fileSystemWorkspace,
+                               dirs:    []string{".", "a", "b"},
+                       },
+               },
+               {
+                       desc: "broken module parsing",
+                       initial: `
+-- a/go.mod --
+module moda.com
+
+require gopls.test v0.0.0-goplsworkspace
+replace gopls.test => ../../gopls.test // (this path shouldn't matter)
+-- b/go.mod --
+module modb.com`,
+                       initialState: wsState{
+                               modules: []string{"a/go.mod", "b/go.mod"},
+                               source:  fileSystemWorkspace,
+                               dirs:    []string{".", "a", "b", "../gopls.test"},
+                       },
+                       updates: map[string]wsChange{
+                               "a/go.mod": {`modul moda.com
+
+require gopls.test v0.0.0-goplsworkspace
+replace gopls.test => ../../gopls.test2`, false},
+                       },
+                       wantChanged: true,
+                       wantReload:  false,
+                       finalState: wsState{
+                               modules: []string{"a/go.mod", "b/go.mod"},
+                               source:  fileSystemWorkspace,
+                               // finalDirs should be unchanged: we should preserve dirs in the presence
+                               // of a broken modfile.
+                               dirs: []string{".", "a", "b", "../gopls.test"},
+                       },
+               },
+       }
+
+       for _, test := range tests {
+               t.Run(test.desc, func(t *testing.T) {
+                       ctx := context.Background()
+                       dir, err := fake.Tempdir(test.initial)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       defer os.RemoveAll(dir)
+                       root := span.URIFromPath(dir)
+
+                       fs := &osFileSource{}
+                       excludeNothing := func(string) bool { return false }
+                       w, err := newWorkspace(ctx, root, fs, excludeNothing, false, !test.legacyMode)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       rel := fake.RelativeTo(dir)
+                       checkState(ctx, t, fs, rel, w, test.initialState)
+
+                       // Apply updates.
+                       if test.updates != nil {
+                               changes := make(map[span.URI]*fileChange)
+                               for k, v := range test.updates {
+                                       content := strings.ReplaceAll(v.content, "$SANDBOX_WORKDIR", string(rel))
+                                       uri := span.URIFromPath(rel.AbsPath(k))
+                                       changes[uri], err = fs.change(ctx, uri, content, v.saved)
+                                       if err != nil {
+                                               t.Fatal(err)
+                                       }
+                               }
+                               got, gotChanged, gotReload := w.invalidate(ctx, changes)
+                               if gotChanged != test.wantChanged {
+                                       t.Errorf("w.invalidate(): got changed %t, want %t", gotChanged, test.wantChanged)
+                               }
+                               if gotReload != test.wantReload {
+                                       t.Errorf("w.invalidate(): got reload %t, want %t", gotReload, test.wantReload)
+                               }
+                               checkState(ctx, t, fs, rel, got, test.finalState)
+                       }
+               })
+       }
+}
+
+func checkState(ctx context.Context, t *testing.T, fs source.FileSource, rel fake.RelativeTo, got *workspace, want wsState) {
+       t.Helper()
+       if got.moduleSource != want.source {
+               t.Errorf("module source = %v, want %v", got.moduleSource, want.source)
+       }
+       modules := make(map[span.URI]struct{})
+       for k := range got.getActiveModFiles() {
+               modules[k] = struct{}{}
+       }
+       for _, modPath := range want.modules {
+               path := rel.AbsPath(modPath)
+               uri := span.URIFromPath(path)
+               if _, ok := modules[uri]; !ok {
+                       t.Errorf("missing module %q", uri)
+               }
+               delete(modules, uri)
+       }
+       for remaining := range modules {
+               t.Errorf("unexpected module %q", remaining)
+       }
+       gotDirs := got.dirs(ctx, fs)
+       gotM := make(map[span.URI]bool)
+       for _, dir := range gotDirs {
+               gotM[dir] = true
+       }
+       for _, dir := range want.dirs {
+               path := rel.AbsPath(dir)
+               uri := span.URIFromPath(path)
+               if !gotM[uri] {
+                       t.Errorf("missing dir %q", uri)
+               }
+               delete(gotM, uri)
+       }
+       for remaining := range gotM {
+               t.Errorf("unexpected dir %q", remaining)
+       }
+       gotSumBytes, err := got.sumFile(ctx, fs)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if gotSum := string(gotSumBytes); gotSum != want.sum {
+               t.Errorf("got final sum %q, want %q", gotSum, want.sum)
+       }
+}