Files
build_soong/finder/finder_test.go
Jeff Gaston b64fc1cab5 Run the Finder and make its results available to Kati
The Finder runs roughly 200ms faster than findleaves.py in aosp,
and runs roughly 400ms faster in internal master.

Bug: 64363847
Test: m -j

Change-Id: I62db8dacc90871e913576fe2443021fb1749a483
2017-08-16 14:56:00 -07:00

1632 lines
44 KiB
Go

// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package finder
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"reflect"
"sort"
"testing"
"time"
"android/soong/fs"
"runtime/debug"
)
// some utils for tests to use
func newFs() *fs.MockFs {
return fs.NewMockFs(map[string][]byte{})
}
func newFinder(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams) *Finder {
f, err := newFinderAndErr(t, filesystem, cacheParams)
if err != nil {
fatal(t, err.Error())
}
return f
}
func newFinderAndErr(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams) (*Finder, error) {
cachePath := "/finder/finder-db"
cacheDir := filepath.Dir(cachePath)
filesystem.MkDirs(cacheDir)
if cacheParams.WorkingDirectory == "" {
cacheParams.WorkingDirectory = "/cwd"
}
logger := log.New(ioutil.Discard, "", 0)
f, err := New(cacheParams, filesystem, logger, cachePath)
return f, err
}
func finderWithSameParams(t *testing.T, original *Finder) *Finder {
f, err := finderAndErrorWithSameParams(t, original)
if err != nil {
fatal(t, err.Error())
}
return f
}
func finderAndErrorWithSameParams(t *testing.T, original *Finder) (*Finder, error) {
f, err := New(
original.cacheMetadata.Config.CacheParams,
original.filesystem,
original.logger,
original.DbPath)
return f, err
}
func write(t *testing.T, path string, content string, filesystem *fs.MockFs) {
parent := filepath.Dir(path)
filesystem.MkDirs(parent)
err := filesystem.WriteFile(path, []byte(content), 0777)
if err != nil {
fatal(t, err.Error())
}
}
func create(t *testing.T, path string, filesystem *fs.MockFs) {
write(t, path, "hi", filesystem)
}
func delete(t *testing.T, path string, filesystem *fs.MockFs) {
err := filesystem.Remove(path)
if err != nil {
fatal(t, err.Error())
}
}
func removeAll(t *testing.T, path string, filesystem *fs.MockFs) {
err := filesystem.RemoveAll(path)
if err != nil {
fatal(t, err.Error())
}
}
func move(t *testing.T, oldPath string, newPath string, filesystem *fs.MockFs) {
err := filesystem.Rename(oldPath, newPath)
if err != nil {
fatal(t, err.Error())
}
}
func link(t *testing.T, newPath string, oldPath string, filesystem *fs.MockFs) {
parentPath := filepath.Dir(newPath)
err := filesystem.MkDirs(parentPath)
if err != nil {
t.Fatal(err.Error())
}
err = filesystem.Symlink(oldPath, newPath)
if err != nil {
fatal(t, err.Error())
}
}
func read(t *testing.T, path string, filesystem *fs.MockFs) string {
reader, err := filesystem.Open(path)
if err != nil {
t.Fatalf(err.Error())
}
bytes, err := ioutil.ReadAll(reader)
if err != nil {
t.Fatal(err.Error())
}
return string(bytes)
}
func modTime(t *testing.T, path string, filesystem *fs.MockFs) time.Time {
stats, err := filesystem.Lstat(path)
if err != nil {
t.Fatal(err.Error())
}
return stats.ModTime()
}
func setReadable(t *testing.T, path string, readable bool, filesystem *fs.MockFs) {
err := filesystem.SetReadable(path, readable)
if err != nil {
t.Fatal(err.Error())
}
}
func setReadErr(t *testing.T, path string, readErr error, filesystem *fs.MockFs) {
err := filesystem.SetReadErr(path, readErr)
if err != nil {
t.Fatal(err.Error())
}
}
func fatal(t *testing.T, message string) {
t.Error(message)
debug.PrintStack()
t.FailNow()
}
func assertSameResponse(t *testing.T, actual []string, expected []string) {
sort.Strings(actual)
sort.Strings(expected)
if !reflect.DeepEqual(actual, expected) {
fatal(
t,
fmt.Sprintf(
"Expected Finder to return these %v paths:\n %v,\ninstead returned these %v paths: %v\n",
len(expected), expected, len(actual), actual),
)
}
}
func assertSameStatCalls(t *testing.T, actual []string, expected []string) {
sort.Strings(actual)
sort.Strings(expected)
if !reflect.DeepEqual(actual, expected) {
fatal(
t,
fmt.Sprintf(
"Finder made incorrect Stat calls.\n"+
"Actual:\n"+
"%v\n"+
"Expected:\n"+
"%v\n"+
"\n",
actual, expected),
)
}
}
func assertSameReadDirCalls(t *testing.T, actual []string, expected []string) {
sort.Strings(actual)
sort.Strings(expected)
if !reflect.DeepEqual(actual, expected) {
fatal(
t,
fmt.Sprintf(
"Finder made incorrect ReadDir calls.\n"+
"Actual:\n"+
"%v\n"+
"Expected:\n"+
"%v\n"+
"\n",
actual, expected),
)
}
}
// runSimpleTests creates a few files, searches for findme.txt, and checks for the expected matches
func runSimpleTest(t *testing.T, existentPaths []string, expectedMatches []string) {
filesystem := newFs()
root := "/tmp"
filesystem.MkDirs(root)
for _, path := range existentPaths {
create(t, filepath.Join(root, path), filesystem)
}
finder := newFinder(t,
filesystem,
CacheParams{
"/cwd",
[]string{root},
nil,
nil,
[]string{"findme.txt", "skipme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt(root, "findme.txt")
absoluteMatches := []string{}
for i := range expectedMatches {
absoluteMatches = append(absoluteMatches, filepath.Join(root, expectedMatches[i]))
}
assertSameResponse(t, foundPaths, absoluteMatches)
}
// end of utils, start of individual tests
func TestSingleFile(t *testing.T) {
runSimpleTest(t,
[]string{"findme.txt"},
[]string{"findme.txt"},
)
}
func TestIncludeFiles(t *testing.T) {
runSimpleTest(t,
[]string{"findme.txt", "skipme.txt"},
[]string{"findme.txt"},
)
}
func TestNestedDirectories(t *testing.T) {
runSimpleTest(t,
[]string{"findme.txt", "skipme.txt", "subdir/findme.txt", "subdir/skipme.txt"},
[]string{"findme.txt", "subdir/findme.txt"},
)
}
func TestEmptyDirectory(t *testing.T) {
runSimpleTest(t,
[]string{},
[]string{},
)
}
func TestEmptyPath(t *testing.T) {
filesystem := newFs()
root := "/tmp"
create(t, filepath.Join(root, "findme.txt"), filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{root},
IncludeFiles: []string{"findme.txt", "skipme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt("", "findme.txt")
assertSameResponse(t, foundPaths, []string{})
}
func TestFilesystemRoot(t *testing.T) {
filesystem := newFs()
root := "/"
createdPath := "/findme.txt"
create(t, createdPath, filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{root},
IncludeFiles: []string{"findme.txt", "skipme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt(root, "findme.txt")
assertSameResponse(t, foundPaths, []string{createdPath})
}
func TestNonexistentDir(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
_, err := newFinderAndErr(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp/IDontExist"},
IncludeFiles: []string{"findme.txt", "skipme.txt"},
},
)
if err == nil {
fatal(t, "Did not fail when given a nonexistent root directory")
}
}
func TestExcludeDirs(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/exclude/findme.txt", filesystem)
create(t, "/tmp/exclude/subdir/findme.txt", filesystem)
create(t, "/tmp/subdir/exclude/findme.txt", filesystem)
create(t, "/tmp/subdir/subdir/findme.txt", filesystem)
create(t, "/tmp/subdir/findme.txt", filesystem)
create(t, "/tmp/findme.txt", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
ExcludeDirs: []string{"exclude"},
IncludeFiles: []string{"findme.txt", "skipme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt",
"/tmp/subdir/findme.txt",
"/tmp/subdir/subdir/findme.txt"})
}
func TestPruneFiles(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/out/findme.txt", filesystem)
create(t, "/tmp/out/.ignore-out-dir", filesystem)
create(t, "/tmp/out/child/findme.txt", filesystem)
create(t, "/tmp/out2/.ignore-out-dir", filesystem)
create(t, "/tmp/out2/sub/findme.txt", filesystem)
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/include/findme.txt", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
PruneFiles: []string{".ignore-out-dir"},
IncludeFiles: []string{"findme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt",
"/tmp/include/findme.txt"})
}
func TestRootDir(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/a/subdir/findme.txt", filesystem)
create(t, "/tmp/b/findme.txt", filesystem)
create(t, "/tmp/b/subdir/findme.txt", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp/a"},
IncludeFiles: []string{"findme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
assertSameResponse(t, foundPaths,
[]string{"/tmp/a/findme.txt",
"/tmp/a/subdir/findme.txt"})
}
func TestUncachedDir(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/a/subdir/findme.txt", filesystem)
create(t, "/tmp/b/findme.txt", filesystem)
create(t, "/tmp/b/subdir/findme.txt", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp/b"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
// If the caller queries for a file that is in the cache, then computing the
// correct answer won't be fast, and it would be easy for the caller to
// fail to notice its slowness. Instead, we only ever search the cache for files
// to return, which enforces that we can determine which files will be
// interesting upfront.
assertSameResponse(t, foundPaths, []string{})
finder.Shutdown()
}
func TestSearchingForFilesExcludedFromCache(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/a/misc.txt", filesystem)
// set up the finder and run it
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "misc.txt")
// If the caller queries for a file that is in the cache, then computing the
// correct answer won't be fast, and it would be easy for the caller to
// fail to notice its slowness. Instead, we only ever search the cache for files
// to return, which enforces that we can determine which files will be
// interesting upfront.
assertSameResponse(t, foundPaths, []string{})
finder.Shutdown()
}
func TestRelativeFilePaths(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/ignore/hi.txt", filesystem)
create(t, "/tmp/include/hi.txt", filesystem)
create(t, "/cwd/hi.txt", filesystem)
create(t, "/cwd/a/hi.txt", filesystem)
create(t, "/cwd/a/a/hi.txt", filesystem)
create(t, "/rel/a/hi.txt", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/cwd", "../rel", "/tmp/include"},
IncludeFiles: []string{"hi.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt("a", "hi.txt")
assertSameResponse(t, foundPaths,
[]string{"a/hi.txt",
"a/a/hi.txt"})
foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
assertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
foundPaths = finder.FindNamedAt(".", "hi.txt")
assertSameResponse(t, foundPaths,
[]string{"hi.txt",
"a/hi.txt",
"a/a/hi.txt"})
foundPaths = finder.FindNamedAt("/rel", "hi.txt")
assertSameResponse(t, foundPaths,
[]string{"/rel/a/hi.txt"})
foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
assertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
}
// have to run this test with the race-detector (`go test -race src/android/soong/finder/*.go`)
// for there to be much chance of the test actually detecting any error that may be present
func TestRootDirsContainedInOtherRootDirs(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/", "/tmp/a/b/c", "/tmp/a/b/c/d/e/f", "/tmp/a/b/c/d/e/f/g/h/i"},
IncludeFiles: []string{"findme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
assertSameResponse(t, foundPaths,
[]string{"/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt"})
}
func TestFindFirst(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/a/hi.txt", filesystem)
create(t, "/tmp/b/hi.txt", filesystem)
create(t, "/tmp/b/a/hi.txt", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"hi.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindFirstNamed("hi.txt")
assertSameResponse(t, foundPaths,
[]string{"/tmp/a/hi.txt",
"/tmp/b/hi.txt"},
)
}
func TestConcurrentFindSameDirectory(t *testing.T) {
filesystem := newFs()
// create a bunch of files and directories
paths := []string{}
for i := 0; i < 10; i++ {
parentDir := fmt.Sprintf("/tmp/%v", i)
for j := 0; j < 10; j++ {
filePath := filepath.Join(parentDir, fmt.Sprintf("%v/findme.txt", j))
paths = append(paths, filePath)
}
}
sort.Strings(paths)
for _, path := range paths {
create(t, path, filesystem)
}
// set up a finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
defer finder.Shutdown()
numTests := 20
results := make(chan []string, numTests)
// make several parallel calls to the finder
for i := 0; i < numTests; i++ {
go func() {
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
results <- foundPaths
}()
}
// check that each response was correct
for i := 0; i < numTests; i++ {
foundPaths := <-results
assertSameResponse(t, foundPaths, paths)
}
}
func TestConcurrentFindDifferentDirectories(t *testing.T) {
filesystem := newFs()
// create a bunch of files and directories
allFiles := []string{}
numSubdirs := 10
rootPaths := []string{}
queryAnswers := [][]string{}
for i := 0; i < numSubdirs; i++ {
parentDir := fmt.Sprintf("/tmp/%v", i)
rootPaths = append(rootPaths, parentDir)
queryAnswers = append(queryAnswers, []string{})
for j := 0; j < 10; j++ {
filePath := filepath.Join(parentDir, fmt.Sprintf("%v/findme.txt", j))
queryAnswers[i] = append(queryAnswers[i], filePath)
allFiles = append(allFiles, filePath)
}
sort.Strings(queryAnswers[i])
}
sort.Strings(allFiles)
for _, path := range allFiles {
create(t, path, filesystem)
}
// set up a finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
defer finder.Shutdown()
type testRun struct {
path string
foundMatches []string
correctMatches []string
}
numTests := numSubdirs + 1
testRuns := make(chan testRun, numTests)
searchAt := func(path string, correctMatches []string) {
foundPaths := finder.FindNamedAt(path, "findme.txt")
testRuns <- testRun{path, foundPaths, correctMatches}
}
// make several parallel calls to the finder
go searchAt("/tmp", allFiles)
for i := 0; i < len(rootPaths); i++ {
go searchAt(rootPaths[i], queryAnswers[i])
}
// check that each response was correct
for i := 0; i < numTests; i++ {
testRun := <-testRuns
assertSameResponse(t, testRun.foundMatches, testRun.correctMatches)
}
}
func TestStrangelyFormattedPaths(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/b/findme.txt", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"//tmp//a//.."},
IncludeFiles: []string{"findme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt("//tmp//a//..", "findme.txt")
assertSameResponse(t, foundPaths,
[]string{"/tmp/a/findme.txt",
"/tmp/b/findme.txt",
"/tmp/findme.txt"})
}
func TestCorruptedCacheHeader(t *testing.T) {
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
write(t, "/finder/finder-db", "sample header", filesystem)
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
defer finder.Shutdown()
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
assertSameResponse(t, foundPaths,
[]string{"/tmp/a/findme.txt",
"/tmp/findme.txt"})
}
func TestCanUseCache(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
// check the response of the first finder
correctResponse := []string{"/tmp/a/findme.txt",
"/tmp/findme.txt"}
assertSameResponse(t, foundPaths, correctResponse)
finder.Shutdown()
// check results
cacheText := read(t, finder.DbPath, filesystem)
if len(cacheText) < 1 {
t.Fatalf("saved cache db is empty\n")
}
if len(filesystem.StatCalls) == 0 {
t.Fatal("No Stat calls recorded by mock filesystem")
}
if len(filesystem.ReadDirCalls) == 0 {
t.Fatal("No ReadDir calls recorded by filesystem")
}
statCalls := filesystem.StatCalls
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
assertSameReadDirCalls(t, filesystem.StatCalls, statCalls)
finder2.Shutdown()
}
func TestCorruptedCacheBody(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check the response of the first finder
correctResponse := []string{"/tmp/a/findme.txt",
"/tmp/findme.txt"}
assertSameResponse(t, foundPaths, correctResponse)
numStatCalls := len(filesystem.StatCalls)
numReadDirCalls := len(filesystem.ReadDirCalls)
// load the cache file, corrupt it, and save it
cacheReader, err := filesystem.Open(finder.DbPath)
if err != nil {
t.Fatal(err)
}
cacheData, err := ioutil.ReadAll(cacheReader)
if err != nil {
t.Fatal(err)
}
cacheData = append(cacheData, []byte("DontMindMe")...)
filesystem.WriteFile(finder.DbPath, cacheData, 0777)
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameResponse(t, foundPaths, correctResponse)
numNewStatCalls := len(filesystem.StatCalls)
numNewReadDirCalls := len(filesystem.ReadDirCalls)
// It's permissable to make more Stat calls with a corrupted cache because
// the Finder may restart once it detects corruption.
// However, it may have already issued many Stat calls.
// Because a corrupted db is not expected to be a common (or even a supported case),
// we don't care to optimize it and don't cache the already-issued Stat calls
if numNewReadDirCalls < numReadDirCalls {
t.Fatalf(
"Finder made fewer ReadDir calls with a corrupted cache (%v calls) than with no cache"+
" (%v calls)",
numNewReadDirCalls, numReadDirCalls)
}
if numNewStatCalls < numStatCalls {
t.Fatalf(
"Finder made fewer Stat calls with a corrupted cache (%v calls) than with no cache (%v calls)",
numNewStatCalls, numStatCalls)
}
finder2.Shutdown()
}
func TestStatCalls(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/a/findme.txt", filesystem)
// run finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check response
assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
}
func TestFileAdded(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/ignoreme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/b/ignore.txt", filesystem)
create(t, "/tmp/b/c/nope.txt", filesystem)
create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
filesystem.Clock.Tick()
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
// modify the filesystem
filesystem.Clock.Tick()
create(t, "/tmp/b/c/findme.txt", filesystem)
filesystem.Clock.Tick()
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/c/findme.txt"})
assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c"})
finder2.Shutdown()
}
func TestDirectoriesAdded(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/ignoreme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/b/ignore.txt", filesystem)
create(t, "/tmp/b/c/nope.txt", filesystem)
create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
// modify the filesystem
filesystem.Clock.Tick()
create(t, "/tmp/b/c/new/findme.txt", filesystem)
create(t, "/tmp/b/c/new/new2/findme.txt", filesystem)
create(t, "/tmp/b/c/new/new2/ignoreme.txt", filesystem)
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameResponse(t, foundPaths,
[]string{"/tmp/a/findme.txt", "/tmp/b/c/new/findme.txt", "/tmp/b/c/new/new2/findme.txt"})
assertSameStatCalls(t, filesystem.StatCalls,
[]string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
finder2.Shutdown()
}
func TestDirectoryAndSubdirectoryBothUpdated(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/hi1.txt", filesystem)
create(t, "/tmp/a/hi1.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"hi1.txt", "hi2.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "hi1.txt")
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths, []string{"/tmp/hi1.txt", "/tmp/a/hi1.txt"})
// modify the filesystem
filesystem.Clock.Tick()
create(t, "/tmp/hi2.txt", filesystem)
create(t, "/tmp/a/hi2.txt", filesystem)
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindAll()
// check results
assertSameResponse(t, foundPaths,
[]string{"/tmp/hi1.txt", "/tmp/hi2.txt", "/tmp/a/hi1.txt", "/tmp/a/hi2.txt"})
assertSameStatCalls(t, filesystem.StatCalls,
[]string{"/tmp", "/tmp/a"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
finder2.Shutdown()
}
func TestFileDeleted(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/ignoreme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/b/findme.txt", filesystem)
create(t, "/tmp/b/c/nope.txt", filesystem)
create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/findme.txt"})
// modify the filesystem
filesystem.Clock.Tick()
delete(t, "/tmp/b/findme.txt", filesystem)
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
finder2.Shutdown()
}
func TestDirectoriesDeleted(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/a/1/findme.txt", filesystem)
create(t, "/tmp/a/1/2/findme.txt", filesystem)
create(t, "/tmp/b/findme.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt",
"/tmp/a/findme.txt",
"/tmp/a/1/findme.txt",
"/tmp/a/1/2/findme.txt",
"/tmp/b/findme.txt"})
// modify the filesystem
filesystem.Clock.Tick()
removeAll(t, "/tmp/a/1", filesystem)
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/b/findme.txt"})
// Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
// if the Finder detects the nonexistence of /tmp/a/1
// However, when resuming from cache, we don't want the Finder to necessarily wait
// to stat a directory until after statting its parent.
// So here we just include /tmp/a/1/2 in the list.
// The Finder is currently implemented to always restat every dir and
// to not short-circuit due to nonexistence of parents (but it will remove
// missing dirs from the cache for next time)
assertSameStatCalls(t, filesystem.StatCalls,
[]string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/a"})
finder2.Shutdown()
}
func TestDirectoriesMoved(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/a/1/findme.txt", filesystem)
create(t, "/tmp/a/1/2/findme.txt", filesystem)
create(t, "/tmp/b/findme.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt",
"/tmp/a/findme.txt",
"/tmp/a/1/findme.txt",
"/tmp/a/1/2/findme.txt",
"/tmp/b/findme.txt"})
// modify the filesystem
filesystem.Clock.Tick()
move(t, "/tmp/a", "/tmp/c", filesystem)
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt",
"/tmp/b/findme.txt",
"/tmp/c/findme.txt",
"/tmp/c/1/findme.txt",
"/tmp/c/1/2/findme.txt"})
// Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
// if the Finder detects the nonexistence of /tmp/a/1
// However, when resuming from cache, we don't want the Finder to necessarily wait
// to stat a directory until after statting its parent.
// So here we just include /tmp/a/1/2 in the list.
// The Finder is currently implemented to always restat every dir and
// to not short-circuit due to nonexistence of parents (but it will remove
// missing dirs from the cache for next time)
assertSameStatCalls(t, filesystem.StatCalls,
[]string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
finder2.Shutdown()
}
func TestDirectoriesSwapped(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/a/1/findme.txt", filesystem)
create(t, "/tmp/a/1/2/findme.txt", filesystem)
create(t, "/tmp/b/findme.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt",
"/tmp/a/findme.txt",
"/tmp/a/1/findme.txt",
"/tmp/a/1/2/findme.txt",
"/tmp/b/findme.txt"})
// modify the filesystem
filesystem.Clock.Tick()
move(t, "/tmp/a", "/tmp/temp", filesystem)
move(t, "/tmp/b", "/tmp/a", filesystem)
move(t, "/tmp/temp", "/tmp/b", filesystem)
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt",
"/tmp/a/findme.txt",
"/tmp/b/findme.txt",
"/tmp/b/1/findme.txt",
"/tmp/b/1/2/findme.txt"})
// Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
// if the Finder detects the nonexistence of /tmp/a/1
// However, when resuming from cache, we don't want the Finder to necessarily wait
// to stat a directory until after statting its parent.
// So here we just include /tmp/a/1/2 in the list.
// The Finder is currently implemented to always restat every dir and
// to not short-circuit due to nonexistence of parents (but it will remove
// missing dirs from the cache for next time)
assertSameStatCalls(t, filesystem.StatCalls,
[]string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
finder2.Shutdown()
}
// runFsReplacementTest tests a change modifying properties of the filesystem itself:
// runFsReplacementTest tests changing the user, the hostname, or the device number
// runFsReplacementTest is a helper method called by other tests
func runFsReplacementTest(t *testing.T, fs1 *fs.MockFs, fs2 *fs.MockFs) {
// setup fs1
create(t, "/tmp/findme.txt", fs1)
create(t, "/tmp/a/findme.txt", fs1)
create(t, "/tmp/a/a/findme.txt", fs1)
// setup fs2 to have the same directories but different files
create(t, "/tmp/findme.txt", fs2)
create(t, "/tmp/a/findme.txt", fs2)
create(t, "/tmp/a/a/ignoreme.txt", fs2)
create(t, "/tmp/a/b/findme.txt", fs2)
// run the first finder
finder := newFinder(
t,
fs1,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// check the response of the first finder
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/a/findme.txt"})
// copy the cache data from the first filesystem to the second
cacheContent := read(t, finder.DbPath, fs1)
write(t, finder.DbPath, cacheContent, fs2)
// run the second finder, with the same config and same cache contents but a different filesystem
finder2 := newFinder(
t,
fs2,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
// check results
assertSameResponse(t, foundPaths,
[]string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/b/findme.txt"})
assertSameStatCalls(t, fs2.StatCalls,
[]string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
assertSameReadDirCalls(t, fs2.ReadDirCalls,
[]string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
finder2.Shutdown()
}
func TestChangeOfDevice(t *testing.T) {
fs1 := newFs()
// not as fine-grained mounting controls as a real filesystem, but should be adequate
fs1.SetDeviceNumber(0)
fs2 := newFs()
fs2.SetDeviceNumber(1)
runFsReplacementTest(t, fs1, fs2)
}
func TestChangeOfUserOrHost(t *testing.T) {
fs1 := newFs()
fs1.SetViewId("me@here")
fs2 := newFs()
fs2.SetViewId("you@there")
runFsReplacementTest(t, fs1, fs2)
}
func TestConsistentCacheOrdering(t *testing.T) {
// setup filesystem
filesystem := newFs()
for i := 0; i < 5; i++ {
create(t, fmt.Sprintf("/tmp/%v/findme.txt", i), filesystem)
}
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
finder.FindNamedAt("/tmp", "findme.txt")
finder.Shutdown()
// read db file
string1 := read(t, finder.DbPath, filesystem)
err := filesystem.Remove(finder.DbPath)
if err != nil {
t.Fatal(err)
}
// run another finder
finder2 := finderWithSameParams(t, finder)
finder2.FindNamedAt("/tmp", "findme.txt")
finder2.Shutdown()
string2 := read(t, finder.DbPath, filesystem)
if string1 != string2 {
t.Errorf("Running Finder twice generated two dbs not having identical contents.\n"+
"Content of first file:\n"+
"\n"+
"%v"+
"\n"+
"\n"+
"Content of second file:\n"+
"\n"+
"%v\n"+
"\n",
string1,
string2,
)
}
}
func TestNumSyscallsOfSecondFind(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/a/misc.txt", filesystem)
// set up the finder and run it once
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
assertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
filesystem.ClearMetrics()
// run the finder again and confirm it doesn't check the filesystem
refoundPaths := finder.FindNamedAt("/tmp", "findme.txt")
assertSameResponse(t, refoundPaths, foundPaths)
assertSameStatCalls(t, filesystem.StatCalls, []string{})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
finder.Shutdown()
}
func TestChangingParamsOfSecondFind(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/findme.txt", filesystem)
create(t, "/tmp/a/findme.txt", filesystem)
create(t, "/tmp/a/metoo.txt", filesystem)
// set up the finder and run it once
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"findme.txt", "metoo.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
assertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
filesystem.ClearMetrics()
// run the finder again and confirm it gets the right answer without asking the filesystem
refoundPaths := finder.FindNamedAt("/tmp", "metoo.txt")
assertSameResponse(t, refoundPaths, []string{"/tmp/a/metoo.txt"})
assertSameStatCalls(t, filesystem.StatCalls, []string{})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
finder.Shutdown()
}
func TestSymlinkPointingToFile(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/a/hi.txt", filesystem)
create(t, "/tmp/a/ignoreme.txt", filesystem)
link(t, "/tmp/hi.txt", "a/hi.txt", filesystem)
link(t, "/tmp/b/hi.txt", "../a/hi.txt", filesystem)
link(t, "/tmp/c/hi.txt", "/tmp/hi.txt", filesystem)
link(t, "/tmp/d/hi.txt", "../a/bye.txt", filesystem)
link(t, "/tmp/d/bye.txt", "../a/hi.txt", filesystem)
link(t, "/tmp/e/bye.txt", "../a/bye.txt", filesystem)
link(t, "/tmp/f/hi.txt", "somethingThatDoesntExist", filesystem)
// set up the finder and run it once
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"hi.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
// should search based on the name of the link rather than the destination or validity of the link
correctResponse := []string{
"/tmp/a/hi.txt",
"/tmp/hi.txt",
"/tmp/b/hi.txt",
"/tmp/c/hi.txt",
"/tmp/d/hi.txt",
"/tmp/f/hi.txt",
}
assertSameResponse(t, foundPaths, correctResponse)
}
func TestSymlinkPointingToDirectory(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/dir/hi.txt", filesystem)
create(t, "/tmp/dir/ignoreme.txt", filesystem)
link(t, "/tmp/links/dir", "../dir", filesystem)
link(t, "/tmp/links/link", "../dir", filesystem)
link(t, "/tmp/links/broken", "nothingHere", filesystem)
link(t, "/tmp/links/recursive", "recursive", filesystem)
// set up the finder and run it once
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"hi.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
// should completely ignore symlinks that point to directories
correctResponse := []string{
"/tmp/dir/hi.txt",
}
assertSameResponse(t, foundPaths, correctResponse)
}
// TestAddPruneFile confirms that adding a prune-file (into a directory for which we
// already had a cache) causes the directory to be ignored
func TestAddPruneFile(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/out/hi.txt", filesystem)
create(t, "/tmp/out/a/hi.txt", filesystem)
create(t, "/tmp/hi.txt", filesystem)
// do find
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
PruneFiles: []string{".ignore-out-dir"},
IncludeFiles: []string{"hi.txt"},
},
)
foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
// check result
assertSameResponse(t, foundPaths,
[]string{"/tmp/hi.txt",
"/tmp/out/hi.txt",
"/tmp/out/a/hi.txt"},
)
finder.Shutdown()
// modify filesystem
filesystem.Clock.Tick()
create(t, "/tmp/out/.ignore-out-dir", filesystem)
// run another find and check its result
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindNamedAt("/tmp", "hi.txt")
assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
finder2.Shutdown()
}
func TestUpdatingDbIffChanged(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/a/hi.txt", filesystem)
create(t, "/tmp/b/bye.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"hi.txt"},
},
)
foundPaths := finder.FindAll()
filesystem.Clock.Tick()
finder.Shutdown()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
// modify the filesystem
filesystem.Clock.Tick()
create(t, "/tmp/b/hi.txt", filesystem)
filesystem.Clock.Tick()
filesystem.ClearMetrics()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindAll()
finder2.Shutdown()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
expectedDbWriteTime := filesystem.Clock.Time()
actualDbWriteTime := modTime(t, finder2.DbPath, filesystem)
if actualDbWriteTime != expectedDbWriteTime {
t.Fatalf("Expected to write db at %v, actually wrote db at %v\n",
expectedDbWriteTime, actualDbWriteTime)
}
// reset metrics
filesystem.ClearMetrics()
// run the third finder
finder3 := finderWithSameParams(t, finder2)
foundPaths = finder3.FindAll()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
finder3.Shutdown()
actualDbWriteTime = modTime(t, finder3.DbPath, filesystem)
if actualDbWriteTime != expectedDbWriteTime {
t.Fatalf("Re-wrote db even when contents did not change")
}
}
func TestDirectoryNotPermitted(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/hi.txt", filesystem)
create(t, "/tmp/a/hi.txt", filesystem)
create(t, "/tmp/a/a/hi.txt", filesystem)
create(t, "/tmp/b/hi.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"hi.txt"},
},
)
foundPaths := finder.FindAll()
filesystem.Clock.Tick()
finder.Shutdown()
allPaths := []string{"/tmp/hi.txt", "/tmp/a/hi.txt", "/tmp/a/a/hi.txt", "/tmp/b/hi.txt"}
// check results
assertSameResponse(t, foundPaths, allPaths)
// modify the filesystem
filesystem.Clock.Tick()
setReadable(t, "/tmp/a", false, filesystem)
filesystem.Clock.Tick()
// run the second finder
finder2 := finderWithSameParams(t, finder)
foundPaths = finder2.FindAll()
finder2.Shutdown()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt", "/tmp/b/hi.txt"})
// modify the filesystem back
setReadable(t, "/tmp/a", true, filesystem)
// run the third finder
finder3 := finderWithSameParams(t, finder2)
foundPaths = finder3.FindAll()
finder3.Shutdown()
// check results
assertSameResponse(t, foundPaths, allPaths)
}
func TestFileNotPermitted(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/hi.txt", filesystem)
setReadable(t, "/tmp/hi.txt", false, filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"hi.txt"},
},
)
foundPaths := finder.FindAll()
filesystem.Clock.Tick()
finder.Shutdown()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
}
func TestCacheEntryPathUnexpectedError(t *testing.T) {
// setup filesystem
filesystem := newFs()
create(t, "/tmp/a/hi.txt", filesystem)
// run the first finder
finder := newFinder(
t,
filesystem,
CacheParams{
RootDirs: []string{"/tmp"},
IncludeFiles: []string{"hi.txt"},
},
)
foundPaths := finder.FindAll()
filesystem.Clock.Tick()
finder.Shutdown()
// check results
assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
// make the directory not readable
setReadErr(t, "/tmp/a", os.ErrInvalid, filesystem)
// run the second finder
_, err := finderAndErrorWithSameParams(t, finder)
if err == nil {
fatal(t, "Failed to detect unexpected filesystem error")
}
}