blob: 8d1bbd7fb3027f835aa186155654cc00d3242a1a [file] [log] [blame]
Jeff Gastonf1fd45e2017-08-09 18:25:28 -07001// 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
15package finder
16
17import (
18 "fmt"
Jeff Gastonb629e182017-08-14 16:49:18 -070019 "io/ioutil"
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070020 "log"
Jeff Gastonb629e182017-08-14 16:49:18 -070021 "os"
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070022 "path/filepath"
23 "reflect"
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070024 "sort"
Jeff Gastonb629e182017-08-14 16:49:18 -070025 "testing"
26 "time"
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070027
28 "android/soong/fs"
29 "runtime/debug"
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070030)
31
32// some utils for tests to use
33func newFs() *fs.MockFs {
34 return fs.NewMockFs(map[string][]byte{})
35}
36
37func newFinder(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams) *Finder {
Jeff Gastonb629e182017-08-14 16:49:18 -070038 f, err := newFinderAndErr(t, filesystem, cacheParams)
39 if err != nil {
40 fatal(t, err.Error())
41 }
42 return f
43}
44
45func newFinderAndErr(t *testing.T, filesystem *fs.MockFs, cacheParams CacheParams) (*Finder, error) {
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070046 cachePath := "/finder/finder-db"
47 cacheDir := filepath.Dir(cachePath)
48 filesystem.MkDirs(cacheDir)
49 if cacheParams.WorkingDirectory == "" {
50 cacheParams.WorkingDirectory = "/cwd"
51 }
52
53 logger := log.New(ioutil.Discard, "", 0)
Jeff Gastonb629e182017-08-14 16:49:18 -070054 f, err := New(cacheParams, filesystem, logger, cachePath)
55 return f, err
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070056}
57
58func finderWithSameParams(t *testing.T, original *Finder) *Finder {
Jeff Gastonb629e182017-08-14 16:49:18 -070059 f, err := finderAndErrorWithSameParams(t, original)
60 if err != nil {
61 fatal(t, err.Error())
62 }
63 return f
64}
65
66func finderAndErrorWithSameParams(t *testing.T, original *Finder) (*Finder, error) {
67 f, err := New(
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070068 original.cacheMetadata.Config.CacheParams,
69 original.filesystem,
70 original.logger,
71 original.DbPath)
Jeff Gastonb629e182017-08-14 16:49:18 -070072 return f, err
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070073}
74
75func write(t *testing.T, path string, content string, filesystem *fs.MockFs) {
76 parent := filepath.Dir(path)
77 filesystem.MkDirs(parent)
78 err := filesystem.WriteFile(path, []byte(content), 0777)
79 if err != nil {
Jeff Gastonb629e182017-08-14 16:49:18 -070080 fatal(t, err.Error())
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070081 }
82}
83
84func create(t *testing.T, path string, filesystem *fs.MockFs) {
85 write(t, path, "hi", filesystem)
86}
87
88func delete(t *testing.T, path string, filesystem *fs.MockFs) {
89 err := filesystem.Remove(path)
90 if err != nil {
Jeff Gastonb629e182017-08-14 16:49:18 -070091 fatal(t, err.Error())
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070092 }
93}
94
95func removeAll(t *testing.T, path string, filesystem *fs.MockFs) {
96 err := filesystem.RemoveAll(path)
97 if err != nil {
Jeff Gastonb629e182017-08-14 16:49:18 -070098 fatal(t, err.Error())
Jeff Gastonf1fd45e2017-08-09 18:25:28 -070099 }
100}
101
102func move(t *testing.T, oldPath string, newPath string, filesystem *fs.MockFs) {
103 err := filesystem.Rename(oldPath, newPath)
104 if err != nil {
Jeff Gastonb629e182017-08-14 16:49:18 -0700105 fatal(t, err.Error())
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700106 }
107}
108
109func link(t *testing.T, newPath string, oldPath string, filesystem *fs.MockFs) {
110 parentPath := filepath.Dir(newPath)
111 err := filesystem.MkDirs(parentPath)
112 if err != nil {
113 t.Fatal(err.Error())
114 }
115 err = filesystem.Symlink(oldPath, newPath)
116 if err != nil {
Jeff Gastonb629e182017-08-14 16:49:18 -0700117 fatal(t, err.Error())
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700118 }
119}
120func read(t *testing.T, path string, filesystem *fs.MockFs) string {
121 reader, err := filesystem.Open(path)
122 if err != nil {
123 t.Fatalf(err.Error())
124 }
125 bytes, err := ioutil.ReadAll(reader)
126 if err != nil {
127 t.Fatal(err.Error())
128 }
129 return string(bytes)
130}
131func modTime(t *testing.T, path string, filesystem *fs.MockFs) time.Time {
132 stats, err := filesystem.Lstat(path)
133 if err != nil {
134 t.Fatal(err.Error())
135 }
136 return stats.ModTime()
137}
138func setReadable(t *testing.T, path string, readable bool, filesystem *fs.MockFs) {
139 err := filesystem.SetReadable(path, readable)
140 if err != nil {
141 t.Fatal(err.Error())
142 }
143}
Jeff Gastonb629e182017-08-14 16:49:18 -0700144
145func setReadErr(t *testing.T, path string, readErr error, filesystem *fs.MockFs) {
146 err := filesystem.SetReadErr(path, readErr)
147 if err != nil {
148 t.Fatal(err.Error())
149 }
150}
151
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700152func fatal(t *testing.T, message string) {
153 t.Error(message)
154 debug.PrintStack()
155 t.FailNow()
156}
Jeff Gastonb629e182017-08-14 16:49:18 -0700157
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700158func assertSameResponse(t *testing.T, actual []string, expected []string) {
159 sort.Strings(actual)
160 sort.Strings(expected)
161 if !reflect.DeepEqual(actual, expected) {
162 fatal(
163 t,
164 fmt.Sprintf(
165 "Expected Finder to return these %v paths:\n %v,\ninstead returned these %v paths: %v\n",
166 len(expected), expected, len(actual), actual),
167 )
168 }
169}
170
171func assertSameStatCalls(t *testing.T, actual []string, expected []string) {
172 sort.Strings(actual)
173 sort.Strings(expected)
174
175 if !reflect.DeepEqual(actual, expected) {
176 fatal(
177 t,
178 fmt.Sprintf(
179 "Finder made incorrect Stat calls.\n"+
180 "Actual:\n"+
181 "%v\n"+
182 "Expected:\n"+
183 "%v\n"+
184 "\n",
185 actual, expected),
186 )
187 }
188}
189func assertSameReadDirCalls(t *testing.T, actual []string, expected []string) {
190 sort.Strings(actual)
191 sort.Strings(expected)
192
193 if !reflect.DeepEqual(actual, expected) {
194 fatal(
195 t,
196 fmt.Sprintf(
197 "Finder made incorrect ReadDir calls.\n"+
198 "Actual:\n"+
199 "%v\n"+
200 "Expected:\n"+
201 "%v\n"+
202 "\n",
203 actual, expected),
204 )
205 }
206}
207
208// runSimpleTests creates a few files, searches for findme.txt, and checks for the expected matches
209func runSimpleTest(t *testing.T, existentPaths []string, expectedMatches []string) {
210 filesystem := newFs()
211 root := "/tmp"
212 filesystem.MkDirs(root)
213 for _, path := range existentPaths {
214 create(t, filepath.Join(root, path), filesystem)
215 }
216
217 finder := newFinder(t,
218 filesystem,
219 CacheParams{
220 "/cwd",
221 []string{root},
222 nil,
223 nil,
224 []string{"findme.txt", "skipme.txt"},
225 },
226 )
227 defer finder.Shutdown()
228
229 foundPaths := finder.FindNamedAt(root, "findme.txt")
230 absoluteMatches := []string{}
231 for i := range expectedMatches {
232 absoluteMatches = append(absoluteMatches, filepath.Join(root, expectedMatches[i]))
233 }
234 assertSameResponse(t, foundPaths, absoluteMatches)
235}
236
237// end of utils, start of individual tests
238
239func TestSingleFile(t *testing.T) {
240 runSimpleTest(t,
241 []string{"findme.txt"},
242 []string{"findme.txt"},
243 )
244}
245
246func TestIncludeFiles(t *testing.T) {
247 runSimpleTest(t,
248 []string{"findme.txt", "skipme.txt"},
249 []string{"findme.txt"},
250 )
251}
252
253func TestNestedDirectories(t *testing.T) {
254 runSimpleTest(t,
255 []string{"findme.txt", "skipme.txt", "subdir/findme.txt", "subdir/skipme.txt"},
256 []string{"findme.txt", "subdir/findme.txt"},
257 )
258}
259
260func TestEmptyDirectory(t *testing.T) {
261 runSimpleTest(t,
262 []string{},
263 []string{},
264 )
265}
266
267func TestEmptyPath(t *testing.T) {
268 filesystem := newFs()
269 root := "/tmp"
270 create(t, filepath.Join(root, "findme.txt"), filesystem)
271
272 finder := newFinder(
273 t,
274 filesystem,
275 CacheParams{
276 RootDirs: []string{root},
277 IncludeFiles: []string{"findme.txt", "skipme.txt"},
278 },
279 )
280 defer finder.Shutdown()
281
282 foundPaths := finder.FindNamedAt("", "findme.txt")
283
284 assertSameResponse(t, foundPaths, []string{})
285}
286
287func TestFilesystemRoot(t *testing.T) {
288 filesystem := newFs()
289 root := "/"
290 createdPath := "/findme.txt"
291 create(t, createdPath, filesystem)
292
293 finder := newFinder(
294 t,
295 filesystem,
296 CacheParams{
297 RootDirs: []string{root},
298 IncludeFiles: []string{"findme.txt", "skipme.txt"},
299 },
300 )
301 defer finder.Shutdown()
302
303 foundPaths := finder.FindNamedAt(root, "findme.txt")
304
305 assertSameResponse(t, foundPaths, []string{createdPath})
306}
307
Jeff Gastonb629e182017-08-14 16:49:18 -0700308func TestNonexistentDir(t *testing.T) {
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700309 filesystem := newFs()
310 create(t, "/tmp/findme.txt", filesystem)
311
Jeff Gastonb629e182017-08-14 16:49:18 -0700312 _, err := newFinderAndErr(
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700313 t,
314 filesystem,
315 CacheParams{
316 RootDirs: []string{"/tmp/IDontExist"},
317 IncludeFiles: []string{"findme.txt", "skipme.txt"},
318 },
319 )
Jeff Gastonb629e182017-08-14 16:49:18 -0700320 if err == nil {
321 fatal(t, "Did not fail when given a nonexistent root directory")
322 }
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700323}
324
325func TestExcludeDirs(t *testing.T) {
326 filesystem := newFs()
327 create(t, "/tmp/exclude/findme.txt", filesystem)
328 create(t, "/tmp/exclude/subdir/findme.txt", filesystem)
329 create(t, "/tmp/subdir/exclude/findme.txt", filesystem)
330 create(t, "/tmp/subdir/subdir/findme.txt", filesystem)
331 create(t, "/tmp/subdir/findme.txt", filesystem)
332 create(t, "/tmp/findme.txt", filesystem)
333
334 finder := newFinder(
335 t,
336 filesystem,
337 CacheParams{
338 RootDirs: []string{"/tmp"},
339 ExcludeDirs: []string{"exclude"},
340 IncludeFiles: []string{"findme.txt", "skipme.txt"},
341 },
342 )
343 defer finder.Shutdown()
344
345 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
346
347 assertSameResponse(t, foundPaths,
348 []string{"/tmp/findme.txt",
349 "/tmp/subdir/findme.txt",
350 "/tmp/subdir/subdir/findme.txt"})
351}
352
353func TestPruneFiles(t *testing.T) {
354 filesystem := newFs()
355 create(t, "/tmp/out/findme.txt", filesystem)
356 create(t, "/tmp/out/.ignore-out-dir", filesystem)
357 create(t, "/tmp/out/child/findme.txt", filesystem)
358
359 create(t, "/tmp/out2/.ignore-out-dir", filesystem)
360 create(t, "/tmp/out2/sub/findme.txt", filesystem)
361
362 create(t, "/tmp/findme.txt", filesystem)
363 create(t, "/tmp/include/findme.txt", filesystem)
364
365 finder := newFinder(
366 t,
367 filesystem,
368 CacheParams{
369 RootDirs: []string{"/tmp"},
370 PruneFiles: []string{".ignore-out-dir"},
371 IncludeFiles: []string{"findme.txt"},
372 },
373 )
374 defer finder.Shutdown()
375
376 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
377
378 assertSameResponse(t, foundPaths,
379 []string{"/tmp/findme.txt",
380 "/tmp/include/findme.txt"})
381}
382
383func TestRootDir(t *testing.T) {
384 filesystem := newFs()
385 create(t, "/tmp/a/findme.txt", filesystem)
386 create(t, "/tmp/a/subdir/findme.txt", filesystem)
387 create(t, "/tmp/b/findme.txt", filesystem)
388 create(t, "/tmp/b/subdir/findme.txt", filesystem)
389
390 finder := newFinder(
391 t,
392 filesystem,
393 CacheParams{
394 RootDirs: []string{"/tmp/a"},
395 IncludeFiles: []string{"findme.txt"},
396 },
397 )
398 defer finder.Shutdown()
399
400 foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
401
402 assertSameResponse(t, foundPaths,
403 []string{"/tmp/a/findme.txt",
404 "/tmp/a/subdir/findme.txt"})
405}
406
407func TestUncachedDir(t *testing.T) {
408 filesystem := newFs()
409 create(t, "/tmp/a/findme.txt", filesystem)
410 create(t, "/tmp/a/subdir/findme.txt", filesystem)
411 create(t, "/tmp/b/findme.txt", filesystem)
412 create(t, "/tmp/b/subdir/findme.txt", filesystem)
413
414 finder := newFinder(
415 t,
416 filesystem,
417 CacheParams{
Jeff Gastonb629e182017-08-14 16:49:18 -0700418 RootDirs: []string{"/tmp/b"},
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700419 IncludeFiles: []string{"findme.txt"},
420 },
421 )
422
423 foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
424 // If the caller queries for a file that is in the cache, then computing the
425 // correct answer won't be fast, and it would be easy for the caller to
426 // fail to notice its slowness. Instead, we only ever search the cache for files
427 // to return, which enforces that we can determine which files will be
428 // interesting upfront.
429 assertSameResponse(t, foundPaths, []string{})
430
431 finder.Shutdown()
432}
433
434func TestSearchingForFilesExcludedFromCache(t *testing.T) {
435 // setup filesystem
436 filesystem := newFs()
437 create(t, "/tmp/findme.txt", filesystem)
438 create(t, "/tmp/a/findme.txt", filesystem)
439 create(t, "/tmp/a/misc.txt", filesystem)
440
441 // set up the finder and run it
442 finder := newFinder(
443 t,
444 filesystem,
445 CacheParams{
446 RootDirs: []string{"/tmp"},
447 IncludeFiles: []string{"findme.txt"},
448 },
449 )
450 foundPaths := finder.FindNamedAt("/tmp", "misc.txt")
451 // If the caller queries for a file that is in the cache, then computing the
452 // correct answer won't be fast, and it would be easy for the caller to
453 // fail to notice its slowness. Instead, we only ever search the cache for files
454 // to return, which enforces that we can determine which files will be
455 // interesting upfront.
456 assertSameResponse(t, foundPaths, []string{})
457
458 finder.Shutdown()
459}
460
461func TestRelativeFilePaths(t *testing.T) {
462 filesystem := newFs()
463
464 create(t, "/tmp/ignore/hi.txt", filesystem)
465 create(t, "/tmp/include/hi.txt", filesystem)
466 create(t, "/cwd/hi.txt", filesystem)
467 create(t, "/cwd/a/hi.txt", filesystem)
468 create(t, "/cwd/a/a/hi.txt", filesystem)
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700469 create(t, "/rel/a/hi.txt", filesystem)
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700470
471 finder := newFinder(
472 t,
473 filesystem,
474 CacheParams{
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700475 RootDirs: []string{"/cwd", "../rel", "/tmp/include"},
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700476 IncludeFiles: []string{"hi.txt"},
477 },
478 )
479 defer finder.Shutdown()
480
481 foundPaths := finder.FindNamedAt("a", "hi.txt")
482 assertSameResponse(t, foundPaths,
483 []string{"a/hi.txt",
484 "a/a/hi.txt"})
485
486 foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
487 assertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
488
489 foundPaths = finder.FindNamedAt(".", "hi.txt")
490 assertSameResponse(t, foundPaths,
491 []string{"hi.txt",
492 "a/hi.txt",
493 "a/a/hi.txt"})
494
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700495 foundPaths = finder.FindNamedAt("/rel", "hi.txt")
496 assertSameResponse(t, foundPaths,
497 []string{"/rel/a/hi.txt"})
498
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700499 foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
500 assertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
501}
502
503// have to run this test with the race-detector (`go test -race src/android/soong/finder/*.go`)
504// for there to be much chance of the test actually detecting any error that may be present
505func TestRootDirsContainedInOtherRootDirs(t *testing.T) {
506 filesystem := newFs()
507
508 create(t, "/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt", filesystem)
509
510 finder := newFinder(
511 t,
512 filesystem,
513 CacheParams{
Jeff Gastonb629e182017-08-14 16:49:18 -0700514 RootDirs: []string{"/", "/tmp/a/b/c", "/tmp/a/b/c/d/e/f", "/tmp/a/b/c/d/e/f/g/h/i"},
Jeff Gastonf1fd45e2017-08-09 18:25:28 -0700515 IncludeFiles: []string{"findme.txt"},
516 },
517 )
518 defer finder.Shutdown()
519
520 foundPaths := finder.FindNamedAt("/tmp/a", "findme.txt")
521
522 assertSameResponse(t, foundPaths,
523 []string{"/tmp/a/b/c/d/e/f/g/h/i/j/findme.txt"})
524}
525
526func TestFindFirst(t *testing.T) {
527 filesystem := newFs()
528 create(t, "/tmp/a/hi.txt", filesystem)
529 create(t, "/tmp/b/hi.txt", filesystem)
530 create(t, "/tmp/b/a/hi.txt", filesystem)
531
532 finder := newFinder(
533 t,
534 filesystem,
535 CacheParams{
536 RootDirs: []string{"/tmp"},
537 IncludeFiles: []string{"hi.txt"},
538 },
539 )
540 defer finder.Shutdown()
541
542 foundPaths := finder.FindFirstNamed("hi.txt")
543
544 assertSameResponse(t, foundPaths,
545 []string{"/tmp/a/hi.txt",
546 "/tmp/b/hi.txt"},
547 )
548}
549
550func TestConcurrentFindSameDirectory(t *testing.T) {
551 filesystem := newFs()
552
553 // create a bunch of files and directories
554 paths := []string{}
555 for i := 0; i < 10; i++ {
556 parentDir := fmt.Sprintf("/tmp/%v", i)
557 for j := 0; j < 10; j++ {
558 filePath := filepath.Join(parentDir, fmt.Sprintf("%v/findme.txt", j))
559 paths = append(paths, filePath)
560 }
561 }
562 sort.Strings(paths)
563 for _, path := range paths {
564 create(t, path, filesystem)
565 }
566
567 // set up a finder
568 finder := newFinder(
569 t,
570 filesystem,
571 CacheParams{
572 RootDirs: []string{"/tmp"},
573 IncludeFiles: []string{"findme.txt"},
574 },
575 )
576 defer finder.Shutdown()
577
578 numTests := 20
579 results := make(chan []string, numTests)
580 // make several parallel calls to the finder
581 for i := 0; i < numTests; i++ {
582 go func() {
583 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
584 results <- foundPaths
585 }()
586 }
587
588 // check that each response was correct
589 for i := 0; i < numTests; i++ {
590 foundPaths := <-results
591 assertSameResponse(t, foundPaths, paths)
592 }
593}
594
595func TestConcurrentFindDifferentDirectories(t *testing.T) {
596 filesystem := newFs()
597
598 // create a bunch of files and directories
599 allFiles := []string{}
600 numSubdirs := 10
601 rootPaths := []string{}
602 queryAnswers := [][]string{}
603 for i := 0; i < numSubdirs; i++ {
604 parentDir := fmt.Sprintf("/tmp/%v", i)
605 rootPaths = append(rootPaths, parentDir)
606 queryAnswers = append(queryAnswers, []string{})
607 for j := 0; j < 10; j++ {
608 filePath := filepath.Join(parentDir, fmt.Sprintf("%v/findme.txt", j))
609 queryAnswers[i] = append(queryAnswers[i], filePath)
610 allFiles = append(allFiles, filePath)
611 }
612 sort.Strings(queryAnswers[i])
613 }
614 sort.Strings(allFiles)
615 for _, path := range allFiles {
616 create(t, path, filesystem)
617 }
618
619 // set up a finder
620 finder := newFinder(
621 t,
622 filesystem,
623
624 CacheParams{
625 RootDirs: []string{"/tmp"},
626 IncludeFiles: []string{"findme.txt"},
627 },
628 )
629 defer finder.Shutdown()
630
631 type testRun struct {
632 path string
633 foundMatches []string
634 correctMatches []string
635 }
636
637 numTests := numSubdirs + 1
638 testRuns := make(chan testRun, numTests)
639
640 searchAt := func(path string, correctMatches []string) {
641 foundPaths := finder.FindNamedAt(path, "findme.txt")
642 testRuns <- testRun{path, foundPaths, correctMatches}
643 }
644
645 // make several parallel calls to the finder
646 go searchAt("/tmp", allFiles)
647 for i := 0; i < len(rootPaths); i++ {
648 go searchAt(rootPaths[i], queryAnswers[i])
649 }
650
651 // check that each response was correct
652 for i := 0; i < numTests; i++ {
653 testRun := <-testRuns
654 assertSameResponse(t, testRun.foundMatches, testRun.correctMatches)
655 }
656}
657
658func TestStrangelyFormattedPaths(t *testing.T) {
659 filesystem := newFs()
660
661 create(t, "/tmp/findme.txt", filesystem)
662 create(t, "/tmp/a/findme.txt", filesystem)
663 create(t, "/tmp/b/findme.txt", filesystem)
664
665 finder := newFinder(
666 t,
667 filesystem,
668 CacheParams{
669 RootDirs: []string{"//tmp//a//.."},
670 IncludeFiles: []string{"findme.txt"},
671 },
672 )
673 defer finder.Shutdown()
674
675 foundPaths := finder.FindNamedAt("//tmp//a//..", "findme.txt")
676
677 assertSameResponse(t, foundPaths,
678 []string{"/tmp/a/findme.txt",
679 "/tmp/b/findme.txt",
680 "/tmp/findme.txt"})
681}
682
683func TestCorruptedCacheHeader(t *testing.T) {
684 filesystem := newFs()
685
686 create(t, "/tmp/findme.txt", filesystem)
687 create(t, "/tmp/a/findme.txt", filesystem)
688 write(t, "/finder/finder-db", "sample header", filesystem)
689
690 finder := newFinder(
691 t,
692 filesystem,
693 CacheParams{
694 RootDirs: []string{"/tmp"},
695 IncludeFiles: []string{"findme.txt"},
696 },
697 )
698 defer finder.Shutdown()
699
700 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
701
702 assertSameResponse(t, foundPaths,
703 []string{"/tmp/a/findme.txt",
704 "/tmp/findme.txt"})
705}
706
707func TestCanUseCache(t *testing.T) {
708 // setup filesystem
709 filesystem := newFs()
710 create(t, "/tmp/findme.txt", filesystem)
711 create(t, "/tmp/a/findme.txt", filesystem)
712
713 // run the first finder
714 finder := newFinder(
715 t,
716 filesystem,
717 CacheParams{
718 RootDirs: []string{"/tmp"},
719 IncludeFiles: []string{"findme.txt"},
720 },
721 )
722 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
723 // check the response of the first finder
724 correctResponse := []string{"/tmp/a/findme.txt",
725 "/tmp/findme.txt"}
726 assertSameResponse(t, foundPaths, correctResponse)
727 finder.Shutdown()
728
729 // check results
730 cacheText := read(t, finder.DbPath, filesystem)
731 if len(cacheText) < 1 {
732 t.Fatalf("saved cache db is empty\n")
733 }
734 if len(filesystem.StatCalls) == 0 {
735 t.Fatal("No Stat calls recorded by mock filesystem")
736 }
737 if len(filesystem.ReadDirCalls) == 0 {
738 t.Fatal("No ReadDir calls recorded by filesystem")
739 }
740 statCalls := filesystem.StatCalls
741 filesystem.ClearMetrics()
742
743 // run the second finder
744 finder2 := finderWithSameParams(t, finder)
745 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
746 // check results
747 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
748 assertSameReadDirCalls(t, filesystem.StatCalls, statCalls)
749
750 finder2.Shutdown()
751}
752
753func TestCorruptedCacheBody(t *testing.T) {
754 // setup filesystem
755 filesystem := newFs()
756 create(t, "/tmp/findme.txt", filesystem)
757 create(t, "/tmp/a/findme.txt", filesystem)
758
759 // run the first finder
760 finder := newFinder(
761 t,
762 filesystem,
763 CacheParams{
764 RootDirs: []string{"/tmp"},
765 IncludeFiles: []string{"findme.txt"},
766 },
767 )
768 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
769 finder.Shutdown()
770
771 // check the response of the first finder
772 correctResponse := []string{"/tmp/a/findme.txt",
773 "/tmp/findme.txt"}
774 assertSameResponse(t, foundPaths, correctResponse)
775 numStatCalls := len(filesystem.StatCalls)
776 numReadDirCalls := len(filesystem.ReadDirCalls)
777
778 // load the cache file, corrupt it, and save it
779 cacheReader, err := filesystem.Open(finder.DbPath)
780 if err != nil {
781 t.Fatal(err)
782 }
783 cacheData, err := ioutil.ReadAll(cacheReader)
784 if err != nil {
785 t.Fatal(err)
786 }
787 cacheData = append(cacheData, []byte("DontMindMe")...)
788 filesystem.WriteFile(finder.DbPath, cacheData, 0777)
789 filesystem.ClearMetrics()
790
791 // run the second finder
792 finder2 := finderWithSameParams(t, finder)
793 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
794 // check results
795 assertSameResponse(t, foundPaths, correctResponse)
796 numNewStatCalls := len(filesystem.StatCalls)
797 numNewReadDirCalls := len(filesystem.ReadDirCalls)
798 // It's permissable to make more Stat calls with a corrupted cache because
799 // the Finder may restart once it detects corruption.
800 // However, it may have already issued many Stat calls.
801 // Because a corrupted db is not expected to be a common (or even a supported case),
802 // we don't care to optimize it and don't cache the already-issued Stat calls
803 if numNewReadDirCalls < numReadDirCalls {
804 t.Fatalf(
805 "Finder made fewer ReadDir calls with a corrupted cache (%v calls) than with no cache"+
806 " (%v calls)",
807 numNewReadDirCalls, numReadDirCalls)
808 }
809 if numNewStatCalls < numStatCalls {
810 t.Fatalf(
811 "Finder made fewer Stat calls with a corrupted cache (%v calls) than with no cache (%v calls)",
812 numNewStatCalls, numStatCalls)
813 }
814 finder2.Shutdown()
815}
816
817func TestStatCalls(t *testing.T) {
818 // setup filesystem
819 filesystem := newFs()
820 create(t, "/tmp/a/findme.txt", filesystem)
821
822 // run finder
823 finder := newFinder(
824 t,
825 filesystem,
826 CacheParams{
827 RootDirs: []string{"/tmp"},
828 IncludeFiles: []string{"findme.txt"},
829 },
830 )
831 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
832 finder.Shutdown()
833
834 // check response
835 assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
836 assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a"})
837 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
838}
839
840func TestFileAdded(t *testing.T) {
841 // setup filesystem
842 filesystem := newFs()
843 create(t, "/tmp/ignoreme.txt", filesystem)
844 create(t, "/tmp/a/findme.txt", filesystem)
845 create(t, "/tmp/b/ignore.txt", filesystem)
846 create(t, "/tmp/b/c/nope.txt", filesystem)
847 create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
848
849 // run the first finder
850 finder := newFinder(
851 t,
852 filesystem,
853 CacheParams{
854 RootDirs: []string{"/tmp"},
855 IncludeFiles: []string{"findme.txt"},
856 },
857 )
858 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
859 filesystem.Clock.Tick()
860 finder.Shutdown()
861 // check the response of the first finder
862 assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
863
864 // modify the filesystem
865 filesystem.Clock.Tick()
866 create(t, "/tmp/b/c/findme.txt", filesystem)
867 filesystem.Clock.Tick()
868 filesystem.ClearMetrics()
869
870 // run the second finder
871 finder2 := finderWithSameParams(t, finder)
872 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
873
874 // check results
875 assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/c/findme.txt"})
876 assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
877 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c"})
878 finder2.Shutdown()
879
880}
881
882func TestDirectoriesAdded(t *testing.T) {
883 // setup filesystem
884 filesystem := newFs()
885 create(t, "/tmp/ignoreme.txt", filesystem)
886 create(t, "/tmp/a/findme.txt", filesystem)
887 create(t, "/tmp/b/ignore.txt", filesystem)
888 create(t, "/tmp/b/c/nope.txt", filesystem)
889 create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
890
891 // run the first finder
892 finder := newFinder(
893 t,
894 filesystem,
895 CacheParams{
896 RootDirs: []string{"/tmp"},
897 IncludeFiles: []string{"findme.txt"},
898 },
899 )
900 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
901 finder.Shutdown()
902 // check the response of the first finder
903 assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
904
905 // modify the filesystem
906 filesystem.Clock.Tick()
907 create(t, "/tmp/b/c/new/findme.txt", filesystem)
908 create(t, "/tmp/b/c/new/new2/findme.txt", filesystem)
909 create(t, "/tmp/b/c/new/new2/ignoreme.txt", filesystem)
910 filesystem.ClearMetrics()
911
912 // run the second finder
913 finder2 := finderWithSameParams(t, finder)
914 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
915
916 // check results
917 assertSameResponse(t, foundPaths,
918 []string{"/tmp/a/findme.txt", "/tmp/b/c/new/findme.txt", "/tmp/b/c/new/new2/findme.txt"})
919 assertSameStatCalls(t, filesystem.StatCalls,
920 []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
921 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b/c", "/tmp/b/c/new", "/tmp/b/c/new/new2"})
922
923 finder2.Shutdown()
924}
925
926func TestDirectoryAndSubdirectoryBothUpdated(t *testing.T) {
927 // setup filesystem
928 filesystem := newFs()
929 create(t, "/tmp/hi1.txt", filesystem)
930 create(t, "/tmp/a/hi1.txt", filesystem)
931
932 // run the first finder
933 finder := newFinder(
934 t,
935 filesystem,
936 CacheParams{
937 RootDirs: []string{"/tmp"},
938 IncludeFiles: []string{"hi1.txt", "hi2.txt"},
939 },
940 )
941 foundPaths := finder.FindNamedAt("/tmp", "hi1.txt")
942 finder.Shutdown()
943 // check the response of the first finder
944 assertSameResponse(t, foundPaths, []string{"/tmp/hi1.txt", "/tmp/a/hi1.txt"})
945
946 // modify the filesystem
947 filesystem.Clock.Tick()
948 create(t, "/tmp/hi2.txt", filesystem)
949 create(t, "/tmp/a/hi2.txt", filesystem)
950 filesystem.ClearMetrics()
951
952 // run the second finder
953 finder2 := finderWithSameParams(t, finder)
954 foundPaths = finder2.FindAll()
955
956 // check results
957 assertSameResponse(t, foundPaths,
958 []string{"/tmp/hi1.txt", "/tmp/hi2.txt", "/tmp/a/hi1.txt", "/tmp/a/hi2.txt"})
959 assertSameStatCalls(t, filesystem.StatCalls,
960 []string{"/tmp", "/tmp/a"})
961 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a"})
962
963 finder2.Shutdown()
964}
965
966func TestFileDeleted(t *testing.T) {
967 // setup filesystem
968 filesystem := newFs()
969 create(t, "/tmp/ignoreme.txt", filesystem)
970 create(t, "/tmp/a/findme.txt", filesystem)
971 create(t, "/tmp/b/findme.txt", filesystem)
972 create(t, "/tmp/b/c/nope.txt", filesystem)
973 create(t, "/tmp/b/c/d/irrelevant.txt", filesystem)
974
975 // run the first finder
976 finder := newFinder(
977 t,
978 filesystem,
979 CacheParams{
980 RootDirs: []string{"/tmp"},
981 IncludeFiles: []string{"findme.txt"},
982 },
983 )
984 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
985 finder.Shutdown()
986 // check the response of the first finder
987 assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt", "/tmp/b/findme.txt"})
988
989 // modify the filesystem
990 filesystem.Clock.Tick()
991 delete(t, "/tmp/b/findme.txt", filesystem)
992 filesystem.ClearMetrics()
993
994 // run the second finder
995 finder2 := finderWithSameParams(t, finder)
996 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
997
998 // check results
999 assertSameResponse(t, foundPaths, []string{"/tmp/a/findme.txt"})
1000 assertSameStatCalls(t, filesystem.StatCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/c", "/tmp/b/c/d"})
1001 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
1002
1003 finder2.Shutdown()
1004}
1005
1006func TestDirectoriesDeleted(t *testing.T) {
1007 // setup filesystem
1008 filesystem := newFs()
1009 create(t, "/tmp/findme.txt", filesystem)
1010 create(t, "/tmp/a/findme.txt", filesystem)
1011 create(t, "/tmp/a/1/findme.txt", filesystem)
1012 create(t, "/tmp/a/1/2/findme.txt", filesystem)
1013 create(t, "/tmp/b/findme.txt", filesystem)
1014
1015 // run the first finder
1016 finder := newFinder(
1017 t,
1018 filesystem,
1019 CacheParams{
1020 RootDirs: []string{"/tmp"},
1021 IncludeFiles: []string{"findme.txt"},
1022 },
1023 )
1024 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
1025 finder.Shutdown()
1026 // check the response of the first finder
1027 assertSameResponse(t, foundPaths,
1028 []string{"/tmp/findme.txt",
1029 "/tmp/a/findme.txt",
1030 "/tmp/a/1/findme.txt",
1031 "/tmp/a/1/2/findme.txt",
1032 "/tmp/b/findme.txt"})
1033
1034 // modify the filesystem
1035 filesystem.Clock.Tick()
1036 removeAll(t, "/tmp/a/1", filesystem)
1037 filesystem.ClearMetrics()
1038
1039 // run the second finder
1040 finder2 := finderWithSameParams(t, finder)
1041 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
1042
1043 // check results
1044 assertSameResponse(t, foundPaths,
1045 []string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/b/findme.txt"})
1046 // Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
1047 // if the Finder detects the nonexistence of /tmp/a/1
1048 // However, when resuming from cache, we don't want the Finder to necessarily wait
1049 // to stat a directory until after statting its parent.
1050 // So here we just include /tmp/a/1/2 in the list.
1051 // The Finder is currently implemented to always restat every dir and
1052 // to not short-circuit due to nonexistence of parents (but it will remove
1053 // missing dirs from the cache for next time)
1054 assertSameStatCalls(t, filesystem.StatCalls,
1055 []string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b"})
1056 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/a"})
1057
1058 finder2.Shutdown()
1059}
1060
1061func TestDirectoriesMoved(t *testing.T) {
1062 // setup filesystem
1063 filesystem := newFs()
1064 create(t, "/tmp/findme.txt", filesystem)
1065 create(t, "/tmp/a/findme.txt", filesystem)
1066 create(t, "/tmp/a/1/findme.txt", filesystem)
1067 create(t, "/tmp/a/1/2/findme.txt", filesystem)
1068 create(t, "/tmp/b/findme.txt", filesystem)
1069
1070 // run the first finder
1071 finder := newFinder(
1072 t,
1073 filesystem,
1074 CacheParams{
1075 RootDirs: []string{"/tmp"},
1076 IncludeFiles: []string{"findme.txt"},
1077 },
1078 )
1079 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
1080 finder.Shutdown()
1081 // check the response of the first finder
1082 assertSameResponse(t, foundPaths,
1083 []string{"/tmp/findme.txt",
1084 "/tmp/a/findme.txt",
1085 "/tmp/a/1/findme.txt",
1086 "/tmp/a/1/2/findme.txt",
1087 "/tmp/b/findme.txt"})
1088
1089 // modify the filesystem
1090 filesystem.Clock.Tick()
1091 move(t, "/tmp/a", "/tmp/c", filesystem)
1092 filesystem.ClearMetrics()
1093
1094 // run the second finder
1095 finder2 := finderWithSameParams(t, finder)
1096 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
1097
1098 // check results
1099 assertSameResponse(t, foundPaths,
1100 []string{"/tmp/findme.txt",
1101 "/tmp/b/findme.txt",
1102 "/tmp/c/findme.txt",
1103 "/tmp/c/1/findme.txt",
1104 "/tmp/c/1/2/findme.txt"})
1105 // Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
1106 // if the Finder detects the nonexistence of /tmp/a/1
1107 // However, when resuming from cache, we don't want the Finder to necessarily wait
1108 // to stat a directory until after statting its parent.
1109 // So here we just include /tmp/a/1/2 in the list.
1110 // The Finder is currently implemented to always restat every dir and
1111 // to not short-circuit due to nonexistence of parents (but it will remove
1112 // missing dirs from the cache for next time)
1113 assertSameStatCalls(t, filesystem.StatCalls,
1114 []string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
1115 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/c", "/tmp/c/1", "/tmp/c/1/2"})
1116 finder2.Shutdown()
1117}
1118
1119func TestDirectoriesSwapped(t *testing.T) {
1120 // setup filesystem
1121 filesystem := newFs()
1122 create(t, "/tmp/findme.txt", filesystem)
1123 create(t, "/tmp/a/findme.txt", filesystem)
1124 create(t, "/tmp/a/1/findme.txt", filesystem)
1125 create(t, "/tmp/a/1/2/findme.txt", filesystem)
1126 create(t, "/tmp/b/findme.txt", filesystem)
1127
1128 // run the first finder
1129 finder := newFinder(
1130 t,
1131 filesystem,
1132 CacheParams{
1133 RootDirs: []string{"/tmp"},
1134 IncludeFiles: []string{"findme.txt"},
1135 },
1136 )
1137 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
1138 finder.Shutdown()
1139 // check the response of the first finder
1140 assertSameResponse(t, foundPaths,
1141 []string{"/tmp/findme.txt",
1142 "/tmp/a/findme.txt",
1143 "/tmp/a/1/findme.txt",
1144 "/tmp/a/1/2/findme.txt",
1145 "/tmp/b/findme.txt"})
1146
1147 // modify the filesystem
1148 filesystem.Clock.Tick()
1149 move(t, "/tmp/a", "/tmp/temp", filesystem)
1150 move(t, "/tmp/b", "/tmp/a", filesystem)
1151 move(t, "/tmp/temp", "/tmp/b", filesystem)
1152 filesystem.ClearMetrics()
1153
1154 // run the second finder
1155 finder2 := finderWithSameParams(t, finder)
1156 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
1157
1158 // check results
1159 assertSameResponse(t, foundPaths,
1160 []string{"/tmp/findme.txt",
1161 "/tmp/a/findme.txt",
1162 "/tmp/b/findme.txt",
1163 "/tmp/b/1/findme.txt",
1164 "/tmp/b/1/2/findme.txt"})
1165 // Technically, we don't care whether /tmp/a/1/2 gets Statted or gets skipped
1166 // if the Finder detects the nonexistence of /tmp/a/1
1167 // However, when resuming from cache, we don't want the Finder to necessarily wait
1168 // to stat a directory until after statting its parent.
1169 // So here we just include /tmp/a/1/2 in the list.
1170 // The Finder is currently implemented to always restat every dir and
1171 // to not short-circuit due to nonexistence of parents (but it will remove
1172 // missing dirs from the cache for next time)
1173 assertSameStatCalls(t, filesystem.StatCalls,
1174 []string{"/tmp", "/tmp/a", "/tmp/a/1", "/tmp/a/1/2", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
1175 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp", "/tmp/a", "/tmp/b", "/tmp/b/1", "/tmp/b/1/2"})
1176 finder2.Shutdown()
1177}
1178
1179// runFsReplacementTest tests a change modifying properties of the filesystem itself:
1180// runFsReplacementTest tests changing the user, the hostname, or the device number
1181// runFsReplacementTest is a helper method called by other tests
1182func runFsReplacementTest(t *testing.T, fs1 *fs.MockFs, fs2 *fs.MockFs) {
1183 // setup fs1
1184 create(t, "/tmp/findme.txt", fs1)
1185 create(t, "/tmp/a/findme.txt", fs1)
1186 create(t, "/tmp/a/a/findme.txt", fs1)
1187
1188 // setup fs2 to have the same directories but different files
1189 create(t, "/tmp/findme.txt", fs2)
1190 create(t, "/tmp/a/findme.txt", fs2)
1191 create(t, "/tmp/a/a/ignoreme.txt", fs2)
1192 create(t, "/tmp/a/b/findme.txt", fs2)
1193
1194 // run the first finder
1195 finder := newFinder(
1196 t,
1197 fs1,
1198 CacheParams{
1199 RootDirs: []string{"/tmp"},
1200 IncludeFiles: []string{"findme.txt"},
1201 },
1202 )
1203 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
1204 finder.Shutdown()
1205 // check the response of the first finder
1206 assertSameResponse(t, foundPaths,
1207 []string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/a/findme.txt"})
1208
1209 // copy the cache data from the first filesystem to the second
1210 cacheContent := read(t, finder.DbPath, fs1)
1211 write(t, finder.DbPath, cacheContent, fs2)
1212
1213 // run the second finder, with the same config and same cache contents but a different filesystem
1214 finder2 := newFinder(
1215 t,
1216 fs2,
1217 CacheParams{
1218 RootDirs: []string{"/tmp"},
1219 IncludeFiles: []string{"findme.txt"},
1220 },
1221 )
1222 foundPaths = finder2.FindNamedAt("/tmp", "findme.txt")
1223
1224 // check results
1225 assertSameResponse(t, foundPaths,
1226 []string{"/tmp/findme.txt", "/tmp/a/findme.txt", "/tmp/a/b/findme.txt"})
1227 assertSameStatCalls(t, fs2.StatCalls,
1228 []string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
1229 assertSameReadDirCalls(t, fs2.ReadDirCalls,
1230 []string{"/tmp", "/tmp/a", "/tmp/a/a", "/tmp/a/b"})
1231 finder2.Shutdown()
1232}
1233
1234func TestChangeOfDevice(t *testing.T) {
1235 fs1 := newFs()
1236 // not as fine-grained mounting controls as a real filesystem, but should be adequate
1237 fs1.SetDeviceNumber(0)
1238
1239 fs2 := newFs()
1240 fs2.SetDeviceNumber(1)
1241
1242 runFsReplacementTest(t, fs1, fs2)
1243}
1244
1245func TestChangeOfUserOrHost(t *testing.T) {
1246 fs1 := newFs()
1247 fs1.SetViewId("me@here")
1248
1249 fs2 := newFs()
1250 fs2.SetViewId("you@there")
1251
1252 runFsReplacementTest(t, fs1, fs2)
1253}
1254
1255func TestConsistentCacheOrdering(t *testing.T) {
1256 // setup filesystem
1257 filesystem := newFs()
1258 for i := 0; i < 5; i++ {
1259 create(t, fmt.Sprintf("/tmp/%v/findme.txt", i), filesystem)
1260 }
1261
1262 // run the first finder
1263 finder := newFinder(
1264 t,
1265 filesystem,
1266 CacheParams{
1267 RootDirs: []string{"/tmp"},
1268 IncludeFiles: []string{"findme.txt"},
1269 },
1270 )
1271 finder.FindNamedAt("/tmp", "findme.txt")
1272 finder.Shutdown()
1273
1274 // read db file
1275 string1 := read(t, finder.DbPath, filesystem)
1276
1277 err := filesystem.Remove(finder.DbPath)
1278 if err != nil {
1279 t.Fatal(err)
1280 }
1281
1282 // run another finder
1283 finder2 := finderWithSameParams(t, finder)
1284 finder2.FindNamedAt("/tmp", "findme.txt")
1285 finder2.Shutdown()
1286
1287 string2 := read(t, finder.DbPath, filesystem)
1288
1289 if string1 != string2 {
1290 t.Errorf("Running Finder twice generated two dbs not having identical contents.\n"+
1291 "Content of first file:\n"+
1292 "\n"+
1293 "%v"+
1294 "\n"+
1295 "\n"+
1296 "Content of second file:\n"+
1297 "\n"+
1298 "%v\n"+
1299 "\n",
1300 string1,
1301 string2,
1302 )
1303 }
1304
1305}
1306
1307func TestNumSyscallsOfSecondFind(t *testing.T) {
1308 // setup filesystem
1309 filesystem := newFs()
1310 create(t, "/tmp/findme.txt", filesystem)
1311 create(t, "/tmp/a/findme.txt", filesystem)
1312 create(t, "/tmp/a/misc.txt", filesystem)
1313
1314 // set up the finder and run it once
1315 finder := newFinder(
1316 t,
1317 filesystem,
1318 CacheParams{
1319 RootDirs: []string{"/tmp"},
1320 IncludeFiles: []string{"findme.txt"},
1321 },
1322 )
1323 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
1324 assertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
1325
1326 filesystem.ClearMetrics()
1327
1328 // run the finder again and confirm it doesn't check the filesystem
1329 refoundPaths := finder.FindNamedAt("/tmp", "findme.txt")
1330 assertSameResponse(t, refoundPaths, foundPaths)
1331 assertSameStatCalls(t, filesystem.StatCalls, []string{})
1332 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
1333
1334 finder.Shutdown()
1335}
1336
1337func TestChangingParamsOfSecondFind(t *testing.T) {
1338 // setup filesystem
1339 filesystem := newFs()
1340 create(t, "/tmp/findme.txt", filesystem)
1341 create(t, "/tmp/a/findme.txt", filesystem)
1342 create(t, "/tmp/a/metoo.txt", filesystem)
1343
1344 // set up the finder and run it once
1345 finder := newFinder(
1346 t,
1347 filesystem,
1348 CacheParams{
1349 RootDirs: []string{"/tmp"},
1350 IncludeFiles: []string{"findme.txt", "metoo.txt"},
1351 },
1352 )
1353 foundPaths := finder.FindNamedAt("/tmp", "findme.txt")
1354 assertSameResponse(t, foundPaths, []string{"/tmp/findme.txt", "/tmp/a/findme.txt"})
1355
1356 filesystem.ClearMetrics()
1357
1358 // run the finder again and confirm it gets the right answer without asking the filesystem
1359 refoundPaths := finder.FindNamedAt("/tmp", "metoo.txt")
1360 assertSameResponse(t, refoundPaths, []string{"/tmp/a/metoo.txt"})
1361 assertSameStatCalls(t, filesystem.StatCalls, []string{})
1362 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
1363
1364 finder.Shutdown()
1365}
1366
1367func TestSymlinkPointingToFile(t *testing.T) {
1368 // setup filesystem
1369 filesystem := newFs()
1370 create(t, "/tmp/a/hi.txt", filesystem)
1371 create(t, "/tmp/a/ignoreme.txt", filesystem)
1372 link(t, "/tmp/hi.txt", "a/hi.txt", filesystem)
1373 link(t, "/tmp/b/hi.txt", "../a/hi.txt", filesystem)
1374 link(t, "/tmp/c/hi.txt", "/tmp/hi.txt", filesystem)
1375 link(t, "/tmp/d/hi.txt", "../a/bye.txt", filesystem)
1376 link(t, "/tmp/d/bye.txt", "../a/hi.txt", filesystem)
1377 link(t, "/tmp/e/bye.txt", "../a/bye.txt", filesystem)
1378 link(t, "/tmp/f/hi.txt", "somethingThatDoesntExist", filesystem)
1379
1380 // set up the finder and run it once
1381 finder := newFinder(
1382 t,
1383 filesystem,
1384 CacheParams{
1385 RootDirs: []string{"/tmp"},
1386 IncludeFiles: []string{"hi.txt"},
1387 },
1388 )
1389 foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
1390 // should search based on the name of the link rather than the destination or validity of the link
1391 correctResponse := []string{
1392 "/tmp/a/hi.txt",
1393 "/tmp/hi.txt",
1394 "/tmp/b/hi.txt",
1395 "/tmp/c/hi.txt",
1396 "/tmp/d/hi.txt",
1397 "/tmp/f/hi.txt",
1398 }
1399 assertSameResponse(t, foundPaths, correctResponse)
1400
1401}
1402
1403func TestSymlinkPointingToDirectory(t *testing.T) {
1404 // setup filesystem
1405 filesystem := newFs()
1406 create(t, "/tmp/dir/hi.txt", filesystem)
1407 create(t, "/tmp/dir/ignoreme.txt", filesystem)
1408
1409 link(t, "/tmp/links/dir", "../dir", filesystem)
1410 link(t, "/tmp/links/link", "../dir", filesystem)
1411 link(t, "/tmp/links/broken", "nothingHere", filesystem)
1412 link(t, "/tmp/links/recursive", "recursive", filesystem)
1413
1414 // set up the finder and run it once
1415 finder := newFinder(
1416 t,
1417 filesystem,
1418 CacheParams{
1419 RootDirs: []string{"/tmp"},
1420 IncludeFiles: []string{"hi.txt"},
1421 },
1422 )
1423
1424 foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
1425
1426 // should completely ignore symlinks that point to directories
1427 correctResponse := []string{
1428 "/tmp/dir/hi.txt",
1429 }
1430 assertSameResponse(t, foundPaths, correctResponse)
1431
1432}
1433
1434// TestAddPruneFile confirms that adding a prune-file (into a directory for which we
1435// already had a cache) causes the directory to be ignored
1436func TestAddPruneFile(t *testing.T) {
1437 // setup filesystem
1438 filesystem := newFs()
1439 create(t, "/tmp/out/hi.txt", filesystem)
1440 create(t, "/tmp/out/a/hi.txt", filesystem)
1441 create(t, "/tmp/hi.txt", filesystem)
1442
1443 // do find
1444 finder := newFinder(
1445 t,
1446 filesystem,
1447 CacheParams{
1448 RootDirs: []string{"/tmp"},
1449 PruneFiles: []string{".ignore-out-dir"},
1450 IncludeFiles: []string{"hi.txt"},
1451 },
1452 )
1453
1454 foundPaths := finder.FindNamedAt("/tmp", "hi.txt")
1455
1456 // check result
1457 assertSameResponse(t, foundPaths,
1458 []string{"/tmp/hi.txt",
1459 "/tmp/out/hi.txt",
1460 "/tmp/out/a/hi.txt"},
1461 )
1462 finder.Shutdown()
1463
1464 // modify filesystem
1465 filesystem.Clock.Tick()
1466 create(t, "/tmp/out/.ignore-out-dir", filesystem)
1467 // run another find and check its result
1468 finder2 := finderWithSameParams(t, finder)
1469 foundPaths = finder2.FindNamedAt("/tmp", "hi.txt")
1470 assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
1471 finder2.Shutdown()
1472}
1473
1474func TestUpdatingDbIffChanged(t *testing.T) {
1475 // setup filesystem
1476 filesystem := newFs()
1477 create(t, "/tmp/a/hi.txt", filesystem)
1478 create(t, "/tmp/b/bye.txt", filesystem)
1479
1480 // run the first finder
1481 finder := newFinder(
1482 t,
1483 filesystem,
1484 CacheParams{
1485 RootDirs: []string{"/tmp"},
1486 IncludeFiles: []string{"hi.txt"},
1487 },
1488 )
1489 foundPaths := finder.FindAll()
1490 filesystem.Clock.Tick()
1491 finder.Shutdown()
1492 // check results
1493 assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
1494
1495 // modify the filesystem
1496 filesystem.Clock.Tick()
1497 create(t, "/tmp/b/hi.txt", filesystem)
1498 filesystem.Clock.Tick()
1499 filesystem.ClearMetrics()
1500
1501 // run the second finder
1502 finder2 := finderWithSameParams(t, finder)
1503 foundPaths = finder2.FindAll()
1504 finder2.Shutdown()
1505 // check results
1506 assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
1507 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{"/tmp/b"})
1508 expectedDbWriteTime := filesystem.Clock.Time()
1509 actualDbWriteTime := modTime(t, finder2.DbPath, filesystem)
1510 if actualDbWriteTime != expectedDbWriteTime {
1511 t.Fatalf("Expected to write db at %v, actually wrote db at %v\n",
1512 expectedDbWriteTime, actualDbWriteTime)
1513 }
1514
1515 // reset metrics
1516 filesystem.ClearMetrics()
1517
1518 // run the third finder
1519 finder3 := finderWithSameParams(t, finder2)
1520 foundPaths = finder3.FindAll()
1521
1522 // check results
1523 assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt", "/tmp/b/hi.txt"})
1524 assertSameReadDirCalls(t, filesystem.ReadDirCalls, []string{})
1525 finder3.Shutdown()
1526 actualDbWriteTime = modTime(t, finder3.DbPath, filesystem)
1527 if actualDbWriteTime != expectedDbWriteTime {
1528 t.Fatalf("Re-wrote db even when contents did not change")
1529 }
1530
1531}
1532
1533func TestDirectoryNotPermitted(t *testing.T) {
1534 // setup filesystem
1535 filesystem := newFs()
1536 create(t, "/tmp/hi.txt", filesystem)
1537 create(t, "/tmp/a/hi.txt", filesystem)
1538 create(t, "/tmp/a/a/hi.txt", filesystem)
1539 create(t, "/tmp/b/hi.txt", filesystem)
1540
1541 // run the first finder
1542 finder := newFinder(
1543 t,
1544 filesystem,
1545 CacheParams{
1546 RootDirs: []string{"/tmp"},
1547 IncludeFiles: []string{"hi.txt"},
1548 },
1549 )
1550 foundPaths := finder.FindAll()
1551 filesystem.Clock.Tick()
1552 finder.Shutdown()
1553 allPaths := []string{"/tmp/hi.txt", "/tmp/a/hi.txt", "/tmp/a/a/hi.txt", "/tmp/b/hi.txt"}
1554 // check results
1555 assertSameResponse(t, foundPaths, allPaths)
1556
1557 // modify the filesystem
1558 filesystem.Clock.Tick()
1559
1560 setReadable(t, "/tmp/a", false, filesystem)
1561 filesystem.Clock.Tick()
1562
1563 // run the second finder
1564 finder2 := finderWithSameParams(t, finder)
1565 foundPaths = finder2.FindAll()
1566 finder2.Shutdown()
1567 // check results
1568 assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt", "/tmp/b/hi.txt"})
1569
1570 // modify the filesystem back
1571 setReadable(t, "/tmp/a", true, filesystem)
1572
1573 // run the third finder
1574 finder3 := finderWithSameParams(t, finder2)
1575 foundPaths = finder3.FindAll()
1576 finder3.Shutdown()
1577 // check results
1578 assertSameResponse(t, foundPaths, allPaths)
1579}
1580
1581func TestFileNotPermitted(t *testing.T) {
1582 // setup filesystem
1583 filesystem := newFs()
1584 create(t, "/tmp/hi.txt", filesystem)
1585 setReadable(t, "/tmp/hi.txt", false, filesystem)
1586
1587 // run the first finder
1588 finder := newFinder(
1589 t,
1590 filesystem,
1591 CacheParams{
1592 RootDirs: []string{"/tmp"},
1593 IncludeFiles: []string{"hi.txt"},
1594 },
1595 )
1596 foundPaths := finder.FindAll()
1597 filesystem.Clock.Tick()
1598 finder.Shutdown()
1599 // check results
1600 assertSameResponse(t, foundPaths, []string{"/tmp/hi.txt"})
1601}
Jeff Gastonb629e182017-08-14 16:49:18 -07001602
1603func TestCacheEntryPathUnexpectedError(t *testing.T) {
1604 // setup filesystem
1605 filesystem := newFs()
1606 create(t, "/tmp/a/hi.txt", filesystem)
1607
1608 // run the first finder
1609 finder := newFinder(
1610 t,
1611 filesystem,
1612 CacheParams{
1613 RootDirs: []string{"/tmp"},
1614 IncludeFiles: []string{"hi.txt"},
1615 },
1616 )
1617 foundPaths := finder.FindAll()
1618 filesystem.Clock.Tick()
1619 finder.Shutdown()
1620 // check results
1621 assertSameResponse(t, foundPaths, []string{"/tmp/a/hi.txt"})
1622
1623 // make the directory not readable
1624 setReadErr(t, "/tmp/a", os.ErrInvalid, filesystem)
1625
1626 // run the second finder
1627 _, err := finderAndErrorWithSameParams(t, finder)
1628 if err == nil {
1629 fatal(t, "Failed to detect unexpected filesystem error")
1630 }
1631}