finder_test.go 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532
  1. // Copyright 2017 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package finder
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "log"
  19. "os"
  20. "path/filepath"
  21. "sort"
  22. "testing"
  23. "android/soong/finder/fs"
  24. )
  25. // some utils for tests to use
  26. func newFs() *fs.MockFs {
  27. return fs.NewMockFs(map[string][]byte{})
  28. }
  29. func newFinder(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams) *Finder {
  30. return newFinderWithNumThreads(t, filesystem, cacheParams, 2)
  31. }
  32. func newFinderWithNumThreads(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams, numThreads int) *Finder {
  33. f, err := newFinderAndErr(t, filesystem, cacheParams, numThreads)
  34. if err != nil {
  35. t.Fatal(err.Error())
  36. }
  37. return f
  38. }
  39. func newFinderAndErr(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams, numThreads int) (*Finder, error) {
  40. cachePath := "/finder/finder-db"
  41. cacheDir := filepath.Dir(cachePath)
  42. filesystem.MkDirs(cacheDir)
  43. if cacheParams.WorkingDirectory == "" {
  44. cacheParams.WorkingDirectory = "/cwd"
  45. }
  46. logger := log.New(ioutil.Discard, "", 0)
  47. f, err := newImpl(cacheParams, filesystem, logger, cachePath, numThreads)
  48. return f, err
  49. }
  50. func finderWithSameParams(t *testing.T, original *Finder) *Finder {
  51. f, err := finderAndErrorWithSameParams(t, original)
  52. if err != nil {
  53. t.Fatal(err.Error())
  54. }
  55. return f
  56. }
  57. func finderAndErrorWithSameParams(t *testing.T, original *Finder) (*Finder, error) {
  58. f, err := newImpl(
  59. original.cacheMetadata.Config.CacheParams,
  60. original.filesystem,
  61. original.logger,
  62. original.DbPath,
  63. original.numDbLoadingThreads,
  64. )
  65. return f, err
  66. }
  67. // runSimpleTests creates a few files, searches for findme.txt, and checks for the expected matches
  68. func runSimpleTest(t *testing.T, existentPaths []string, expectedMatches []string) {
  69. filesystem := newFs()
  70. root := "/tmp"
  71. filesystem.MkDirs(root)
  72. for _, path := range existentPaths {
  73. fs.Create(t, filepath.Join(root, path), filesystem)
  74. }
  75. finder := newFinder(t,
  76. filesystem,
  77. CacheParams{
  78. "/cwd",
  79. []string{root},
  80. nil,
  81. nil,
  82. []string{"findme.txt", "skipme.txt"},
  83. },
  84. )
  85. defer finder.Shutdown()
  86. foundPaths := finder.FindNamedAt(root, "findme.txt")
  87. absoluteMatches := []string{}
  88. for i := range expectedMatches {
  89. absoluteMatches = append(absoluteMatches, filepath.Join(root, expectedMatches[i]))
  90. }
  91. fs.AssertSameResponse(t, foundPaths, absoluteMatches)
  92. }
  93. // testAgainstSeveralThreadcounts runs the given test for each threadcount that we care to test
  94. func testAgainstSeveralThreadcounts(t *testing.T, tester func(t *testing.T, numThreads int)) {
  95. // test singlethreaded, multithreaded, and also using the same number of threads as
  96. // will be used on the current system
  97. threadCounts := []int{1, 2, defaultNumThreads}
  98. for _, numThreads := range threadCounts {
  99. testName := fmt.Sprintf("%v threads", numThreads)
  100. // store numThreads in a new variable to prevent numThreads from changing in each loop
  101. localNumThreads := numThreads
  102. t.Run(testName, func(t *testing.T) {
  103. tester(t, localNumThreads)
  104. })
  105. }
  106. }
  107. // end of utils, start of individual tests
  108. func TestSingleFile(t *testing.T) {
  109. runSimpleTest(t,
  110. []string{"findme.txt"},
  111. []string{"findme.txt"},
  112. )
  113. }
  114. func TestIncludeFiles(t *testing.T) {
  115. runSimpleTest(t,
  116. []string{"findme.txt", "skipme.txt"},
  117. []string{"findme.txt"},
  118. )
  119. }
  120. func TestNestedDirectories(t *testing.T) {
  121. runSimpleTest(t,
  122. []string{"findme.txt", "skipme.txt", "subdir/findme.txt", "subdir/skipme.txt"},
  123. []string{"findme.txt", "subdir/findme.txt"},
  124. )
  125. }
  126. func TestEmptyDirectory(t *testing.T) {
  127. runSimpleTest(t,
  128. []string{},
  129. []string{},
  130. )
  131. }
  132. func TestEmptyPath(t *testing.T) {
  133. filesystem := newFs()
  134. root := "/tmp"
  135. fs.Create(t, filepath.Join(root, "findme.txt"), filesystem)
  136. finder := newFinder(
  137. t,
  138. filesystem,
  139. CacheParams{
  140. RootDirs: []string{root},
  141. IncludeFiles: []string{"findme.txt", "skipme.txt"},
  142. },
  143. )
  144. defer finder.Shutdown()
  145. foundPaths := finder.FindNamedAt("", "findme.txt")
  146. fs.AssertSameResponse(t, foundPaths, []string{})
  147. }
  148. func TestFilesystemRoot(t *testing.T) {
  149. testWithNumThreads := func(t *testing.T, numThreads int) {
  150. filesystem := newFs()
  151. root := "/"
  152. createdPath := "/findme.txt"
  153. fs.Create(t, createdPath, filesystem)
  154. finder := newFinderWithNumThreads(
  155. t,
  156. filesystem,
  157. CacheParams{
  158. RootDirs: []string{root},
  159. IncludeFiles: []string{"findme.txt", "skipme.txt"},
  160. },
  161. numThreads,
  162. )
  163. defer finder.Shutdown()
  164. foundPaths := finder.FindNamedAt(root, "findme.txt")
  165. fs.AssertSameResponse(t, foundPaths, []string{createdPath})
  166. }
  167. testAgainstSeveralThreadcounts(t, testWithNumThreads)
  168. }
  169. func TestNonexistentDir(t *testing.T) {
  170. filesystem := newFs()
  171. fs.Create(t, "/tmp/findme.txt", filesystem)
  172. _, err := newFinderAndErr(
  173. t,
  174. filesystem,
  175. CacheParams{
  176. RootDirs: []string{"/tmp/IDontExist"},
  177. IncludeFiles: []string{"findme.txt", "skipme.txt"},
  178. },
  179. 1,
  180. )
  181. if err == nil {
  182. t.Fatal("Did not fail when given a nonexistent root directory")
  183. }
  184. }
  185. func TestExcludeDirs(t *testing.T) {
  186. filesystem := newFs()
  187. fs.Create(t, "/tmp/exclude/findme.txt", filesystem)
  188. fs.Create(t, "/tmp/exclude/subdir/findme.txt", filesystem)
  189. fs.Create(t, "/tmp/subdir/exclude/findme.txt", filesystem)
  190. fs.Create(t, "/tmp/subdir/subdir/findme.txt", filesystem)
  191. fs.Create(t, "/tmp/subdir/findme.txt", filesystem)
  192. fs.Create(t, "/tmp/findme.txt", filesystem)
  193. finder := newFinder(
  194. t,
  195. filesystem,
  196. CacheParams{
  197. RootDirs: []string{"/tmp"},
  198. ExcludeDirs: []string{"exclude"},
  199. IncludeFiles: []string{"findme.txt", "skipme.txt"},
  200. },
  201. )
  202. defer finder.Shutdown()
  203. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  204. fs.AssertSameResponse(t, foundPaths,
  205. []string{"/tmp/findme.txt",
  206. "/tmp/subdir/findme.txt",
  207. "/tmp/subdir/subdir/findme.txt"})
  208. }
  209. func TestPruneFiles(t *testing.T) {
  210. filesystem := newFs()
  211. fs.Create(t, "/tmp/out/findme.txt", filesystem)
  212. fs.Create(t, "/tmp/out/.ignore-out-dir", filesystem)
  213. fs.Create(t, "/tmp/out/child/findme.txt", filesystem)
  214. fs.Create(t, "/tmp/out2/.ignore-out-dir", filesystem)
  215. fs.Create(t, "/tmp/out2/sub/findme.txt", filesystem)
  216. fs.Create(t, "/tmp/findme.txt", filesystem)
  217. fs.Create(t, "/tmp/include/findme.txt", filesystem)
  218. finder := newFinder(
  219. t,
  220. filesystem,
  221. CacheParams{
  222. RootDirs: []string{"/tmp"},
  223. PruneFiles: []string{".ignore-out-dir"},
  224. IncludeFiles: []string{"findme.txt"},
  225. },
  226. )
  227. defer finder.Shutdown()
  228. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  229. fs.AssertSameResponse(t, foundPaths,
  230. []string{"/tmp/findme.txt",
  231. "/tmp/include/findme.txt"})
  232. }
  233. // TestRootDir tests that the value of RootDirs is used
  234. // tests of the filesystem root are in TestFilesystemRoot
  235. func TestRootDir(t *testing.T) {
  236. filesystem := newFs()
  237. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  238. fs.Create(t, "/tmp/a/subdir/findme.txt", filesystem)
  239. fs.Create(t, "/tmp/b/findme.txt", filesystem)
  240. fs.Create(t, "/tmp/b/subdir/findme.txt", filesystem)
  241. finder := newFinder(
  242. t,
  243. filesystem,
  244. CacheParams{
  245. RootDirs: []string{"/tmp/a"},
  246. IncludeFiles: []string{"findme.txt"},
  247. },
  248. )
  249. defer finder.Shutdown()
  250. foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
  251. fs.AssertSameResponse(t, foundPaths,
  252. []string{"/tmp/a/findme.txt",
  253. "/tmp/a/subdir/findme.txt"})
  254. }
  255. func TestUncachedDir(t *testing.T) {
  256. filesystem := newFs()
  257. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  258. fs.Create(t, "/tmp/a/subdir/findme.txt", filesystem)
  259. fs.Create(t, "/tmp/b/findme.txt", filesystem)
  260. fs.Create(t, "/tmp/b/subdir/findme.txt", filesystem)
  261. finder := newFinder(
  262. t,
  263. filesystem,
  264. CacheParams{
  265. RootDirs: []string{"/tmp/b"},
  266. IncludeFiles: []string{"findme.txt"},
  267. },
  268. )
  269. foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
  270. // If the caller queries for a file that is in the cache, then computing the
  271. // correct answer won't be fast, and it would be easy for the caller to
  272. // fail to notice its slowness. Instead, we only ever search the cache for files
  273. // to return, which enforces that we can determine which files will be
  274. // interesting upfront.
  275. fs.AssertSameResponse(t, foundPaths, []string{})
  276. finder.Shutdown()
  277. }
  278. func TestSearchingForFilesExcludedFromCache(t *testing.T) {
  279. // setup filesystem
  280. filesystem := newFs()
  281. fs.Create(t, "/tmp/findme.txt", filesystem)
  282. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  283. fs.Create(t, "/tmp/a/misc.txt", filesystem)
  284. // set up the finder and run it
  285. finder := newFinder(
  286. t,
  287. filesystem,
  288. CacheParams{
  289. RootDirs: []string{"/tmp"},
  290. IncludeFiles: []string{"findme.txt"},
  291. },
  292. )
  293. foundPaths := finder.FindNamedAt("/tmp", "misc.txt")
  294. // If the caller queries for a file that is in the cache, then computing the
  295. // correct answer won't be fast, and it would be easy for the caller to
  296. // fail to notice its slowness. Instead, we only ever search the cache for files
  297. // to return, which enforces that we can determine which files will be
  298. // interesting upfront.
  299. fs.AssertSameResponse(t, foundPaths, []string{})
  300. finder.Shutdown()
  301. }
  302. func TestRelativeFilePaths(t *testing.T) {
  303. filesystem := newFs()
  304. fs.Create(t, "/tmp/ignore/hi.txt", filesystem)
  305. fs.Create(t, "/tmp/include/hi.txt", filesystem)
  306. fs.Create(t, "/cwd/hi.txt", filesystem)
  307. fs.Create(t, "/cwd/a/hi.txt", filesystem)
  308. fs.Create(t, "/cwd/a/a/hi.txt", filesystem)
  309. fs.Create(t, "/rel/a/hi.txt", filesystem)
  310. finder := newFinder(
  311. t,
  312. filesystem,
  313. CacheParams{
  314. RootDirs: []string{"/cwd", "../rel", "/tmp/include"},
  315. IncludeFiles: []string{"hi.txt"},
  316. },
  317. )
  318. defer finder.Shutdown()
  319. foundPaths := finder.FindNamedAt("a", "hi.txt")
  320. fs.AssertSameResponse(t, foundPaths,
  321. []string{"a/hi.txt",
  322. "a/a/hi.txt"})
  323. foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
  324. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
  325. foundPaths = finder.FindNamedAt(".", "hi.txt")
  326. fs.AssertSameResponse(t, foundPaths,
  327. []string{"hi.txt",
  328. "a/hi.txt",
  329. "a/a/hi.txt"})
  330. foundPaths = finder.FindNamedAt("/rel", "hi.txt")
  331. fs.AssertSameResponse(t, foundPaths,
  332. []string{"/rel/a/hi.txt"})
  333. foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
  334. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
  335. }
  336. // have to run this test with the race-detector (`go test -race src/android/soong/finder/*.go`)
  337. // for there to be much chance of the test actually detecting any error that may be present
  338. func TestRootDirsContainedInOtherRootDirs(t *testing.T) {
  339. filesystem := newFs()
  340. fs.Create(t, "/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt", filesystem)
  341. finder := newFinder(
  342. t,
  343. filesystem,
  344. CacheParams{
  345. RootDirs: []string{"/", "/tmp/a/b/c", "/tmp/a/b/c/d/e/f", "/tmp/a/b/c/d/e/f/g/h/i"},
  346. IncludeFiles: []string{"findme.txt"},
  347. },
  348. )
  349. defer finder.Shutdown()
  350. foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
  351. fs.AssertSameResponse(t, foundPaths,
  352. []string{"/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt"})
  353. }
  354. func TestFindFirst(t *testing.T) {
  355. filesystem := newFs()
  356. fs.Create(t, "/tmp/a/hi.txt", filesystem)
  357. fs.Create(t, "/tmp/b/hi.txt", filesystem)
  358. fs.Create(t, "/tmp/b/a/hi.txt", filesystem)
  359. finder := newFinder(
  360. t,
  361. filesystem,
  362. CacheParams{
  363. RootDirs: []string{"/tmp"},
  364. IncludeFiles: []string{"hi.txt"},
  365. },
  366. )
  367. defer finder.Shutdown()
  368. foundPaths := finder.FindFirstNamed("hi.txt")
  369. fs.AssertSameResponse(t, foundPaths,
  370. []string{"/tmp/a/hi.txt",
  371. "/tmp/b/hi.txt"},
  372. )
  373. }
  374. func TestConcurrentFindSameDirectory(t *testing.T) {
  375. testWithNumThreads := func(t *testing.T, numThreads int) {
  376. filesystem := newFs()
  377. // create a bunch of files and directories
  378. paths := []string{}
  379. for i := 0; i < 10; i++ {
  380. parentDir := fmt.Sprintf("/tmp/%v", i)
  381. for j := 0; j < 10; j++ {
  382. filePath := filepath.Join(parentDir, fmt.Sprintf("%v/findme.txt", j))
  383. paths = append(paths, filePath)
  384. }
  385. }
  386. sort.Strings(paths)
  387. for _, path := range paths {
  388. fs.Create(t, path, filesystem)
  389. }
  390. // set up a finder
  391. finder := newFinderWithNumThreads(
  392. t,
  393. filesystem,
  394. CacheParams{
  395. RootDirs: []string{"/tmp"},
  396. IncludeFiles: []string{"findme.txt"},
  397. },
  398. numThreads,
  399. )
  400. defer finder.Shutdown()
  401. numTests := 20
  402. results := make(chan []string, numTests)
  403. // make several parallel calls to the finder
  404. for i := 0; i < numTests; i++ {
  405. go func() {
  406. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  407. results <- foundPaths
  408. }()
  409. }
  410. // check that each response was correct
  411. for i := 0; i < numTests; i++ {
  412. foundPaths := <-results
  413. fs.AssertSameResponse(t, foundPaths, paths)
  414. }
  415. }
  416. testAgainstSeveralThreadcounts(t, testWithNumThreads)
  417. }
  418. func TestConcurrentFindDifferentDirectories(t *testing.T) {
  419. filesystem := newFs()
  420. // create a bunch of files and directories
  421. allFiles := []string{}
  422. numSubdirs := 10
  423. rootPaths := []string{}
  424. queryAnswers := [][]string{}
  425. for i := 0; i < numSubdirs; i++ {
  426. parentDir := fmt.Sprintf("/tmp/%v", i)
  427. rootPaths = append(rootPaths, parentDir)
  428. queryAnswers = append(queryAnswers, []string{})
  429. for j := 0; j < 10; j++ {
  430. filePath := filepath.Join(parentDir, fmt.Sprintf("%v/findme.txt", j))
  431. queryAnswers[i] = append(queryAnswers[i], filePath)
  432. allFiles = append(allFiles, filePath)
  433. }
  434. sort.Strings(queryAnswers[i])
  435. }
  436. sort.Strings(allFiles)
  437. for _, path := range allFiles {
  438. fs.Create(t, path, filesystem)
  439. }
  440. // set up a finder
  441. finder := newFinder(
  442. t,
  443. filesystem,
  444. CacheParams{
  445. RootDirs: []string{"/tmp"},
  446. IncludeFiles: []string{"findme.txt"},
  447. },
  448. )
  449. defer finder.Shutdown()
  450. type testRun struct {
  451. path string
  452. foundMatches []string
  453. correctMatches []string
  454. }
  455. numTests := numSubdirs + 1
  456. testRuns := make(chan testRun, numTests)
  457. searchAt := func(path string, correctMatches []string) {
  458. foundPaths := finder.FindNamedAt(path, "findme.txt")
  459. testRuns <- testRun{path, foundPaths, correctMatches}
  460. }
  461. // make several parallel calls to the finder
  462. go searchAt("/tmp", allFiles)
  463. for i := 0; i < len(rootPaths); i++ {
  464. go searchAt(rootPaths[i], queryAnswers[i])
  465. }
  466. // check that each response was correct
  467. for i := 0; i < numTests; i++ {
  468. testRun := <-testRuns
  469. fs.AssertSameResponse(t, testRun.foundMatches, testRun.correctMatches)
  470. }
  471. }
  472. func TestStrangelyFormattedPaths(t *testing.T) {
  473. filesystem := newFs()
  474. fs.Create(t, "/tmp/findme.txt", filesystem)
  475. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  476. fs.Create(t, "/tmp/b/findme.txt", filesystem)
  477. finder := newFinder(
  478. t,
  479. filesystem,
  480. CacheParams{
  481. RootDirs: []string{"//tmp//a//.."},
  482. IncludeFiles: []string{"findme.txt"},
  483. },
  484. )
  485. defer finder.Shutdown()
  486. foundPaths := finder.FindNamedAt("//tmp//a//..", "findme.txt")
  487. fs.AssertSameResponse(t, foundPaths,
  488. []string{"/tmp/a/findme.txt",
  489. "/tmp/b/findme.txt",
  490. "/tmp/findme.txt"})
  491. }
  492. func TestCorruptedCacheHeader(t *testing.T) {
  493. filesystem := newFs()
  494. fs.Create(t, "/tmp/findme.txt", filesystem)
  495. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  496. fs.Write(t, "/finder/finder-db", "sample header", filesystem)
  497. finder := newFinder(
  498. t,
  499. filesystem,
  500. CacheParams{
  501. RootDirs: []string{"/tmp"},
  502. IncludeFiles: []string{"findme.txt"},
  503. },
  504. )
  505. defer finder.Shutdown()
  506. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  507. fs.AssertSameResponse(t, foundPaths,
  508. []string{"/tmp/a/findme.txt",
  509. "/tmp/findme.txt"})
  510. }
  511. func TestCanUseCache(t *testing.T) {
  512. // setup filesystem
  513. filesystem := newFs()
  514. fs.Create(t, "/tmp/findme.txt", filesystem)
  515. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  516. // run the first finder
  517. finder := newFinder(
  518. t,
  519. filesystem,
  520. CacheParams{
  521. RootDirs: []string{"/tmp"},
  522. IncludeFiles: []string{"findme.txt"},
  523. },
  524. )
  525. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  526. // check the response of the first finder
  527. correctResponse := []string{"/tmp/a/findme.txt",
  528. "/tmp/findme.txt"}
  529. fs.AssertSameResponse(t, foundPaths, correctResponse)
  530. finder.Shutdown()
  531. // check results
  532. cacheText := fs.Read(t, finder.DbPath, filesystem)
  533. if len(cacheText) < 1 {
  534. t.Fatalf("saved cache db is empty\n")
  535. }
  536. if len(filesystem.StatCalls) == 0 {
  537. t.Fatal("No Stat calls recorded by mock filesystem")
  538. }
  539. if len(filesystem.ReadDirCalls) == 0 {
  540. t.Fatal("No ReadDir calls recorded by filesystem")
  541. }
  542. statCalls := filesystem.StatCalls
  543. filesystem.ClearMetrics()
  544. // run the second finder
  545. finder2 := finderWithSameParams(t, finder)
  546. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  547. // check results
  548. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
  549. fs.AssertSameReadDirCalls(t, filesystem.StatCalls, statCalls)
  550. finder2.Shutdown()
  551. }
  552. func TestCorruptedCacheBody(t *testing.T) {
  553. // setup filesystem
  554. filesystem := newFs()
  555. fs.Create(t, "/tmp/findme.txt", filesystem)
  556. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  557. // run the first finder
  558. finder := newFinder(
  559. t,
  560. filesystem,
  561. CacheParams{
  562. RootDirs: []string{"/tmp"},
  563. IncludeFiles: []string{"findme.txt"},
  564. },
  565. )
  566. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  567. finder.Shutdown()
  568. // check the response of the first finder
  569. correctResponse := []string{"/tmp/a/findme.txt",
  570. "/tmp/findme.txt"}
  571. fs.AssertSameResponse(t, foundPaths, correctResponse)
  572. numStatCalls := len(filesystem.StatCalls)
  573. numReadDirCalls := len(filesystem.ReadDirCalls)
  574. // load the cache file, corrupt it, and save it
  575. cacheReader, err := filesystem.Open(finder.DbPath)
  576. if err != nil {
  577. t.Fatal(err)
  578. }
  579. cacheData, err := ioutil.ReadAll(cacheReader)
  580. if err != nil {
  581. t.Fatal(err)
  582. }
  583. cacheData = append(cacheData, []byte("DontMindMe")...)
  584. filesystem.WriteFile(finder.DbPath, cacheData, 0777)
  585. filesystem.ClearMetrics()
  586. // run the second finder
  587. finder2 := finderWithSameParams(t, finder)
  588. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  589. // check results
  590. fs.AssertSameResponse(t, foundPaths, correctResponse)
  591. numNewStatCalls := len(filesystem.StatCalls)
  592. numNewReadDirCalls := len(filesystem.ReadDirCalls)
  593. // It's permissable to make more Stat calls with a corrupted cache because
  594. // the Finder may restart once it detects corruption.
  595. // However, it may have already issued many Stat calls.
  596. // Because a corrupted db is not expected to be a common (or even a supported case),
  597. // we don't care to optimize it and don't cache the already-issued Stat calls
  598. if numNewReadDirCalls < numReadDirCalls {
  599. t.Fatalf(
  600. "Finder made fewer ReadDir calls with a corrupted cache (%v calls) than with no cache"+
  601. " (%v calls)",
  602. numNewReadDirCalls, numReadDirCalls)
  603. }
  604. if numNewStatCalls < numStatCalls {
  605. t.Fatalf(
  606. "Finder made fewer Stat calls with a corrupted cache (%v calls) than with no cache (%v calls)",
  607. numNewStatCalls, numStatCalls)
  608. }
  609. finder2.Shutdown()
  610. }
  611. func TestStatCalls(t *testing.T) {
  612. // setup filesystem
  613. filesystem := newFs()
  614. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  615. // run finder
  616. finder := newFinder(
  617. t,
  618. filesystem,
  619. CacheParams{
  620. RootDirs: []string{"/tmp"},
  621. IncludeFiles: []string{"findme.txt"},
  622. },
  623. )
  624. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  625. finder.Shutdown()
  626. // check response
  627. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
  628. fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a"})
  629. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
  630. }
  631. func TestFileAdded(t *testing.T) {
  632. // setup filesystem
  633. filesystem := newFs()
  634. fs.Create(t, "/tmp/ignoreme.txt", filesystem)
  635. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  636. fs.Create(t, "/tmp/b/ignore.txt", filesystem)
  637. fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
  638. fs.Create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
  639. // run the first finder
  640. finder := newFinder(
  641. t,
  642. filesystem,
  643. CacheParams{
  644. RootDirs: []string{"/tmp"},
  645. IncludeFiles: []string{"findme.txt"},
  646. },
  647. )
  648. filesystem.Clock.Tick()
  649. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  650. finder.Shutdown()
  651. // check the response of the first finder
  652. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
  653. // modify the filesystem
  654. filesystem.Clock.Tick()
  655. fs.Create(t, "/tmp/b/c/findme.txt", filesystem)
  656. filesystem.Clock.Tick()
  657. filesystem.ClearMetrics()
  658. // run the second finder
  659. finder2 := finderWithSameParams(t, finder)
  660. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  661. // check results
  662. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/c/findme.txt"})
  663. fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
  664. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c"})
  665. finder2.Shutdown()
  666. }
  667. func TestDirectoriesAdded(t *testing.T) {
  668. // setup filesystem
  669. filesystem := newFs()
  670. fs.Create(t, "/tmp/ignoreme.txt", filesystem)
  671. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  672. fs.Create(t, "/tmp/b/ignore.txt", filesystem)
  673. fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
  674. fs.Create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
  675. // run the first finder
  676. finder := newFinder(
  677. t,
  678. filesystem,
  679. CacheParams{
  680. RootDirs: []string{"/tmp"},
  681. IncludeFiles: []string{"findme.txt"},
  682. },
  683. )
  684. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  685. finder.Shutdown()
  686. // check the response of the first finder
  687. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
  688. // modify the filesystem
  689. filesystem.Clock.Tick()
  690. fs.Create(t, "/tmp/b/c/new/findme.txt", filesystem)
  691. fs.Create(t, "/tmp/b/c/new/new2/findme.txt", filesystem)
  692. fs.Create(t, "/tmp/b/c/new/new2/ignoreme.txt", filesystem)
  693. filesystem.ClearMetrics()
  694. // run the second finder
  695. finder2 := finderWithSameParams(t, finder)
  696. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  697. // check results
  698. fs.AssertSameResponse(t, foundPaths,
  699. []string{"/tmp/a/findme.txt", "/tmp/b/c/new/findme.txt", "/tmp/b/c/new/new2/findme.txt"})
  700. fs.AssertSameStatCalls(t, filesystem.StatCalls,
  701. []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
  702. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
  703. finder2.Shutdown()
  704. }
  705. func TestDirectoryAndSubdirectoryBothUpdated(t *testing.T) {
  706. // setup filesystem
  707. filesystem := newFs()
  708. fs.Create(t, "/tmp/hi1.txt", filesystem)
  709. fs.Create(t, "/tmp/a/hi1.txt", filesystem)
  710. // run the first finder
  711. finder := newFinder(
  712. t,
  713. filesystem,
  714. CacheParams{
  715. RootDirs: []string{"/tmp"},
  716. IncludeFiles: []string{"hi1.txt", "hi2.txt"},
  717. },
  718. )
  719. foundPaths := finder.FindNamedAt("/tmp", "hi1.txt")
  720. finder.Shutdown()
  721. // check the response of the first finder
  722. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi1.txt", "/tmp/a/hi1.txt"})
  723. // modify the filesystem
  724. filesystem.Clock.Tick()
  725. fs.Create(t, "/tmp/hi2.txt", filesystem)
  726. fs.Create(t, "/tmp/a/hi2.txt", filesystem)
  727. filesystem.ClearMetrics()
  728. // run the second finder
  729. finder2 := finderWithSameParams(t, finder)
  730. foundPaths = finder2.FindAll()
  731. // check results
  732. fs.AssertSameResponse(t, foundPaths,
  733. []string{"/tmp/hi1.txt", "/tmp/hi2.txt", "/tmp/a/hi1.txt", "/tmp/a/hi2.txt"})
  734. fs.AssertSameStatCalls(t, filesystem.StatCalls,
  735. []string{"/tmp", "/tmp/a"})
  736. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
  737. finder2.Shutdown()
  738. }
  739. func TestFileDeleted(t *testing.T) {
  740. // setup filesystem
  741. filesystem := newFs()
  742. fs.Create(t, "/tmp/ignoreme.txt", filesystem)
  743. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  744. fs.Create(t, "/tmp/b/findme.txt", filesystem)
  745. fs.Create(t, "/tmp/b/c/nope.txt", filesystem)
  746. fs.Create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
  747. // run the first finder
  748. finder := newFinder(
  749. t,
  750. filesystem,
  751. CacheParams{
  752. RootDirs: []string{"/tmp"},
  753. IncludeFiles: []string{"findme.txt"},
  754. },
  755. )
  756. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  757. finder.Shutdown()
  758. // check the response of the first finder
  759. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/findme.txt"})
  760. // modify the filesystem
  761. filesystem.Clock.Tick()
  762. fs.Delete(t, "/tmp/b/findme.txt", filesystem)
  763. filesystem.ClearMetrics()
  764. // run the second finder
  765. finder2 := finderWithSameParams(t, finder)
  766. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  767. // check results
  768. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
  769. fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
  770. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
  771. finder2.Shutdown()
  772. }
  773. func TestDirectoriesDeleted(t *testing.T) {
  774. // setup filesystem
  775. filesystem := newFs()
  776. fs.Create(t, "/tmp/findme.txt", filesystem)
  777. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  778. fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
  779. fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
  780. fs.Create(t, "/tmp/b/findme.txt", filesystem)
  781. // run the first finder
  782. finder := newFinder(
  783. t,
  784. filesystem,
  785. CacheParams{
  786. RootDirs: []string{"/tmp"},
  787. IncludeFiles: []string{"findme.txt"},
  788. },
  789. )
  790. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  791. finder.Shutdown()
  792. // check the response of the first finder
  793. fs.AssertSameResponse(t, foundPaths,
  794. []string{"/tmp/findme.txt",
  795. "/tmp/a/findme.txt",
  796. "/tmp/a/1/findme.txt",
  797. "/tmp/a/1/2/findme.txt",
  798. "/tmp/b/findme.txt"})
  799. // modify the filesystem
  800. filesystem.Clock.Tick()
  801. fs.RemoveAll(t, "/tmp/a/1", filesystem)
  802. filesystem.ClearMetrics()
  803. // run the second finder
  804. finder2 := finderWithSameParams(t, finder)
  805. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  806. // check results
  807. fs.AssertSameResponse(t, foundPaths,
  808. []string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/b/findme.txt"})
  809. // Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
  810. // if the Finder detects the nonexistence of /tmp/a/1
  811. // However, when resuming from cache, we don't want the Finder to necessarily wait
  812. // to stat a directory until after statting its parent.
  813. // So here we just include /tmp/a/1/2 in the list.
  814. // The Finder is currently implemented to always restat every dir and
  815. // to not short-circuit due to nonexistence of parents (but it will remove
  816. // missing dirs from the cache for next time)
  817. fs.AssertSameStatCalls(t, filesystem.StatCalls,
  818. []string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b"})
  819. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/a"})
  820. finder2.Shutdown()
  821. }
  822. func TestDirectoriesMoved(t *testing.T) {
  823. // setup filesystem
  824. filesystem := newFs()
  825. fs.Create(t, "/tmp/findme.txt", filesystem)
  826. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  827. fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
  828. fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
  829. fs.Create(t, "/tmp/b/findme.txt", filesystem)
  830. // run the first finder
  831. finder := newFinder(
  832. t,
  833. filesystem,
  834. CacheParams{
  835. RootDirs: []string{"/tmp"},
  836. IncludeFiles: []string{"findme.txt"},
  837. },
  838. )
  839. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  840. finder.Shutdown()
  841. // check the response of the first finder
  842. fs.AssertSameResponse(t, foundPaths,
  843. []string{"/tmp/findme.txt",
  844. "/tmp/a/findme.txt",
  845. "/tmp/a/1/findme.txt",
  846. "/tmp/a/1/2/findme.txt",
  847. "/tmp/b/findme.txt"})
  848. // modify the filesystem
  849. filesystem.Clock.Tick()
  850. fs.Move(t, "/tmp/a", "/tmp/c", filesystem)
  851. filesystem.ClearMetrics()
  852. // run the second finder
  853. finder2 := finderWithSameParams(t, finder)
  854. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  855. // check results
  856. fs.AssertSameResponse(t, foundPaths,
  857. []string{"/tmp/findme.txt",
  858. "/tmp/b/findme.txt",
  859. "/tmp/c/findme.txt",
  860. "/tmp/c/1/findme.txt",
  861. "/tmp/c/1/2/findme.txt"})
  862. // Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
  863. // if the Finder detects the nonexistence of /tmp/a/1
  864. // However, when resuming from cache, we don't want the Finder to necessarily wait
  865. // to stat a directory until after statting its parent.
  866. // So here we just include /tmp/a/1/2 in the list.
  867. // The Finder is currently implemented to always restat every dir and
  868. // to not short-circuit due to nonexistence of parents (but it will remove
  869. // missing dirs from the cache for next time)
  870. fs.AssertSameStatCalls(t, filesystem.StatCalls,
  871. []string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
  872. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
  873. finder2.Shutdown()
  874. }
  875. func TestDirectoriesSwapped(t *testing.T) {
  876. // setup filesystem
  877. filesystem := newFs()
  878. fs.Create(t, "/tmp/findme.txt", filesystem)
  879. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  880. fs.Create(t, "/tmp/a/1/findme.txt", filesystem)
  881. fs.Create(t, "/tmp/a/1/2/findme.txt", filesystem)
  882. fs.Create(t, "/tmp/b/findme.txt", filesystem)
  883. // run the first finder
  884. finder := newFinder(
  885. t,
  886. filesystem,
  887. CacheParams{
  888. RootDirs: []string{"/tmp"},
  889. IncludeFiles: []string{"findme.txt"},
  890. },
  891. )
  892. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  893. finder.Shutdown()
  894. // check the response of the first finder
  895. fs.AssertSameResponse(t, foundPaths,
  896. []string{"/tmp/findme.txt",
  897. "/tmp/a/findme.txt",
  898. "/tmp/a/1/findme.txt",
  899. "/tmp/a/1/2/findme.txt",
  900. "/tmp/b/findme.txt"})
  901. // modify the filesystem
  902. filesystem.Clock.Tick()
  903. fs.Move(t, "/tmp/a", "/tmp/temp", filesystem)
  904. fs.Move(t, "/tmp/b", "/tmp/a", filesystem)
  905. fs.Move(t, "/tmp/temp", "/tmp/b", filesystem)
  906. filesystem.ClearMetrics()
  907. // run the second finder
  908. finder2 := finderWithSameParams(t, finder)
  909. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  910. // check results
  911. fs.AssertSameResponse(t, foundPaths,
  912. []string{"/tmp/findme.txt",
  913. "/tmp/a/findme.txt",
  914. "/tmp/b/findme.txt",
  915. "/tmp/b/1/findme.txt",
  916. "/tmp/b/1/2/findme.txt"})
  917. // Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
  918. // if the Finder detects the nonexistence of /tmp/a/1
  919. // However, when resuming from cache, we don't want the Finder to necessarily wait
  920. // to stat a directory until after statting its parent.
  921. // So here we just include /tmp/a/1/2 in the list.
  922. // The Finder is currently implemented to always restat every dir and
  923. // to not short-circuit due to nonexistence of parents (but it will remove
  924. // missing dirs from the cache for next time)
  925. fs.AssertSameStatCalls(t, filesystem.StatCalls,
  926. []string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
  927. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
  928. finder2.Shutdown()
  929. }
  930. // runFsReplacementTest tests a change modifying properties of the filesystem itself:
  931. // runFsReplacementTest tests changing the user, the hostname, or the device number
  932. // runFsReplacementTest is a helper method called by other tests
  933. func runFsReplacementTest(t *testing.T, fs1 *fs.MockFs, fs2 *fs.MockFs) {
  934. // setup fs1
  935. fs.Create(t, "/tmp/findme.txt", fs1)
  936. fs.Create(t, "/tmp/a/findme.txt", fs1)
  937. fs.Create(t, "/tmp/a/a/findme.txt", fs1)
  938. // setup fs2 to have the same directories but different files
  939. fs.Create(t, "/tmp/findme.txt", fs2)
  940. fs.Create(t, "/tmp/a/findme.txt", fs2)
  941. fs.Create(t, "/tmp/a/a/ignoreme.txt", fs2)
  942. fs.Create(t, "/tmp/a/b/findme.txt", fs2)
  943. // run the first finder
  944. finder := newFinder(
  945. t,
  946. fs1,
  947. CacheParams{
  948. RootDirs: []string{"/tmp"},
  949. IncludeFiles: []string{"findme.txt"},
  950. },
  951. )
  952. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  953. finder.Shutdown()
  954. // check the response of the first finder
  955. fs.AssertSameResponse(t, foundPaths,
  956. []string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/a/findme.txt"})
  957. // copy the cache data from the first filesystem to the second
  958. cacheContent := fs.Read(t, finder.DbPath, fs1)
  959. fs.Write(t, finder.DbPath, cacheContent, fs2)
  960. // run the second finder, with the same config and same cache contents but a different filesystem
  961. finder2 := newFinder(
  962. t,
  963. fs2,
  964. CacheParams{
  965. RootDirs: []string{"/tmp"},
  966. IncludeFiles: []string{"findme.txt"},
  967. },
  968. )
  969. foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
  970. // check results
  971. fs.AssertSameResponse(t, foundPaths,
  972. []string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/b/findme.txt"})
  973. fs.AssertSameStatCalls(t, fs2.StatCalls,
  974. []string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
  975. fs.AssertSameReadDirCalls(t, fs2.ReadDirCalls,
  976. []string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
  977. finder2.Shutdown()
  978. }
  979. func TestChangeOfDevice(t *testing.T) {
  980. fs1 := newFs()
  981. // not as fine-grained mounting controls as a real filesystem, but should be adequate
  982. fs1.SetDeviceNumber(0)
  983. fs2 := newFs()
  984. fs2.SetDeviceNumber(1)
  985. runFsReplacementTest(t, fs1, fs2)
  986. }
  987. func TestChangeOfUserOrHost(t *testing.T) {
  988. fs1 := newFs()
  989. fs1.SetViewId("me@here")
  990. fs2 := newFs()
  991. fs2.SetViewId("you@there")
  992. runFsReplacementTest(t, fs1, fs2)
  993. }
  994. func TestConsistentCacheOrdering(t *testing.T) {
  995. // setup filesystem
  996. filesystem := newFs()
  997. for i := 0; i < 5; i++ {
  998. fs.Create(t, fmt.Sprintf("/tmp/%v/findme.txt", i), filesystem)
  999. }
  1000. // run the first finder
  1001. finder := newFinder(
  1002. t,
  1003. filesystem,
  1004. CacheParams{
  1005. RootDirs: []string{"/tmp"},
  1006. IncludeFiles: []string{"findme.txt"},
  1007. },
  1008. )
  1009. finder.FindNamedAt("/tmp", "findme.txt")
  1010. finder.Shutdown()
  1011. // read db file
  1012. string1 := fs.Read(t, finder.DbPath, filesystem)
  1013. err := filesystem.Remove(finder.DbPath)
  1014. if err != nil {
  1015. t.Fatal(err)
  1016. }
  1017. // run another finder
  1018. finder2 := finderWithSameParams(t, finder)
  1019. finder2.FindNamedAt("/tmp", "findme.txt")
  1020. finder2.Shutdown()
  1021. string2 := fs.Read(t, finder.DbPath, filesystem)
  1022. if string1 != string2 {
  1023. t.Errorf("Running Finder twice generated two dbs not having identical contents.\n"+
  1024. "Content of first file:\n"+
  1025. "\n"+
  1026. "%v"+
  1027. "\n"+
  1028. "\n"+
  1029. "Content of second file:\n"+
  1030. "\n"+
  1031. "%v\n"+
  1032. "\n",
  1033. string1,
  1034. string2,
  1035. )
  1036. }
  1037. }
  1038. func TestNumSyscallsOfSecondFind(t *testing.T) {
  1039. // setup filesystem
  1040. filesystem := newFs()
  1041. fs.Create(t, "/tmp/findme.txt", filesystem)
  1042. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  1043. fs.Create(t, "/tmp/a/misc.txt", filesystem)
  1044. // set up the finder and run it once
  1045. finder := newFinder(
  1046. t,
  1047. filesystem,
  1048. CacheParams{
  1049. RootDirs: []string{"/tmp"},
  1050. IncludeFiles: []string{"findme.txt"},
  1051. },
  1052. )
  1053. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  1054. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
  1055. filesystem.ClearMetrics()
  1056. // run the finder again and confirm it doesn't check the filesystem
  1057. refoundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  1058. fs.AssertSameResponse(t, refoundPaths, foundPaths)
  1059. fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{})
  1060. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
  1061. finder.Shutdown()
  1062. }
  1063. func TestChangingParamsOfSecondFind(t *testing.T) {
  1064. // setup filesystem
  1065. filesystem := newFs()
  1066. fs.Create(t, "/tmp/findme.txt", filesystem)
  1067. fs.Create(t, "/tmp/a/findme.txt", filesystem)
  1068. fs.Create(t, "/tmp/a/metoo.txt", filesystem)
  1069. // set up the finder and run it once
  1070. finder := newFinder(
  1071. t,
  1072. filesystem,
  1073. CacheParams{
  1074. RootDirs: []string{"/tmp"},
  1075. IncludeFiles: []string{"findme.txt", "metoo.txt"},
  1076. },
  1077. )
  1078. foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
  1079. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
  1080. filesystem.ClearMetrics()
  1081. // run the finder again and confirm it gets the right answer without asking the filesystem
  1082. refoundPaths := finder.FindNamedAt("/tmp", "metoo.txt")
  1083. fs.AssertSameResponse(t, refoundPaths, []string{"/tmp/a/metoo.txt"})
  1084. fs.AssertSameStatCalls(t, filesystem.StatCalls, []string{})
  1085. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
  1086. finder.Shutdown()
  1087. }
  1088. func TestSymlinkPointingToFile(t *testing.T) {
  1089. // setup filesystem
  1090. filesystem := newFs()
  1091. fs.Create(t, "/tmp/a/hi.txt", filesystem)
  1092. fs.Create(t, "/tmp/a/ignoreme.txt", filesystem)
  1093. fs.Link(t, "/tmp/hi.txt", "a/hi.txt", filesystem)
  1094. fs.Link(t, "/tmp/b/hi.txt", "../a/hi.txt", filesystem)
  1095. fs.Link(t, "/tmp/c/hi.txt", "/tmp/hi.txt", filesystem)
  1096. fs.Link(t, "/tmp/d/hi.txt", "../a/bye.txt", filesystem)
  1097. fs.Link(t, "/tmp/d/bye.txt", "../a/hi.txt", filesystem)
  1098. fs.Link(t, "/tmp/e/bye.txt", "../a/bye.txt", filesystem)
  1099. fs.Link(t, "/tmp/f/hi.txt", "somethingThatDoesntExist", filesystem)
  1100. // set up the finder and run it once
  1101. finder := newFinder(
  1102. t,
  1103. filesystem,
  1104. CacheParams{
  1105. RootDirs: []string{"/tmp"},
  1106. IncludeFiles: []string{"hi.txt"},
  1107. },
  1108. )
  1109. foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
  1110. // should search based on the name of the link rather than the destination or validity of the link
  1111. correctResponse := []string{
  1112. "/tmp/a/hi.txt",
  1113. "/tmp/hi.txt",
  1114. "/tmp/b/hi.txt",
  1115. "/tmp/c/hi.txt",
  1116. "/tmp/d/hi.txt",
  1117. "/tmp/f/hi.txt",
  1118. }
  1119. fs.AssertSameResponse(t, foundPaths, correctResponse)
  1120. }
  1121. func TestSymlinkPointingToDirectory(t *testing.T) {
  1122. // setup filesystem
  1123. filesystem := newFs()
  1124. fs.Create(t, "/tmp/dir/hi.txt", filesystem)
  1125. fs.Create(t, "/tmp/dir/ignoreme.txt", filesystem)
  1126. fs.Link(t, "/tmp/links/dir", "../dir", filesystem)
  1127. fs.Link(t, "/tmp/links/link", "../dir", filesystem)
  1128. fs.Link(t, "/tmp/links/hi.txt", "../dir", filesystem)
  1129. fs.Link(t, "/tmp/links/broken", "nothingHere", filesystem)
  1130. fs.Link(t, "/tmp/links/recursive", "recursive", filesystem)
  1131. // set up the finder and run it once
  1132. finder := newFinder(
  1133. t,
  1134. filesystem,
  1135. CacheParams{
  1136. RootDirs: []string{"/tmp"},
  1137. IncludeFiles: []string{"hi.txt"},
  1138. },
  1139. )
  1140. foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
  1141. // should completely ignore symlinks that point to directories
  1142. correctResponse := []string{
  1143. "/tmp/dir/hi.txt",
  1144. }
  1145. fs.AssertSameResponse(t, foundPaths, correctResponse)
  1146. }
  1147. // TestAddPruneFile confirms that adding a prune-file (into a directory for which we
  1148. // already had a cache) causes the directory to be ignored
  1149. func TestAddPruneFile(t *testing.T) {
  1150. // setup filesystem
  1151. filesystem := newFs()
  1152. fs.Create(t, "/tmp/out/hi.txt", filesystem)
  1153. fs.Create(t, "/tmp/out/a/hi.txt", filesystem)
  1154. fs.Create(t, "/tmp/hi.txt", filesystem)
  1155. // do find
  1156. finder := newFinder(
  1157. t,
  1158. filesystem,
  1159. CacheParams{
  1160. RootDirs: []string{"/tmp"},
  1161. PruneFiles: []string{".ignore-out-dir"},
  1162. IncludeFiles: []string{"hi.txt"},
  1163. },
  1164. )
  1165. foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
  1166. // check result
  1167. fs.AssertSameResponse(t, foundPaths,
  1168. []string{"/tmp/hi.txt",
  1169. "/tmp/out/hi.txt",
  1170. "/tmp/out/a/hi.txt"},
  1171. )
  1172. finder.Shutdown()
  1173. // modify filesystem
  1174. filesystem.Clock.Tick()
  1175. fs.Create(t, "/tmp/out/.ignore-out-dir", filesystem)
  1176. // run another find and check its result
  1177. finder2 := finderWithSameParams(t, finder)
  1178. foundPaths = finder2.FindNamedAt("/tmp", "hi.txt")
  1179. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
  1180. finder2.Shutdown()
  1181. }
  1182. func TestUpdatingDbIffChanged(t *testing.T) {
  1183. // setup filesystem
  1184. filesystem := newFs()
  1185. fs.Create(t, "/tmp/a/hi.txt", filesystem)
  1186. fs.Create(t, "/tmp/b/bye.txt", filesystem)
  1187. // run the first finder
  1188. finder := newFinder(
  1189. t,
  1190. filesystem,
  1191. CacheParams{
  1192. RootDirs: []string{"/tmp"},
  1193. IncludeFiles: []string{"hi.txt"},
  1194. },
  1195. )
  1196. filesystem.Clock.Tick()
  1197. foundPaths := finder.FindAll()
  1198. finder.Shutdown()
  1199. // check results
  1200. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
  1201. // modify the filesystem
  1202. filesystem.Clock.Tick()
  1203. fs.Create(t, "/tmp/b/hi.txt", filesystem)
  1204. filesystem.Clock.Tick()
  1205. filesystem.ClearMetrics()
  1206. // run the second finder
  1207. finder2 := finderWithSameParams(t, finder)
  1208. foundPaths = finder2.FindAll()
  1209. finder2.Shutdown()
  1210. // check results
  1211. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
  1212. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
  1213. expectedDbWriteTime := filesystem.Clock.Time()
  1214. actualDbWriteTime := fs.ModTime(t, finder2.DbPath, filesystem)
  1215. if actualDbWriteTime != expectedDbWriteTime {
  1216. t.Fatalf("Expected to write db at %v, actually wrote db at %v\n",
  1217. expectedDbWriteTime, actualDbWriteTime)
  1218. }
  1219. // reset metrics
  1220. filesystem.ClearMetrics()
  1221. // run the third finder
  1222. finder3 := finderWithSameParams(t, finder2)
  1223. foundPaths = finder3.FindAll()
  1224. // check results
  1225. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
  1226. fs.AssertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
  1227. finder3.Shutdown()
  1228. actualDbWriteTime = fs.ModTime(t, finder3.DbPath, filesystem)
  1229. if actualDbWriteTime != expectedDbWriteTime {
  1230. t.Fatalf("Re-wrote db even when contents did not change")
  1231. }
  1232. }
  1233. func TestDirectoryNotPermitted(t *testing.T) {
  1234. // setup filesystem
  1235. filesystem := newFs()
  1236. fs.Create(t, "/tmp/hi.txt", filesystem)
  1237. fs.Create(t, "/tmp/a/hi.txt", filesystem)
  1238. fs.Create(t, "/tmp/a/a/hi.txt", filesystem)
  1239. fs.Create(t, "/tmp/b/hi.txt", filesystem)
  1240. // run the first finder
  1241. finder := newFinder(
  1242. t,
  1243. filesystem,
  1244. CacheParams{
  1245. RootDirs: []string{"/tmp"},
  1246. IncludeFiles: []string{"hi.txt"},
  1247. },
  1248. )
  1249. filesystem.Clock.Tick()
  1250. foundPaths := finder.FindAll()
  1251. finder.Shutdown()
  1252. allPaths := []string{"/tmp/hi.txt", "/tmp/a/hi.txt", "/tmp/a/a/hi.txt", "/tmp/b/hi.txt"}
  1253. // check results
  1254. fs.AssertSameResponse(t, foundPaths, allPaths)
  1255. // modify the filesystem
  1256. filesystem.Clock.Tick()
  1257. fs.SetReadable(t, "/tmp/a", false, filesystem)
  1258. filesystem.Clock.Tick()
  1259. // run the second finder
  1260. finder2 := finderWithSameParams(t, finder)
  1261. foundPaths = finder2.FindAll()
  1262. finder2.Shutdown()
  1263. // check results
  1264. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt", "/tmp/b/hi.txt"})
  1265. // modify the filesystem back
  1266. fs.SetReadable(t, "/tmp/a", true, filesystem)
  1267. // run the third finder
  1268. finder3 := finderWithSameParams(t, finder2)
  1269. foundPaths = finder3.FindAll()
  1270. finder3.Shutdown()
  1271. // check results
  1272. fs.AssertSameResponse(t, foundPaths, allPaths)
  1273. }
  1274. func TestFileNotPermitted(t *testing.T) {
  1275. // setup filesystem
  1276. filesystem := newFs()
  1277. fs.Create(t, "/tmp/hi.txt", filesystem)
  1278. fs.SetReadable(t, "/tmp/hi.txt", false, filesystem)
  1279. // run the first finder
  1280. finder := newFinder(
  1281. t,
  1282. filesystem,
  1283. CacheParams{
  1284. RootDirs: []string{"/tmp"},
  1285. IncludeFiles: []string{"hi.txt"},
  1286. },
  1287. )
  1288. filesystem.Clock.Tick()
  1289. foundPaths := finder.FindAll()
  1290. finder.Shutdown()
  1291. // check results
  1292. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
  1293. }
  1294. func TestCacheEntryPathUnexpectedError(t *testing.T) {
  1295. // setup filesystem
  1296. filesystem := newFs()
  1297. fs.Create(t, "/tmp/a/hi.txt", filesystem)
  1298. // run the first finder
  1299. finder := newFinder(
  1300. t,
  1301. filesystem,
  1302. CacheParams{
  1303. RootDirs: []string{"/tmp"},
  1304. IncludeFiles: []string{"hi.txt"},
  1305. },
  1306. )
  1307. filesystem.Clock.Tick()
  1308. foundPaths := finder.FindAll()
  1309. finder.Shutdown()
  1310. // check results
  1311. fs.AssertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
  1312. // make the directory not readable
  1313. fs.SetReadErr(t, "/tmp/a", os.ErrInvalid, filesystem)
  1314. // run the second finder
  1315. _, err := finderAndErrorWithSameParams(t, finder)
  1316. if err == nil {
  1317. t.Fatal("Failed to detect unexpected filesystem error")
  1318. }
  1319. }