diff --git a/internal/api/api.go b/internal/api/api.go index d03df66cd6..95e38fda6e 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -115,6 +115,11 @@ func (api *API) Log(s string) { api.options.Logger.Info(s) } +// Log implements ProjectHost. +func (api *API) Trace(s string) { + api.options.Logger.Info(s) +} + // NewLine implements ProjectHost. func (api *API) NewLine() string { return api.host.NewLine() diff --git a/internal/project/project.go b/internal/project/project.go index 2d041dda3e..dfd3d461e9 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -15,9 +15,11 @@ import ( "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/ls" "github.com/microsoft/typescript-go/internal/lsp/lsproto" + "github.com/microsoft/typescript-go/internal/module" "github.com/microsoft/typescript-go/internal/tsoptions" "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" + "github.com/microsoft/typescript-go/internal/vfs/cachedvfs" ) //go:generate go tool golang.org/x/tools/cmd/stringer -type=Kind -output=project_stringer_generated.go @@ -72,6 +74,7 @@ const ( type ProjectHost interface { tsoptions.ParseConfigHost + module.ResolutionHost NewLine() string DefaultLibraryPath() string TypingsInstaller() *TypingsInstaller @@ -120,7 +123,7 @@ func typeAcquisitionChanged(opt1 *core.TypeAcquisition, opt2 *core.TypeAcquisiti var _ compiler.CompilerHost = (*Project)(nil) type Project struct { - host ProjectHost + host *projectHostWithCachedFS name string kind Kind @@ -186,9 +189,11 @@ func NewInferredProject(compilerOptions *core.CompilerOptions, currentDirectory } func NewProject(name string, kind Kind, currentDirectory string, host ProjectHost) *Project { + cachedHost := newProjectHostWithCachedFS(host) + host.Log(fmt.Sprintf("Creating %sProject: %s, currentDirectory: %s", kind.String(), name, currentDirectory)) project := &Project{ - host: host, + host: cachedHost, name: name, kind: kind, currentDirectory: currentDirectory, @@ -196,11 +201,11 @@ func NewProject(name string, kind Kind, currentDirectory string, host ProjectHos } project.comparePathsOptions = tspath.ComparePathsOptions{ CurrentDirectory: currentDirectory, - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), + UseCaseSensitiveFileNames: project.host.FS().UseCaseSensitiveFileNames(), } - client := host.Client() - if host.IsWatchEnabled() && client != nil { - globMapper := createResolutionLookupGlobMapper(host) + client := project.host.Client() + if project.host.IsWatchEnabled() && client != nil { + globMapper := createResolutionLookupGlobMapper(project.host) project.failedLookupsWatch = newWatchedFiles(project, lsproto.WatchKindCreate, globMapper, "failed lookup") project.affectingLocationsWatch = newWatchedFiles(project, lsproto.WatchKindChange|lsproto.WatchKindCreate|lsproto.WatchKindDelete, globMapper, "affecting location") project.typingsFilesWatch = newWatchedFiles(project, lsproto.WatchKindChange|lsproto.WatchKindCreate|lsproto.WatchKindDelete, globMapperForTypingsInstaller, "typings installer files") @@ -210,6 +215,24 @@ func NewProject(name string, kind Kind, currentDirectory string, host ProjectHos return project } +type projectHostWithCachedFS struct { + ProjectHost + fs *cachedvfs.FS +} + +func newProjectHostWithCachedFS(host ProjectHost) *projectHostWithCachedFS { + newHost := &projectHostWithCachedFS{ + ProjectHost: host, + fs: cachedvfs.From(host.FS()), + } + newHost.fs.DisableAndClearCache() + return newHost +} + +func (p *projectHostWithCachedFS) FS() vfs.FS { + return p.fs +} + // FS implements compiler.CompilerHost. func (p *Project) FS() vfs.FS { return p.host.FS() @@ -263,7 +286,7 @@ func (p *Project) NewLine() string { // Trace implements compiler.CompilerHost. func (p *Project) Trace(msg string) { - p.Log(msg) + p.host.Log(msg) } // GetDefaultLibraryPath implements compiler.CompilerHost. @@ -461,6 +484,9 @@ func (p *Project) updateGraph() bool { return false } + p.host.fs.Enable() + defer p.host.fs.DisableAndClearCache() + start := time.Now() p.Log("Starting updateGraph: Project: " + p.name) var writeFileNames bool diff --git a/internal/project/service.go b/internal/project/service.go index 3ec6f02f94..c8190faab0 100644 --- a/internal/project/service.go +++ b/internal/project/service.go @@ -108,6 +108,10 @@ func (s *Service) Log(msg string) { s.options.Logger.Info(msg) } +func (s *Service) Trace(msg string) { + s.Log(msg) +} + func (s *Service) HasLevel(level LogLevel) bool { return s.options.Logger.HasLevel(level) } diff --git a/internal/project/service_test.go b/internal/project/service_test.go index 20a6a3e357..ae8ec4d525 100644 --- a/internal/project/service_test.go +++ b/internal/project/service_test.go @@ -168,6 +168,8 @@ func TestService(t *testing.T) { service, _ := projecttestutil.Setup(files, nil) service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") assert.Check(t, service.GetScriptInfo("/home/projects/TS/p1/y.ts") == nil) + // Avoid using initial file set after this point + files = nil //nolint:ineffassign err := service.ChangeFile( lsproto.VersionedTextDocumentIdentifier{ @@ -214,6 +216,8 @@ func TestService(t *testing.T) { _, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts") programBefore := project.GetProgram() assert.Equal(t, len(programBefore.GetSourceFiles()), 2) + // Avoid using initial file set after this point + files = nil //nolint:ineffassign err := service.ChangeFile( lsproto.VersionedTextDocumentIdentifier{ @@ -242,15 +246,16 @@ func TestService(t *testing.T) { ) assert.NilError(t, err) - files["/home/projects/TS/p1/tsconfig.json"] = `{ + err = host.FS().WriteFile("/home/projects/TS/p1/tsconfig.json", `{ "compilerOptions": { "noLib": true, "module": "nodenext", "strict": true, }, "include": ["./**/*"] - }` - host.ReplaceFS(files) + }`, false) + assert.NilError(t, err) + err = service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeChanged, @@ -270,23 +275,25 @@ func TestService(t *testing.T) { t.Parallel() t.Run("delete a file, close it, recreate it", func(t *testing.T) { t.Parallel() - service, host := projecttestutil.Setup(defaultFiles, nil) - service.OpenFile("/home/projects/TS/p1/src/x.ts", defaultFiles["/home/projects/TS/p1/src/x.ts"].(string), core.ScriptKindTS, "") - service.OpenFile("/home/projects/TS/p1/src/index.ts", defaultFiles["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") + files := maps.Clone(defaultFiles) + service, host := projecttestutil.Setup(files, nil) + service.OpenFile("/home/projects/TS/p1/src/x.ts", files["/home/projects/TS/p1/src/x.ts"].(string), core.ScriptKindTS, "") + service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") assert.Equal(t, service.SourceFileCount(), 2) + // Avoid using initial file set after this point + files = nil //nolint:ineffassign - files := maps.Clone(defaultFiles) - delete(files, "/home/projects/TS/p1/src/x.ts") - host.ReplaceFS(files) + assert.NilError(t, host.FS().Remove("/home/projects/TS/p1/src/x.ts")) service.CloseFile("/home/projects/TS/p1/src/x.ts") assert.Check(t, service.GetScriptInfo("/home/projects/TS/p1/src/x.ts") == nil) assert.Check(t, service.Projects()[0].GetProgram().GetSourceFile("/home/projects/TS/p1/src/x.ts") == nil) assert.Equal(t, service.SourceFileCount(), 1) - files["/home/projects/TS/p1/src/x.ts"] = `` - host.ReplaceFS(files) - service.OpenFile("/home/projects/TS/p1/src/x.ts", files["/home/projects/TS/p1/src/x.ts"].(string), core.ScriptKindTS, "") + err := host.FS().WriteFile("/home/projects/TS/p1/src/x.ts", "", false) + assert.NilError(t, err) + + service.OpenFile("/home/projects/TS/p1/src/x.ts", "", core.ScriptKindTS, "") assert.Equal(t, service.GetScriptInfo("/home/projects/TS/p1/src/x.ts").Text(), "") assert.Check(t, service.Projects()[0].GetProgram().GetSourceFile("/home/projects/TS/p1/src/x.ts") != nil) assert.Equal(t, service.Projects()[0].GetProgram().GetSourceFile("/home/projects/TS/p1/src/x.ts").Text(), "") @@ -300,19 +307,22 @@ func TestService(t *testing.T) { files := maps.Clone(defaultFiles) delete(files, "/home/projects/TS/p1/tsconfig.json") service, host := projecttestutil.Setup(files, nil) - service.OpenFile("/home/projects/TS/p1/src/x.ts", defaultFiles["/home/projects/TS/p1/src/x.ts"].(string), core.ScriptKindTS, "") - service.OpenFile("/home/projects/TS/p1/src/index.ts", defaultFiles["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") + service.OpenFile("/home/projects/TS/p1/src/x.ts", files["/home/projects/TS/p1/src/x.ts"].(string), core.ScriptKindTS, "") + service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") + // Avoid using initial file set after this point + files = nil //nolint:ineffassign - delete(files, "/home/projects/TS/p1/src/x.ts") - host.ReplaceFS(files) + err := host.FS().Remove("/home/projects/TS/p1/src/x.ts") + assert.NilError(t, err) service.CloseFile("/home/projects/TS/p1/src/x.ts") assert.Check(t, service.GetScriptInfo("/home/projects/TS/p1/src/x.ts") == nil) assert.Check(t, service.Projects()[0].GetProgram().GetSourceFile("/home/projects/TS/p1/src/x.ts") == nil) - files["/home/projects/TS/p1/src/x.ts"] = `` - host.ReplaceFS(files) - service.OpenFile("/home/projects/TS/p1/src/x.ts", files["/home/projects/TS/p1/src/x.ts"].(string), core.ScriptKindTS, "") + err = host.FS().WriteFile("/home/projects/TS/p1/src/x.ts", "", false) + assert.NilError(t, err) + + service.OpenFile("/home/projects/TS/p1/src/x.ts", "", core.ScriptKindTS, "") assert.Equal(t, service.GetScriptInfo("/home/projects/TS/p1/src/x.ts").Text(), "") assert.Check(t, service.Projects()[0].GetProgram().GetSourceFile("/home/projects/TS/p1/src/x.ts") != nil) assert.Equal(t, service.Projects()[0].GetProgram().GetSourceFile("/home/projects/TS/p1/src/x.ts").Text(), "") @@ -338,6 +348,8 @@ func TestService(t *testing.T) { service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") service.OpenFile("/home/projects/TS/p2/src/index.ts", files["/home/projects/TS/p2/src/index.ts"].(string), core.ScriptKindTS, "") assert.Equal(t, len(service.Projects()), 2) + // Avoid using initial file set after this point + files = nil //nolint:ineffassign _, p1 := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts") _, p2 := service.EnsureDefaultProjectForFile("/home/projects/TS/p2/src/index.ts") assert.Equal( @@ -361,6 +373,8 @@ func TestService(t *testing.T) { service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") service.OpenFile("/home/projects/TS/p2/src/index.ts", files["/home/projects/TS/p2/src/index.ts"].(string), core.ScriptKindTS, "") assert.Equal(t, len(service.Projects()), 2) + // Avoid using initial file set after this point + files = nil //nolint:ineffassign _, p1 := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts") _, p2 := service.EnsureDefaultProjectForFile("/home/projects/TS/p2/src/index.ts") x1 := p1.GetProgram().GetSourceFile("/home/projects/TS/p1/src/x.ts") @@ -375,15 +389,18 @@ func TestService(t *testing.T) { t.Run("change open file", func(t *testing.T) { t.Parallel() - service, host := projecttestutil.Setup(defaultFiles, nil) - service.OpenFile("/home/projects/TS/p1/src/x.ts", defaultFiles["/home/projects/TS/p1/src/x.ts"].(string), core.ScriptKindTS, "") - service.OpenFile("/home/projects/TS/p1/src/index.ts", defaultFiles["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") + files := maps.Clone(defaultFiles) + service, host := projecttestutil.Setup(files, nil) + service.OpenFile("/home/projects/TS/p1/src/x.ts", files["/home/projects/TS/p1/src/x.ts"].(string), core.ScriptKindTS, "") + service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") _, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts") programBefore := project.GetProgram() + // Avoid using initial file set after this point + files = nil //nolint:ineffassign + + err := host.FS().WriteFile("/home/projects/TS/p1/src/x.ts", `export const x = 2;`, false) + assert.NilError(t, err) - files := maps.Clone(defaultFiles) - files["/home/projects/TS/p1/src/x.ts"] = `export const x = 2;` - host.ReplaceFS(files) assert.NilError(t, service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeChanged, @@ -396,14 +413,17 @@ func TestService(t *testing.T) { t.Run("change closed program file", func(t *testing.T) { t.Parallel() - service, host := projecttestutil.Setup(defaultFiles, nil) - service.OpenFile("/home/projects/TS/p1/src/index.ts", defaultFiles["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") + files := maps.Clone(defaultFiles) + service, host := projecttestutil.Setup(files, nil) + service.OpenFile("/home/projects/TS/p1/src/index.ts", files["/home/projects/TS/p1/src/index.ts"].(string), core.ScriptKindTS, "") _, project := service.EnsureDefaultProjectForFile("/home/projects/TS/p1/src/index.ts") programBefore := project.GetProgram() + // Avoid using initial file set after this point + files = nil //nolint:ineffassign + + err := host.FS().WriteFile("/home/projects/TS/p1/src/x.ts", `export const x = 2;`, false) + assert.NilError(t, err) - files := maps.Clone(defaultFiles) - files["/home/projects/TS/p1/src/x.ts"] = `export const x = 2;` - host.ReplaceFS(files) assert.NilError(t, service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeChanged, @@ -435,14 +455,14 @@ func TestService(t *testing.T) { program := project.GetProgram() assert.Equal(t, len(program.GetSemanticDiagnostics(projecttestutil.WithRequestID(t.Context()), program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 0) - filesCopy := maps.Clone(files) - filesCopy["/home/projects/TS/p1/tsconfig.json"] = `{ + err := host.FS().WriteFile("/home/projects/TS/p1/tsconfig.json", `{ "compilerOptions": { "noLib": false, "strict": true } - }` - host.ReplaceFS(filesCopy) + }`, false) + assert.NilError(t, err) + assert.NilError(t, service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeChanged, @@ -472,9 +492,9 @@ func TestService(t *testing.T) { program := project.GetProgram() assert.Equal(t, len(program.GetSemanticDiagnostics(projecttestutil.WithRequestID(t.Context()), program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 0) - filesCopy := maps.Clone(files) - delete(filesCopy, "/home/projects/TS/p1/src/x.ts") - host.ReplaceFS(filesCopy) + err := host.FS().Remove("/home/projects/TS/p1/src/x.ts") + assert.NilError(t, err) + assert.NilError(t, service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeDeleted, @@ -505,9 +525,9 @@ func TestService(t *testing.T) { program := project.GetProgram() assert.Equal(t, len(program.GetSemanticDiagnostics(projecttestutil.WithRequestID(t.Context()), program.GetSourceFile("/home/projects/TS/p1/src/x.ts"))), 0) - filesCopy := maps.Clone(files) - delete(filesCopy, "/home/projects/TS/p1/src/index.ts") - host.ReplaceFS(filesCopy) + err := host.FS().Remove("/home/projects/TS/p1/src/index.ts") + assert.NilError(t, err) + assert.NilError(t, service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeDeleted, @@ -561,9 +581,9 @@ func TestService(t *testing.T) { }) // Add the missing file - filesCopy := maps.Clone(files) - filesCopy["/home/projects/TS/p1/src/y.ts"] = `export const y = 1;` - host.ReplaceFS(filesCopy) + err := host.FS().WriteFile("/home/projects/TS/p1/src/y.ts", `export const y = 1;`, false) + assert.NilError(t, err) + assert.NilError(t, service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeCreated, @@ -602,9 +622,9 @@ func TestService(t *testing.T) { })) // Add a new file through failed lookup watch - filesCopy := maps.Clone(files) - filesCopy["/home/projects/TS/p1/src/z.ts"] = `export const z = 1;` - host.ReplaceFS(filesCopy) + err := host.FS().WriteFile("/home/projects/TS/p1/src/z.ts", `export const z = 1;`, false) + assert.NilError(t, err) + assert.NilError(t, service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeCreated, @@ -638,9 +658,10 @@ func TestService(t *testing.T) { assert.Equal(t, len(program.GetSemanticDiagnostics(projecttestutil.WithRequestID(t.Context()), program.GetSourceFile("/home/projects/TS/p1/src/index.ts"))), 1) // Add a new file through wildcard watch - filesCopy := maps.Clone(files) - filesCopy["/home/projects/TS/p1/src/a.ts"] = `const a = 1;` - host.ReplaceFS(filesCopy) + + err := host.FS().WriteFile("/home/projects/TS/p1/src/a.ts", `const a = 1;`, false) + assert.NilError(t, err) + assert.NilError(t, service.OnWatchedFilesChanged(t.Context(), []*lsproto.FileEvent{ { Type: lsproto.FileChangeTypeCreated, diff --git a/internal/testutil/projecttestutil/projecttestutil.go b/internal/testutil/projecttestutil/projecttestutil.go index 6c5f8093ca..eb013b1992 100644 --- a/internal/testutil/projecttestutil/projecttestutil.go +++ b/internal/testutil/projecttestutil/projecttestutil.go @@ -81,10 +81,6 @@ func (p *ProjectServiceHost) Client() project.Client { return p.ClientMock } -func (p *ProjectServiceHost) ReplaceFS(files map[string]any) { - p.fs = bundled.WrapFS(vfstest.FromMap(files, false /*useCaseSensitiveFileNames*/)) -} - var _ project.ServiceHost = (*ProjectServiceHost)(nil) func Setup(files map[string]any, testOptions *TestTypingsInstaller) (*project.Service, *ProjectServiceHost) { diff --git a/internal/vfs/cachedvfs/cachedvfs.go b/internal/vfs/cachedvfs/cachedvfs.go index 132c67bfaf..0e284fe043 100644 --- a/internal/vfs/cachedvfs/cachedvfs.go +++ b/internal/vfs/cachedvfs/cachedvfs.go @@ -1,12 +1,15 @@ package cachedvfs import ( + "sync/atomic" + "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/vfs" ) type FS struct { - fs vfs.FS + fs vfs.FS + enabled atomic.Bool directoryExistsCache collections.SyncMap[string, bool] fileExistsCache collections.SyncMap[string, bool] @@ -18,7 +21,19 @@ type FS struct { var _ vfs.FS = (*FS)(nil) func From(fs vfs.FS) *FS { - return &FS{fs: fs} + fsys := &FS{fs: fs} + fsys.enabled.Store(true) + return fsys +} + +func (fsys *FS) DisableAndClearCache() { + if fsys.enabled.CompareAndSwap(true, false) { + fsys.ClearCache() + } +} + +func (fsys *FS) Enable() { + fsys.enabled.Store(true) } func (fsys *FS) ClearCache() { @@ -30,29 +45,50 @@ func (fsys *FS) ClearCache() { } func (fsys *FS) DirectoryExists(path string) bool { - if ret, ok := fsys.directoryExistsCache.Load(path); ok { - return ret + if fsys.enabled.Load() { + if ret, ok := fsys.directoryExistsCache.Load(path); ok { + return ret + } } + ret := fsys.fs.DirectoryExists(path) - fsys.directoryExistsCache.Store(path, ret) + + if fsys.enabled.Load() { + fsys.directoryExistsCache.Store(path, ret) + } + return ret } func (fsys *FS) FileExists(path string) bool { - if ret, ok := fsys.fileExistsCache.Load(path); ok { - return ret + if fsys.enabled.Load() { + if ret, ok := fsys.fileExistsCache.Load(path); ok { + return ret + } } + ret := fsys.fs.FileExists(path) - fsys.fileExistsCache.Store(path, ret) + + if fsys.enabled.Load() { + fsys.fileExistsCache.Store(path, ret) + } + return ret } func (fsys *FS) GetAccessibleEntries(path string) vfs.Entries { - if ret, ok := fsys.getAccessibleEntriesCache.Load(path); ok { - return ret + if fsys.enabled.Load() { + if ret, ok := fsys.getAccessibleEntriesCache.Load(path); ok { + return ret + } } + ret := fsys.fs.GetAccessibleEntries(path) - fsys.getAccessibleEntriesCache.Store(path, ret) + + if fsys.enabled.Load() { + fsys.getAccessibleEntriesCache.Store(path, ret) + } + return ret } @@ -61,11 +97,18 @@ func (fsys *FS) ReadFile(path string) (contents string, ok bool) { } func (fsys *FS) Realpath(path string) string { - if ret, ok := fsys.realpathCache.Load(path); ok { - return ret + if fsys.enabled.Load() { + if ret, ok := fsys.realpathCache.Load(path); ok { + return ret + } } + ret := fsys.fs.Realpath(path) - fsys.realpathCache.Store(path, ret) + + if fsys.enabled.Load() { + fsys.realpathCache.Store(path, ret) + } + return ret } @@ -74,11 +117,18 @@ func (fsys *FS) Remove(path string) error { } func (fsys *FS) Stat(path string) vfs.FileInfo { - if ret, ok := fsys.statCache.Load(path); ok { - return ret.(vfs.FileInfo) + if fsys.enabled.Load() { + if ret, ok := fsys.statCache.Load(path); ok { + return ret.(vfs.FileInfo) + } } + ret := fsys.fs.Stat(path) - fsys.statCache.Store(path, ret) + + if fsys.enabled.Load() { + fsys.statCache.Store(path, ret) + } + return ret } diff --git a/internal/vfs/cachedvfs/cachedvfs_test.go b/internal/vfs/cachedvfs/cachedvfs_test.go index f74a42c870..a63b4b359f 100644 --- a/internal/vfs/cachedvfs/cachedvfs_test.go +++ b/internal/vfs/cachedvfs/cachedvfs_test.go @@ -34,6 +34,20 @@ func TestDirectoryExists(t *testing.T) { cached.DirectoryExists("/other/path") assert.Equal(t, 3, len(underlying.DirectoryExistsCalls())) + + cached.DisableAndClearCache() + cached.DirectoryExists("/some/path") + assert.Equal(t, 4, len(underlying.DirectoryExistsCalls())) + + cached.DirectoryExists("/some/path") + assert.Equal(t, 5, len(underlying.DirectoryExistsCalls())) + + cached.Enable() + cached.DirectoryExists("/some/path") + assert.Equal(t, 6, len(underlying.DirectoryExistsCalls())) + + cached.DirectoryExists("/some/path") + assert.Equal(t, 6, len(underlying.DirectoryExistsCalls())) } func TestFileExists(t *testing.T) { @@ -54,6 +68,20 @@ func TestFileExists(t *testing.T) { cached.FileExists("/other/path/file.txt") assert.Equal(t, 3, len(underlying.FileExistsCalls())) + + cached.DisableAndClearCache() + cached.FileExists("/some/path/file.txt") + assert.Equal(t, 4, len(underlying.FileExistsCalls())) + + cached.FileExists("/some/path/file.txt") + assert.Equal(t, 5, len(underlying.FileExistsCalls())) + + cached.Enable() + cached.FileExists("/some/path/file.txt") + assert.Equal(t, 6, len(underlying.FileExistsCalls())) + + cached.FileExists("/some/path/file.txt") + assert.Equal(t, 6, len(underlying.FileExistsCalls())) } func TestGetAccessibleEntries(t *testing.T) { @@ -74,6 +102,20 @@ func TestGetAccessibleEntries(t *testing.T) { cached.GetAccessibleEntries("/other/path") assert.Equal(t, 3, len(underlying.GetAccessibleEntriesCalls())) + + cached.DisableAndClearCache() + cached.GetAccessibleEntries("/some/path") + assert.Equal(t, 4, len(underlying.GetAccessibleEntriesCalls())) + + cached.GetAccessibleEntries("/some/path") + assert.Equal(t, 5, len(underlying.GetAccessibleEntriesCalls())) + + cached.Enable() + cached.GetAccessibleEntries("/some/path") + assert.Equal(t, 6, len(underlying.GetAccessibleEntriesCalls())) + + cached.GetAccessibleEntries("/some/path") + assert.Equal(t, 6, len(underlying.GetAccessibleEntriesCalls())) } func TestRealpath(t *testing.T) { @@ -94,6 +136,20 @@ func TestRealpath(t *testing.T) { cached.Realpath("/other/path") assert.Equal(t, 3, len(underlying.RealpathCalls())) + + cached.DisableAndClearCache() + cached.Realpath("/some/path") + assert.Equal(t, 4, len(underlying.RealpathCalls())) + + cached.Realpath("/some/path") + assert.Equal(t, 5, len(underlying.RealpathCalls())) + + cached.Enable() + cached.Realpath("/some/path") + assert.Equal(t, 6, len(underlying.RealpathCalls())) + + cached.Realpath("/some/path") + assert.Equal(t, 6, len(underlying.RealpathCalls())) } func TestStat(t *testing.T) { @@ -114,6 +170,20 @@ func TestStat(t *testing.T) { cached.Stat("/other/path") assert.Equal(t, 3, len(underlying.StatCalls())) + + cached.DisableAndClearCache() + cached.Stat("/some/path") + assert.Equal(t, 4, len(underlying.StatCalls())) + + cached.Stat("/some/path") + assert.Equal(t, 5, len(underlying.StatCalls())) + + cached.Enable() + cached.Stat("/some/path") + assert.Equal(t, 6, len(underlying.StatCalls())) + + cached.Stat("/some/path") + assert.Equal(t, 6, len(underlying.StatCalls())) } func TestReadFile(t *testing.T) { @@ -131,6 +201,20 @@ func TestReadFile(t *testing.T) { cached.ClearCache() cached.ReadFile("/some/path/file.txt") assert.Equal(t, 3, len(underlying.ReadFileCalls())) + + cached.DisableAndClearCache() + cached.ReadFile("/some/path/file.txt") + assert.Equal(t, 4, len(underlying.ReadFileCalls())) + + cached.ReadFile("/some/path/file.txt") + assert.Equal(t, 5, len(underlying.ReadFileCalls())) + + cached.Enable() + cached.ReadFile("/some/path/file.txt") + assert.Equal(t, 6, len(underlying.ReadFileCalls())) + + cached.ReadFile("/some/path/file.txt") + assert.Equal(t, 7, len(underlying.ReadFileCalls())) } func TestUseCaseSensitiveFileNames(t *testing.T) { @@ -148,6 +232,20 @@ func TestUseCaseSensitiveFileNames(t *testing.T) { cached.ClearCache() cached.UseCaseSensitiveFileNames() assert.Equal(t, 3, len(underlying.UseCaseSensitiveFileNamesCalls())) + + cached.DisableAndClearCache() + cached.UseCaseSensitiveFileNames() + assert.Equal(t, 4, len(underlying.UseCaseSensitiveFileNamesCalls())) + + cached.UseCaseSensitiveFileNames() + assert.Equal(t, 5, len(underlying.UseCaseSensitiveFileNamesCalls())) + + cached.Enable() + cached.UseCaseSensitiveFileNames() + assert.Equal(t, 6, len(underlying.UseCaseSensitiveFileNamesCalls())) + + cached.UseCaseSensitiveFileNames() + assert.Equal(t, 7, len(underlying.UseCaseSensitiveFileNamesCalls())) } func TestWalkDir(t *testing.T) { @@ -169,6 +267,20 @@ func TestWalkDir(t *testing.T) { cached.ClearCache() _ = cached.WalkDir("/some/path", walkFn) assert.Equal(t, 3, len(underlying.WalkDirCalls())) + + cached.DisableAndClearCache() + _ = cached.WalkDir("/some/path", walkFn) + assert.Equal(t, 4, len(underlying.WalkDirCalls())) + + _ = cached.WalkDir("/some/path", walkFn) + assert.Equal(t, 5, len(underlying.WalkDirCalls())) + + cached.Enable() + _ = cached.WalkDir("/some/path", walkFn) + assert.Equal(t, 6, len(underlying.WalkDirCalls())) + + _ = cached.WalkDir("/some/path", walkFn) + assert.Equal(t, 7, len(underlying.WalkDirCalls())) } func TestRemove(t *testing.T) { @@ -186,6 +298,20 @@ func TestRemove(t *testing.T) { cached.ClearCache() _ = cached.Remove("/some/path/file.txt") assert.Equal(t, 3, len(underlying.RemoveCalls())) + + cached.DisableAndClearCache() + _ = cached.Remove("/some/path/file.txt") + assert.Equal(t, 4, len(underlying.RemoveCalls())) + + _ = cached.Remove("/some/path/file.txt") + assert.Equal(t, 5, len(underlying.RemoveCalls())) + + cached.Enable() + _ = cached.Remove("/some/path/file.txt") + assert.Equal(t, 6, len(underlying.RemoveCalls())) + + _ = cached.Remove("/some/path/file.txt") + assert.Equal(t, 7, len(underlying.RemoveCalls())) } func TestWriteFile(t *testing.T) { @@ -208,4 +334,18 @@ func TestWriteFile(t *testing.T) { assert.Equal(t, "/some/path/file.txt", call.Path) assert.Equal(t, "third content", call.Data) assert.Equal(t, false, call.WriteByteOrderMark) + + cached.DisableAndClearCache() + _ = cached.WriteFile("/some/path/file.txt", "fourth content", false) + assert.Equal(t, 4, len(underlying.WriteFileCalls())) + + _ = cached.WriteFile("/some/path/file.txt", "fifth content", true) + assert.Equal(t, 5, len(underlying.WriteFileCalls())) + + cached.Enable() + _ = cached.WriteFile("/some/path/file.txt", "sixth content", false) + assert.Equal(t, 6, len(underlying.WriteFileCalls())) + + _ = cached.WriteFile("/some/path/file.txt", "seventh content", true) + assert.Equal(t, 7, len(underlying.WriteFileCalls())) }