b6d161bf16
It can find every Android.bp in internal master in about 2.5 sec the first time and 0.3 sec subsequent times Bug: 62455338 Test: m -j blueprint_tools # which runs the unit tests Test: m -j blueprint_tools && \ out/soong/host/linux-x86/bin/finder \ -v --db /tmp/mydb \ --names Android.mk \ --prune-files .android-out-dir \ --exclude-dirs .git,.repo \ . \ >/tmp/finder-log 2>&1 Change-Id: I5ab2650459a1dae0d5d076faf411ec2d053c743d
1573 lines
42 KiB
Go
1573 lines
42 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"
|
|
"log"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"sort"
|
|
|
|
"io/ioutil"
|
|
|
|
"android/soong/fs"
|
|
"runtime/debug"
|
|
"time"
|
|
)
|
|
|
|
// 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 {
|
|
cachePath := "/finder/finder-db"
|
|
cacheDir := filepath.Dir(cachePath)
|
|
filesystem.MkDirs(cacheDir)
|
|
if cacheParams.WorkingDirectory == "" {
|
|
cacheParams.WorkingDirectory = "/cwd"
|
|
}
|
|
|
|
logger := log.New(ioutil.Discard, "", 0)
|
|
finder := New(cacheParams, filesystem, logger, cachePath)
|
|
return finder
|
|
}
|
|
|
|
func finderWithSameParams(t *testing.T, original *Finder) *Finder {
|
|
return New(
|
|
original.cacheMetadata.Config.CacheParams,
|
|
original.filesystem,
|
|
original.logger,
|
|
original.DbPath)
|
|
}
|
|
|
|
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 {
|
|
t.Fatal(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 {
|
|
t.Fatal(err.Error())
|
|
}
|
|
}
|
|
|
|
func removeAll(t *testing.T, path string, filesystem *fs.MockFs) {
|
|
err := filesystem.RemoveAll(path)
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
}
|
|
|
|
func move(t *testing.T, oldPath string, newPath string, filesystem *fs.MockFs) {
|
|
err := filesystem.Rename(oldPath, newPath)
|
|
if err != nil {
|
|
t.Fatal(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 {
|
|
t.Fatal(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 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 TestNonexistentPath(t *testing.T) {
|
|
filesystem := newFs()
|
|
create(t, "/tmp/findme.txt", filesystem)
|
|
|
|
finder := newFinder(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{"/tmp/IDontExist"},
|
|
IncludeFiles: []string{"findme.txt", "skipme.txt"},
|
|
},
|
|
)
|
|
defer finder.Shutdown()
|
|
|
|
foundPaths := finder.FindNamedAt("/tmp/IAlsoDontExist", "findme.txt")
|
|
|
|
assertSameResponse(t, foundPaths, []string{})
|
|
}
|
|
|
|
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{"/IDoNotExist"},
|
|
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)
|
|
|
|
finder := newFinder(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{"/cwd", "/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("/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{"/", "/a/b/c", "/a/b/c/d/e/f", "/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"})
|
|
}
|