Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  finder_test.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// 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{12, 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"},
  },
 )
 finder.WaitForDbDump()
 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"},
  },
 )
 finder.WaitForDbDump()
 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"},
  },
 )
 finder.WaitForDbDump()
 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"},
  },
 )
 finder.WaitForDbDump()
 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"},
  },
 )
 finder.WaitForDbDump()
 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")
 }
}

[Dauer der Verarbeitung: 0.40 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik