diff --git a/search/files.go b/search/files.go index c5c19714..9d415107 100644 --- a/search/files.go +++ b/search/files.go @@ -64,7 +64,7 @@ func readFileLines(path string) ([]string, error) { return lines, nil } -func readFiles(ctx context.Context, files chan<- file, workspace string) error { +func readFiles(ctx context.Context, files chan<- file, workspace, subdirectory string) error { defer close(files) ignoreFiles := []string{".gitignore", ".ignore", ".ldignore"} allIgnores := newIgnore(workspace, ignoreFiles) @@ -108,9 +108,20 @@ func readFiles(ctx context.Context, files chan<- file, workspace string) error { return nil } - files <- file{path: strings.TrimPrefix(path, workspace+"/"), lines: lines} + resolvedPath := resolvePath(path, workspace, subdirectory) + + files <- file{path: resolvedPath, lines: lines} return nil } return filepath.Walk(workspace, readFile) } + +func resolvePath(path, workspace, subdirectory string) string { + dir := workspace + if subdirectory != "" { + dir = strings.TrimSuffix(workspace, "/"+subdirectory) + } + + return strings.TrimPrefix(path, dir+"/") +} diff --git a/search/files_test.go b/search/files_test.go index 0b2ea2b0..2694256c 100644 --- a/search/files_test.go +++ b/search/files_test.go @@ -11,7 +11,7 @@ import ( func Test_readFiles(t *testing.T) { t.Run("don't ignore .github by default", func(t *testing.T) { files := make(chan file, 8) - err := readFiles(context.Background(), files, "testdata/include-github-files") + err := readFiles(context.Background(), files, "testdata/include-github-files", "") require.NoError(t, err) got := []file{} for file := range files { @@ -33,25 +33,72 @@ func Test_readFiles(t *testing.T) { }) t.Run("explicitly ignore .github files", func(t *testing.T) { - files := make(chan file, 8) - err := readFiles(context.Background(), files, "testdata/exclude-github-files") - require.NoError(t, err) - got := []file{} - for file := range files { - got = append(got, file) - switch file.path { - case "fileWithNoRefs": - assert.Equal(t, []string{"fileWithNoRefs"}, file.lines) - case "fileWithRefs": - assert.Equal(t, testFile.lines, file.lines) - case "ignoredFiles/included": - assert.Equal(t, []string{"IGNORED BUT INCLUDED"}, file.lines) - case "symlink": - assert.Fail(t, "Should not read symlink contents") - default: - assert.Fail(t, "Read unexpected file", file) + t.Run("without subdirectory option", func(t *testing.T) { + files := make(chan file, 8) + err := readFiles(context.Background(), files, "testdata/exclude-github-files", "") + require.NoError(t, err) + got := []file{} + for file := range files { + got = append(got, file) + switch file.path { + case "fileWithNoRefs": + assert.Equal(t, []string{"fileWithNoRefs"}, file.lines) + case "fileWithRefs": + assert.Equal(t, testFile.lines, file.lines) + case "subdir/fileWithNoRefs": + assert.Equal(t, []string{"nope"}, file.lines) + case "subdir/fileWithRefs": + assert.Equal(t, testFileWithSubdir.lines, file.lines) + case "ignoredFiles/included": + assert.Equal(t, []string{"IGNORED BUT INCLUDED"}, file.lines) + case "symlink": + assert.Fail(t, "Should not read symlink contents") + default: + assert.Fail(t, "Read unexpected file", file) + } } - } - assert.Len(t, got, 3, "Expected 3 valid files to have been found") + assert.Len(t, got, 5, "Expected 5 valid files to have been found") + }) + + t.Run("with subdirectory option", func(t *testing.T) { + files := make(chan file, 8) + err := readFiles(context.Background(), files, "testdata/exclude-github-files/subdir", "subdir") + require.NoError(t, err) + got := []file{} + for file := range files { + got = append(got, file) + switch file.path { + case "subdir/fileWithNoRefs": + assert.Equal(t, []string{"nope"}, file.lines) + case "subdir/fileWithRefs": + assert.Equal(t, testFileWithSubdir.lines, file.lines) + default: + assert.Fail(t, "Read unexpected file", file) + } + } + assert.Len(t, got, 2, "Expected 2 valid files to have been found") + }) }) } + +func Test_resolvePath(t *testing.T) { + testCases := []struct{ name, path, workspace, subdirectory, expectedPath string }{{ + name: "with subdirectory", + path: "/path/to/workspace/subdirectory/internal/file.txt", + workspace: "/path/to/workspace", + subdirectory: "subdirectory", + expectedPath: "subdirectory/internal/file.txt", + }, { + name: "without subdirectory", + path: "/path/to/workspace/file.txt", + workspace: "/path/to/workspace", + subdirectory: "", + expectedPath: "file.txt", + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expectedPath, resolvePath(tc.path, tc.workspace, tc.subdirectory)) + }) + } +} diff --git a/search/scan.go b/search/scan.go index 165e7886..ea349f8f 100644 --- a/search/scan.go +++ b/search/scan.go @@ -19,7 +19,7 @@ func Scan(opts options.Options, repoParams ld.RepoParams, dir string) (Matcher, searchDir = filepath.Join(dir, opts.Subdirectory) } - refs, err := SearchForRefs(searchDir, matcher) + refs, err := SearchForRefs(searchDir, opts.Subdirectory, matcher) if err != nil { log.Error.Fatalf("error searching for flag key references: %s", err) } diff --git a/search/search.go b/search/search.go index c63ec279..a8d33023 100644 --- a/search/search.go +++ b/search/search.go @@ -104,6 +104,7 @@ func (f file) aggregateHunksForFlag(projKey, flagKey string, matcher Matcher, li func (f file) toHunks(matcher Matcher) *ld.ReferenceHunksRep { hunks := make([]ld.HunkRep, 0) filteredMatchers := make([]ElementMatcher, 0) + for _, elementSearch := range matcher.Elements { if elementSearch.Dir != "" { matchDir := strings.HasPrefix(f.path, elementSearch.Dir) @@ -192,7 +193,7 @@ func processFiles(ctx context.Context, files <-chan file, references chan<- ld.R w.Wait() } -func SearchForRefs(directory string, matcher Matcher) ([]ld.ReferenceHunksRep, error) { +func SearchForRefs(directory, subdirectory string, matcher Matcher) ([]ld.ReferenceHunksRep, error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() files := make(chan file) @@ -200,7 +201,7 @@ func SearchForRefs(directory string, matcher Matcher) ([]ld.ReferenceHunksRep, e // Start workers to process files asynchronously as they are written to the files channel go processFiles(ctx, files, references, matcher) - err := readFiles(ctx, files, directory) + err := readFiles(ctx, files, directory, subdirectory) if err != nil { return nil, err } diff --git a/search/search_test.go b/search/search_test.go index b32f0cf7..cb6bee7e 100644 --- a/search/search_test.go +++ b/search/search_test.go @@ -48,6 +48,11 @@ var ( *withFlagKey(withAliases(makeHunkPtr(5, testFlag2Alias), testFlag2Alias), testFlagKey2), } + testFileWithSubdir = file{ + path: "subdir/fileWithRefs", + lines: []string{testFlagKey, testFlagKey2}, + } + delimitedTestFlagKey = delimit(testFlagKey, `"`) ) @@ -386,18 +391,40 @@ func Test_processFiles(t *testing.T) { func Test_SearchForRefs(t *testing.T) { os.Symlink("testdata/exclude-github-files/fileWithRefs", "testdata/exclude-github-files/symlink") - want := []ld.ReferenceHunksRep{{Path: testFile.path}} - matcher := Matcher{ - ctxLines: 0, - } + + matcher := Matcher{ctxLines: 0} matcher.Elements = append(matcher.Elements, NewElementMatcher("default", "", "", []string{testFlagKey, testFlagKey2}, nil), ) + + t.Run("without subdirectory option finds both files", func(t *testing.T) { + actual, err := SearchForRefs("testdata/exclude-github-files", "", matcher) + require.NoError(t, err) + require.Len(t, actual, 2) + + var foundFirst, foundSecond bool + for _, r := range actual { + switch r.Path { + case testFile.path: + foundFirst = true + case testFileWithSubdir.path: + foundSecond = true + default: + t.Fatal("found unexpected file " + r.Path) + } + } + require.True(t, foundFirst) + require.True(t, foundSecond) + }) + + t.Run("with subdirectory option finds only the file in the subdirectory", func(t *testing.T) { + actual, err := SearchForRefs("testdata/exclude-github-files/subdir", "subdir", matcher) + require.NoError(t, err) + require.Len(t, actual, 1) + require.Equal(t, testFileWithSubdir.path, actual[0].Path) + }) + t.Cleanup(func() { os.Remove("testdata/exclude-github-files/symlink") }) - got, err := SearchForRefs("testdata/exclude-github-files", matcher) - require.NoError(t, err) - require.Len(t, got, 1) - require.Equal(t, want[0].Path, got[0].Path) } func Test_truncateLine(t *testing.T) { diff --git a/search/testdata/exclude-github-files/subdir/fileWithNoRefs b/search/testdata/exclude-github-files/subdir/fileWithNoRefs new file mode 100644 index 00000000..1634764a --- /dev/null +++ b/search/testdata/exclude-github-files/subdir/fileWithNoRefs @@ -0,0 +1 @@ +nope diff --git a/search/testdata/exclude-github-files/subdir/fileWithRefs b/search/testdata/exclude-github-files/subdir/fileWithRefs new file mode 100644 index 00000000..dea5c807 --- /dev/null +++ b/search/testdata/exclude-github-files/subdir/fileWithRefs @@ -0,0 +1,2 @@ +someFlag +anotherFlag