internal/lsp/fake: use hash rather than mtime to identify workdir files

On builders with low resolution clocks (e.g. WSL), it's possible for us
to miss file events that occur within a single 'tick'.

Fix this by instead tracking full file identity. Since we should have
only a few relatively small files in tests, the additional overhead
should be small.

For golang/go#43554

Change-Id: I05a48567f83007ff2278145638547c6ebb2523fd
Reviewed-on: https://go-review.googlesource.com/c/tools/+/283052
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Robert Findley <rfindley@google.com>
This commit is contained in:
Rob Findley 2021-01-11 09:37:09 -05:00 committed by Robert Findley
parent 45115c1c4d
commit 7646fae968
3 changed files with 32 additions and 17 deletions

View File

@ -146,8 +146,11 @@ func Tempdir(txt string) (string, error) {
if err != nil {
return "", err
}
if err := writeTxtar(txt, RelativeTo(dir)); err != nil {
return "", err
files := unpackTxt(txt)
for name, data := range files {
if err := WriteFileData(name, data, RelativeTo(dir)); err != nil {
return "", errors.Errorf("writing to tempdir: %w", err)
}
}
return dir, nil
}

View File

@ -7,12 +7,13 @@ package fake
import (
"bytes"
"context"
"crypto/sha256"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"time"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span"
@ -82,7 +83,7 @@ type Workdir struct {
watchers []func(context.Context, []FileEvent)
fileMu sync.Mutex
files map[string]time.Time
files map[string]string
}
// NewWorkdir writes the txtar-encoded file data in txt to dir, and returns a
@ -91,11 +92,18 @@ func NewWorkdir(dir string) *Workdir {
return &Workdir{RelativeTo: RelativeTo(dir)}
}
func hashFile(data []byte) string {
return fmt.Sprintf("%x", sha256.Sum256(data))
}
func (w *Workdir) writeInitialFiles(txt string) error {
writeTxtar(txt, w.RelativeTo)
// Poll to capture the current file state.
if _, err := w.pollFiles(); err != nil {
return errors.Errorf("polling files: %w", err)
files := unpackTxt(txt)
w.files = map[string]string{}
for name, data := range files {
w.files[name] = hashFile(data)
if err := WriteFileData(name, data, w.RelativeTo); err != nil {
return errors.Errorf("writing to workdir: %w", err)
}
}
return nil
}
@ -256,10 +264,10 @@ func (w *Workdir) writeFile(ctx context.Context, path, content string) (FileEven
}, nil
}
// ListFiles lists files in the given directory, returning a map of relative
// listFiles lists files in the given directory, returning a map of relative
// path to modification time.
func (w *Workdir) ListFiles(dir string) (map[string]time.Time, error) {
files := make(map[string]time.Time)
func (w *Workdir) listFiles(dir string) (map[string]string, error) {
files := make(map[string]string)
absDir := w.AbsPath(dir)
if err := filepath.Walk(absDir, func(fp string, info os.FileInfo, err error) error {
if err != nil {
@ -269,7 +277,11 @@ func (w *Workdir) ListFiles(dir string) (map[string]time.Time, error) {
return nil
}
path := w.RelPath(fp)
files[path] = info.ModTime()
data, err := ioutil.ReadFile(fp)
if err != nil {
return err
}
files[path] = hashFile(data)
return nil
}); err != nil {
return nil, err
@ -294,20 +306,20 @@ func (w *Workdir) pollFiles() ([]FileEvent, error) {
w.fileMu.Lock()
defer w.fileMu.Unlock()
files, err := w.ListFiles(".")
files, err := w.listFiles(".")
if err != nil {
return nil, err
}
var evts []FileEvent
// Check which files have been added or modified.
for path, mtime := range files {
oldmtime, ok := w.files[path]
for path, hash := range files {
oldhash, ok := w.files[path]
delete(w.files, path)
var typ protocol.FileChangeType
switch {
case !ok:
typ = protocol.Created
case oldmtime != mtime:
case oldhash != hash:
typ = protocol.Changed
default:
continue

View File

@ -104,7 +104,7 @@ func TestWorkdir_ListFiles(t *testing.T) {
defer cleanup()
checkFiles := func(dir string, want []string) {
files, err := wd.ListFiles(dir)
files, err := wd.listFiles(dir)
if err != nil {
t.Fatal(err)
}