e3487c8848
This required the following: - Adding Platform_base_sdk_extension_version to default soong.variables - Teaching the symlink tree creation code to understand symlinks - Making finder.go follow symlinks when requested Adding yet another knob is unfortunate, but I can't allow that unconditionally because the Android code base contains a number of symlinks giving rise to infinite directory trees because they point back to their parent and this seemed preferable to adding complicated logic like "follow symlink but if only its fully resolved version does not point under the source tree". I could be convinced about the latter, though. Test: Presubmits. Change-Id: I453f6b7e5334771f5832c700db00f9d24ed1d82f
1590 lines
44 KiB
Go
1590 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"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"android/soong/finder/fs"
|
|
)
|
|
|
|
// 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 {
|
|
return newFinderWithNumThreads(t, filesystem, cacheParams, 2)
|
|
}
|
|
|
|
func newFinderWithNumThreads(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams, numThreads int) *Finder {
|
|
f, err := newFinderAndErr(t, filesystem, cacheParams, numThreads)
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
return f
|
|
}
|
|
|
|
func newFinderAndErr(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams, numThreads int) (*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 := newImpl(cacheParams, filesystem, logger, cachePath, numThreads)
|
|
return f, err
|
|
}
|
|
|
|
func finderWithSameParams(t *testing.T, original *Finder) *Finder {
|
|
f, err := finderAndErrorWithSameParams(t, original)
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
return f
|
|
}
|
|
|
|
func finderAndErrorWithSameParams(t *testing.T, original *Finder) (*Finder, error) {
|
|
f, err := newImpl(
|
|
original.cacheMetadata.Config.CacheParams,
|
|
original.filesystem,
|
|
original.logger,
|
|
original.DbPath,
|
|
original.numDbLoadingThreads,
|
|
)
|
|
return f, err
|
|
}
|
|
|
|
// 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 {
|
|
fs.Create(t, filepath.Join(root, path), filesystem)
|
|
}
|
|
|
|
finder := newFinder(t,
|
|
filesystem,
|
|
CacheParams{
|
|
"/cwd",
|
|
[]string{root},
|
|
false,
|
|
nil,
|
|
nil,
|
|
[]string{"findme.txt", "skipme.txt"},
|
|
nil,
|
|
},
|
|
)
|
|
defer finder.Shutdown()
|
|
|
|
foundPaths := finder.FindNamedAt(root, "findme.txt")
|
|
absoluteMatches := []string{}
|
|
for i := range expectedMatches {
|
|
absoluteMatches = append(absoluteMatches, filepath.Join(root, expectedMatches[i]))
|
|
}
|
|
fs.AssertSameResponse(t, foundPaths, absoluteMatches)
|
|
}
|
|
|
|
// runTestWithSuffixes creates a few files, searches for findme.txt or any file
|
|
// with suffix `.findme_ext` and checks for the expected matches
|
|
func runTestWithSuffixes(t *testing.T, existentPaths []string, expectedMatches []string) {
|
|
filesystem := newFs()
|
|
root := "/tmp"
|
|
filesystem.MkDirs(root)
|
|
for _, path := range existentPaths {
|
|
fs.Create(t, filepath.Join(root, path), filesystem)
|
|
}
|
|
|
|
finder := newFinder(t,
|
|
filesystem,
|
|
CacheParams{
|
|
"/cwd",
|
|
[]string{root},
|
|
false,
|
|
nil,
|
|
nil,
|
|
[]string{"findme.txt", "skipme.txt"},
|
|
[]string{".findme_ext"},
|
|
},
|
|
)
|
|
defer finder.Shutdown()
|
|
|
|
foundPaths := finder.FindMatching(root,
|
|
func(entries DirEntries) (dirs []string, files []string) {
|
|
matches := []string{}
|
|
for _, foundName := range entries.FileNames {
|
|
if foundName == "findme.txt" || strings.HasSuffix(foundName, ".findme_ext") {
|
|
matches = append(matches, foundName)
|
|
}
|
|
}
|
|
return entries.DirNames, matches
|
|
})
|
|
absoluteMatches := []string{}
|
|
for i := range expectedMatches {
|
|
absoluteMatches = append(absoluteMatches, filepath.Join(root, expectedMatches[i]))
|
|
}
|
|
fs.AssertSameResponse(t, foundPaths, absoluteMatches)
|
|
}
|
|
|
|
// testAgainstSeveralThreadcounts runs the given test for each threadcount that we care to test
|
|
func testAgainstSeveralThreadcounts(t *testing.T, tester func(t *testing.T, numThreads int)) {
|
|
// test singlethreaded, multithreaded, and also using the same number of threads as
|
|
// will be used on the current system
|
|
threadCounts := []int{1, 2, defaultNumThreads}
|
|
for _, numThreads := range threadCounts {
|
|
testName := fmt.Sprintf("%v threads", numThreads)
|
|
// store numThreads in a new variable to prevent numThreads from changing in each loop
|
|
localNumThreads := numThreads
|
|
t.Run(testName, func(t *testing.T) {
|
|
tester(t, localNumThreads)
|
|
})
|
|
}
|
|
}
|
|
|
|
// 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 TestIncludeFilesAndSuffixes(t *testing.T) {
|
|
runTestWithSuffixes(t,
|
|
[]string{"findme.txt", "skipme.txt", "alsome.findme_ext"},
|
|
[]string{"findme.txt", "alsome.findme_ext"},
|
|
)
|
|
}
|
|
|
|
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 TestNestedDirectoriesWithSuffixes(t *testing.T) {
|
|
runTestWithSuffixes(t,
|
|
[]string{"findme.txt", "skipme.txt", "subdir/findme.txt", "subdir/skipme.txt", "subdir/alsome.findme_ext"},
|
|
[]string{"findme.txt", "subdir/findme.txt", "subdir/alsome.findme_ext"},
|
|
)
|
|
}
|
|
|
|
func TestEmptyDirectory(t *testing.T) {
|
|
runSimpleTest(t,
|
|
[]string{},
|
|
[]string{},
|
|
)
|
|
}
|
|
|
|
func TestEmptyPath(t *testing.T) {
|
|
filesystem := newFs()
|
|
root := "/tmp"
|
|
fs.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")
|
|
|
|
fs.AssertSameResponse(t, foundPaths, []string{})
|
|
}
|
|
|
|
func TestFilesystemRoot(t *testing.T) {
|
|
|
|
testWithNumThreads := func(t *testing.T, numThreads int) {
|
|
filesystem := newFs()
|
|
root := "/"
|
|
createdPath := "/findme.txt"
|
|
fs.Create(t, createdPath, filesystem)
|
|
|
|
finder := newFinderWithNumThreads(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{root},
|
|
IncludeFiles: []string{"findme.txt", "skipme.txt"},
|
|
},
|
|
numThreads,
|
|
)
|
|
defer finder.Shutdown()
|
|
|
|
foundPaths := finder.FindNamedAt(root, "findme.txt")
|
|
|
|
fs.AssertSameResponse(t, foundPaths, []string{createdPath})
|
|
}
|
|
|
|
testAgainstSeveralThreadcounts(t, testWithNumThreads)
|
|
}
|
|
|
|
func TestNonexistentDir(t *testing.T) {
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
|
|
_, err := newFinderAndErr(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{"/tmp/IDontExist"},
|
|
IncludeFiles: []string{"findme.txt", "skipme.txt"},
|
|
},
|
|
1,
|
|
)
|
|
if err == nil {
|
|
t.Fatal("Did not fail when given a nonexistent root directory")
|
|
}
|
|
}
|
|
|
|
func TestExcludeDirs(t *testing.T) {
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/exclude/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/exclude/subdir/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/subdir/exclude/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/subdir/subdir/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/subdir/findme.txt", filesystem)
|
|
fs.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")
|
|
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/findme.txt",
|
|
"/tmp/subdir/findme.txt",
|
|
"/tmp/subdir/subdir/findme.txt"})
|
|
}
|
|
|
|
func TestPruneFiles(t *testing.T) {
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/out/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/out/.ignore-out-dir", filesystem)
|
|
fs.Create(t, "/tmp/out/child/findme.txt", filesystem)
|
|
|
|
fs.Create(t, "/tmp/out2/.ignore-out-dir", filesystem)
|
|
fs.Create(t, "/tmp/out2/sub/findme.txt", filesystem)
|
|
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.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")
|
|
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/findme.txt",
|
|
"/tmp/include/findme.txt"})
|
|
}
|
|
|
|
// TestRootDir tests that the value of RootDirs is used
|
|
// tests of the filesystem root are in TestFilesystemRoot
|
|
func TestRootDir(t *testing.T) {
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/subdir/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/findme.txt", filesystem)
|
|
fs.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")
|
|
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/a/findme.txt",
|
|
"/tmp/a/subdir/findme.txt"})
|
|
}
|
|
|
|
func TestUncachedDir(t *testing.T) {
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/subdir/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/findme.txt", filesystem)
|
|
fs.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.
|
|
fs.AssertSameResponse(t, foundPaths, []string{})
|
|
|
|
finder.Shutdown()
|
|
}
|
|
|
|
func TestSearchingForFilesExcludedFromCache(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.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.
|
|
fs.AssertSameResponse(t, foundPaths, []string{})
|
|
|
|
finder.Shutdown()
|
|
}
|
|
|
|
func TestRelativeFilePaths(t *testing.T) {
|
|
filesystem := newFs()
|
|
|
|
fs.Create(t, "/tmp/ignore/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/include/hi.txt", filesystem)
|
|
fs.Create(t, "/cwd/hi.txt", filesystem)
|
|
fs.Create(t, "/cwd/a/hi.txt", filesystem)
|
|
fs.Create(t, "/cwd/a/a/hi.txt", filesystem)
|
|
fs.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")
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"a/hi.txt",
|
|
"a/a/hi.txt"})
|
|
|
|
foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
|
|
|
|
foundPaths = finder.FindNamedAt(".", "hi.txt")
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"hi.txt",
|
|
"a/hi.txt",
|
|
"a/a/hi.txt"})
|
|
|
|
foundPaths = finder.FindNamedAt("/rel", "hi.txt")
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/rel/a/hi.txt"})
|
|
|
|
foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
|
|
fs.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()
|
|
|
|
fs.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")
|
|
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt"})
|
|
}
|
|
|
|
func TestFindFirst(t *testing.T) {
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/a/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/hi.txt", filesystem)
|
|
fs.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")
|
|
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/a/hi.txt",
|
|
"/tmp/b/hi.txt"},
|
|
)
|
|
}
|
|
|
|
func TestConcurrentFindSameDirectory(t *testing.T) {
|
|
|
|
testWithNumThreads := func(t *testing.T, numThreads int) {
|
|
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 {
|
|
fs.Create(t, path, filesystem)
|
|
}
|
|
|
|
// set up a finder
|
|
finder := newFinderWithNumThreads(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{"/tmp"},
|
|
IncludeFiles: []string{"findme.txt"},
|
|
},
|
|
numThreads,
|
|
)
|
|
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
|
|
fs.AssertSameResponse(t, foundPaths, paths)
|
|
}
|
|
}
|
|
|
|
testAgainstSeveralThreadcounts(t, testWithNumThreads)
|
|
}
|
|
|
|
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 {
|
|
fs.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
|
|
fs.AssertSameResponse(t, testRun.foundMatches, testRun.correctMatches)
|
|
}
|
|
}
|
|
|
|
func TestStrangelyFormattedPaths(t *testing.T) {
|
|
filesystem := newFs()
|
|
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.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")
|
|
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/a/findme.txt",
|
|
"/tmp/b/findme.txt",
|
|
"/tmp/findme.txt"})
|
|
}
|
|
|
|
func TestCorruptedCacheHeader(t *testing.T) {
|
|
filesystem := newFs()
|
|
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.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")
|
|
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/a/findme.txt",
|
|
"/tmp/findme.txt"})
|
|
}
|
|
|
|
func TestCanUseCache(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.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"}
|
|
fs.AssertSameResponse(t, foundPaths, correctResponse)
|
|
finder.Shutdown()
|
|
|
|
// check results
|
|
cacheText := fs.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
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
|
|
fs.AssertSameReadDirCalls(t, filesystem.StatCalls, statCalls)
|
|
|
|
finder2.Shutdown()
|
|
}
|
|
|
|
func TestCorruptedCacheBody(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.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"}
|
|
fs.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
|
|
fs.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()
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
|
|
fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a"})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
|
|
}
|
|
|
|
func TestFileAdded(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/ignoreme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/ignore.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
|
|
fs.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"},
|
|
},
|
|
)
|
|
filesystem.Clock.Tick()
|
|
foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
|
|
finder.Shutdown()
|
|
// check the response of the first finder
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
|
|
|
|
// modify the filesystem
|
|
filesystem.Clock.Tick()
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/c/findme.txt"})
|
|
fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c"})
|
|
finder2.Shutdown()
|
|
|
|
}
|
|
|
|
func TestDirectoriesAdded(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/ignoreme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/ignore.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
|
|
|
|
// modify the filesystem
|
|
filesystem.Clock.Tick()
|
|
fs.Create(t, "/tmp/b/c/new/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/c/new/new2/findme.txt", filesystem)
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/a/findme.txt", "/tmp/b/c/new/findme.txt", "/tmp/b/c/new/new2/findme.txt"})
|
|
fs.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"})
|
|
fs.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()
|
|
fs.Create(t, "/tmp/hi1.txt", filesystem)
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi1.txt", "/tmp/a/hi1.txt"})
|
|
|
|
// modify the filesystem
|
|
filesystem.Clock.Tick()
|
|
fs.Create(t, "/tmp/hi2.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/hi2.txt", filesystem)
|
|
filesystem.ClearMetrics()
|
|
|
|
// run the second finder
|
|
finder2 := finderWithSameParams(t, finder)
|
|
foundPaths = finder2.FindAll()
|
|
|
|
// check results
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/hi1.txt", "/tmp/hi2.txt", "/tmp/a/hi1.txt", "/tmp/a/hi2.txt"})
|
|
fs.AssertSameStatCalls(t, filesystem.StatCalls,
|
|
[]string{"/tmp", "/tmp/a"})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
|
|
|
|
finder2.Shutdown()
|
|
}
|
|
|
|
func TestFileDeleted(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/ignoreme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/findme.txt"})
|
|
|
|
// modify the filesystem
|
|
filesystem.Clock.Tick()
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
|
|
fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
|
|
|
|
finder2.Shutdown()
|
|
}
|
|
|
|
func TestDirectoriesDeleted(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
|
|
fs.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
|
|
fs.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()
|
|
fs.RemoveAll(t, "/tmp/a/1", filesystem)
|
|
filesystem.ClearMetrics()
|
|
|
|
// run the second finder
|
|
finder2 := finderWithSameParams(t, finder)
|
|
foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
|
|
|
|
// check results
|
|
fs.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)
|
|
fs.AssertSameStatCalls(t, filesystem.StatCalls,
|
|
[]string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b"})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/a"})
|
|
|
|
finder2.Shutdown()
|
|
}
|
|
|
|
func TestDirectoriesMoved(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
|
|
fs.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
|
|
fs.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()
|
|
fs.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
|
|
fs.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)
|
|
fs.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"})
|
|
fs.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()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
|
|
fs.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
|
|
fs.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()
|
|
fs.Move(t, "/tmp/a", "/tmp/temp", filesystem)
|
|
fs.Move(t, "/tmp/b", "/tmp/a", filesystem)
|
|
fs.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
|
|
fs.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)
|
|
fs.AssertSameStatCalls(t, filesystem.StatCalls,
|
|
[]string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
|
|
fs.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
|
|
fs.Create(t, "/tmp/findme.txt", fs1)
|
|
fs.Create(t, "/tmp/a/findme.txt", fs1)
|
|
fs.Create(t, "/tmp/a/a/findme.txt", fs1)
|
|
|
|
// setup fs2 to have the same directories but different files
|
|
fs.Create(t, "/tmp/findme.txt", fs2)
|
|
fs.Create(t, "/tmp/a/findme.txt", fs2)
|
|
fs.Create(t, "/tmp/a/a/ignoreme.txt", fs2)
|
|
fs.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
|
|
fs.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 := fs.Read(t, finder.DbPath, fs1)
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/b/findme.txt"})
|
|
fs.AssertSameStatCalls(t, fs2.StatCalls,
|
|
[]string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
|
|
fs.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++ {
|
|
fs.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 := fs.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 := fs.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()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.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")
|
|
fs.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")
|
|
fs.AssertSameResponse(t, refoundPaths, foundPaths)
|
|
fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
|
|
|
|
finder.Shutdown()
|
|
}
|
|
|
|
func TestChangingParamsOfSecondFind(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/findme.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/findme.txt", filesystem)
|
|
fs.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")
|
|
fs.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")
|
|
fs.AssertSameResponse(t, refoundPaths, []string{"/tmp/a/metoo.txt"})
|
|
fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
|
|
|
|
finder.Shutdown()
|
|
}
|
|
|
|
func TestSymlinkPointingToFile(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/a/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/ignoreme.txt", filesystem)
|
|
fs.Link(t, "/tmp/hi.txt", "a/hi.txt", filesystem)
|
|
fs.Link(t, "/tmp/b/hi.txt", "../a/hi.txt", filesystem)
|
|
fs.Link(t, "/tmp/c/hi.txt", "/tmp/hi.txt", filesystem)
|
|
fs.Link(t, "/tmp/d/hi.txt", "../a/bye.txt", filesystem)
|
|
fs.Link(t, "/tmp/d/bye.txt", "../a/hi.txt", filesystem)
|
|
fs.Link(t, "/tmp/e/bye.txt", "../a/bye.txt", filesystem)
|
|
fs.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",
|
|
}
|
|
fs.AssertSameResponse(t, foundPaths, correctResponse)
|
|
|
|
}
|
|
|
|
func TestSymlinkPointingToDirectory(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/dir/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/dir/ignoreme.txt", filesystem)
|
|
|
|
fs.Link(t, "/tmp/links/dir", "../dir", filesystem)
|
|
fs.Link(t, "/tmp/links/link", "../dir", filesystem)
|
|
fs.Link(t, "/tmp/links/hi.txt", "../dir", filesystem)
|
|
fs.Link(t, "/tmp/links/broken", "nothingHere", filesystem)
|
|
fs.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",
|
|
}
|
|
fs.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()
|
|
fs.Create(t, "/tmp/out/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/out/a/hi.txt", filesystem)
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths,
|
|
[]string{"/tmp/hi.txt",
|
|
"/tmp/out/hi.txt",
|
|
"/tmp/out/a/hi.txt"},
|
|
)
|
|
finder.Shutdown()
|
|
|
|
// modify filesystem
|
|
filesystem.Clock.Tick()
|
|
fs.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")
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
|
|
finder2.Shutdown()
|
|
}
|
|
|
|
func TestUpdatingDbIffChanged(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/a/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/bye.txt", filesystem)
|
|
|
|
// run the first finder
|
|
finder := newFinder(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{"/tmp"},
|
|
IncludeFiles: []string{"hi.txt"},
|
|
},
|
|
)
|
|
filesystem.Clock.Tick()
|
|
foundPaths := finder.FindAll()
|
|
finder.Shutdown()
|
|
// check results
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
|
|
|
|
// modify the filesystem
|
|
filesystem.Clock.Tick()
|
|
fs.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
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
|
|
expectedDbWriteTime := filesystem.Clock.Time()
|
|
actualDbWriteTime := fs.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
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
|
|
fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
|
|
finder3.Shutdown()
|
|
actualDbWriteTime = fs.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()
|
|
fs.Create(t, "/tmp/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/a/a/hi.txt", filesystem)
|
|
fs.Create(t, "/tmp/b/hi.txt", filesystem)
|
|
|
|
// run the first finder
|
|
finder := newFinder(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{"/tmp"},
|
|
IncludeFiles: []string{"hi.txt"},
|
|
},
|
|
)
|
|
filesystem.Clock.Tick()
|
|
foundPaths := finder.FindAll()
|
|
finder.Shutdown()
|
|
allPaths := []string{"/tmp/hi.txt", "/tmp/a/hi.txt", "/tmp/a/a/hi.txt", "/tmp/b/hi.txt"}
|
|
// check results
|
|
fs.AssertSameResponse(t, foundPaths, allPaths)
|
|
|
|
// modify the filesystem
|
|
filesystem.Clock.Tick()
|
|
|
|
fs.SetReadable(t, "/tmp/a", false, filesystem)
|
|
filesystem.Clock.Tick()
|
|
|
|
// run the second finder
|
|
finder2 := finderWithSameParams(t, finder)
|
|
foundPaths = finder2.FindAll()
|
|
finder2.Shutdown()
|
|
// check results
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt", "/tmp/b/hi.txt"})
|
|
|
|
// modify the filesystem back
|
|
fs.SetReadable(t, "/tmp/a", true, filesystem)
|
|
|
|
// run the third finder
|
|
finder3 := finderWithSameParams(t, finder2)
|
|
foundPaths = finder3.FindAll()
|
|
finder3.Shutdown()
|
|
// check results
|
|
fs.AssertSameResponse(t, foundPaths, allPaths)
|
|
}
|
|
|
|
func TestFileNotPermitted(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/hi.txt", filesystem)
|
|
fs.SetReadable(t, "/tmp/hi.txt", false, filesystem)
|
|
|
|
// run the first finder
|
|
finder := newFinder(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{"/tmp"},
|
|
IncludeFiles: []string{"hi.txt"},
|
|
},
|
|
)
|
|
filesystem.Clock.Tick()
|
|
foundPaths := finder.FindAll()
|
|
finder.Shutdown()
|
|
// check results
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
|
|
}
|
|
|
|
func TestCacheEntryPathUnexpectedError(t *testing.T) {
|
|
// setup filesystem
|
|
filesystem := newFs()
|
|
fs.Create(t, "/tmp/a/hi.txt", filesystem)
|
|
|
|
// run the first finder
|
|
finder := newFinder(
|
|
t,
|
|
filesystem,
|
|
CacheParams{
|
|
RootDirs: []string{"/tmp"},
|
|
IncludeFiles: []string{"hi.txt"},
|
|
},
|
|
)
|
|
filesystem.Clock.Tick()
|
|
foundPaths := finder.FindAll()
|
|
finder.Shutdown()
|
|
// check results
|
|
fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
|
|
|
|
// make the directory not readable
|
|
fs.SetReadErr(t, "/tmp/a", os.ErrInvalid, filesystem)
|
|
|
|
// run the second finder
|
|
_, err := finderAndErrorWithSameParams(t, finder)
|
|
if err == nil {
|
|
t.Fatal("Failed to detect unexpected filesystem error")
|
|
}
|
|
}
|