Skip to content

Commit f36c41d

Browse files
committed
feat(test): add -r/--recursive flag for test discovery
Default behavior is now non-recursive (only test the current/specified directory). Use -r to walk subdirectories and discover all test packages.
1 parent 94b418f commit f36c41d

6 files changed

Lines changed: 83 additions & 13 deletions

File tree

testrunner/runner.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type RunOptions struct {
6666
TodoTemplate string `json:"todo_template,omitempty" flag:"todo-template"` // Path to TODO template file
6767
WorkDir string `json:"work_dir,omitempty" flag:"work-dir"` // Working directory to run tests in
6868
DryRun bool `json:"dry_run,omitempty" flag:"dry-run"` // Show what tests would be executed without running them
69+
Recursive bool `json:"recursive,omitempty" flag:"recursive,r"` // Recursively discover test packages in subdirectories
6970
}
7071

7172
func (opts RunOptions) Pretty() api.Text {
@@ -231,7 +232,7 @@ func (o *TestOrchestrator) detectAndRun(frameworks []Framework, startingPaths []
231232
if len(startingPaths) > 0 {
232233
packages, err = o.discoverPackagesInPaths(runner, startingPaths)
233234
} else {
234-
packages, err = runner.DiscoverPackages(o.WorkDir)
235+
packages, err = runner.DiscoverPackages(o.WorkDir, o.Recursive)
235236
}
236237

237238
if err != nil {
@@ -400,7 +401,7 @@ func (o *TestOrchestrator) discoverPackagesInPaths(runner runners.Runner, starti
400401
}
401402
logger.Debugf("Discovering packages in path: %s", fullPath)
402403

403-
packages, err := runner.DiscoverPackages(fullPath)
404+
packages, err := runner.DiscoverPackages(fullPath, o.Recursive)
404405
if err != nil {
405406
return nil, fmt.Errorf("failed to discover packages in %s: %w", startPath, err)
406407
}

testrunner/runners/ginkgo.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,20 @@ func (r *Ginkgo) Detect(workDir string) (bool, error) {
5656
return found, err
5757
}
5858

59-
// DiscoverPackages returns all packages with Ginkgo tests.
60-
func (r *Ginkgo) DiscoverPackages(workDir string) ([]string, error) {
59+
// DiscoverPackages returns packages with Ginkgo tests.
60+
// When recursive is false, only the given directory is checked.
61+
func (r *Ginkgo) DiscoverPackages(workDir string, recursive bool) ([]string, error) {
62+
if !recursive {
63+
if r.dirHasGinkgoTests(workDir) {
64+
return []string{r.getRelativePath(workDir)}, nil
65+
}
66+
return nil, nil
67+
}
68+
6169
var packages []string
6270
seen := make(map[string]bool)
6371

6472
err := filepath.Walk(workDir, func(path string, info os.FileInfo, err error) error {
65-
6673
if err != nil {
6774
return err
6875
}
@@ -139,6 +146,21 @@ func (r *Ginkgo) NormalizeFilePath(filePath string) string {
139146
return r.normalizeFilePath(filePath)
140147
}
141148

149+
func (r *Ginkgo) dirHasGinkgoTests(dir string) bool {
150+
entries, err := os.ReadDir(dir)
151+
if err != nil {
152+
return false
153+
}
154+
for _, entry := range entries {
155+
if !entry.IsDir() && strings.HasSuffix(entry.Name(), "_test.go") {
156+
if r.hasGinkgoImports(filepath.Join(dir, entry.Name())) {
157+
return true
158+
}
159+
}
160+
}
161+
return false
162+
}
163+
142164
// hasGinkgoImports checks if a file imports ginkgo.
143165
func (r *Ginkgo) hasGinkgoImports(path string) bool {
144166
content, err := os.ReadFile(path)

testrunner/runners/ginkgo_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ import . "github.com/onsi/ginkgo/v2"
9595
}
9696

9797
runner := NewGinkgo(tmpDir)
98-
packages, err := runner.DiscoverPackages(tmpDir)
98+
packages, err := runner.DiscoverPackages(tmpDir, true)
9999
if err != nil {
100100
t.Fatalf("unexpected error: %v", err)
101101
}

testrunner/runners/gotest.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,16 @@ func (r *GoTest) packageHasNonGinkgoTests(pkgDir string) bool {
7676
return false
7777
}
7878

79-
// DiscoverPackages returns all packages with go test files (excluding Ginkgo-only packages).
80-
func (r *GoTest) DiscoverPackages(workDir string) ([]string, error) {
79+
// DiscoverPackages returns packages with go test files (excluding Ginkgo-only packages).
80+
// When recursive is false, only the given directory is checked.
81+
func (r *GoTest) DiscoverPackages(workDir string, recursive bool) ([]string, error) {
82+
if !recursive {
83+
if r.packageHasNonGinkgoTests(workDir) {
84+
return []string{r.getRelativePath(workDir)}, nil
85+
}
86+
return nil, nil
87+
}
88+
8189
var packages []string
8290
seen := make(map[string]bool)
8391

@@ -90,7 +98,6 @@ func (r *GoTest) DiscoverPackages(workDir string) ([]string, error) {
9098
pkgDir := filepath.Dir(path)
9199
if !seen[pkgDir] {
92100
seen[pkgDir] = true
93-
// Only include package if it has non-Ginkgo tests
94101
if r.packageHasNonGinkgoTests(pkgDir) {
95102
relPath := r.getRelativePath(pkgDir)
96103
packages = append(packages, relPath)

testrunner/runners/gotest_test.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func TestGoTestDiscoverPackages(t *testing.T) {
5454
}
5555

5656
runner := NewGoTest(tmpDir)
57-
packages, err := runner.DiscoverPackages(tmpDir)
57+
packages, err := runner.DiscoverPackages(tmpDir, true)
5858
if err != nil {
5959
t.Fatalf("unexpected error: %v", err)
6060
}
@@ -71,6 +71,45 @@ func TestGoTestDiscoverPackages(t *testing.T) {
7171
}
7272
}
7373

74+
func TestGoTestDiscoverPackagesNonRecursive(t *testing.T) {
75+
tmpDir := t.TempDir()
76+
77+
// Create test file in root and in a subdirectory
78+
if err := os.WriteFile(filepath.Join(tmpDir, "root_test.go"), []byte("package root\n"), 0644); err != nil {
79+
t.Fatal(err)
80+
}
81+
subDir := filepath.Join(tmpDir, "sub")
82+
if err := os.MkdirAll(subDir, 0755); err != nil {
83+
t.Fatal(err)
84+
}
85+
if err := os.WriteFile(filepath.Join(subDir, "sub_test.go"), []byte("package sub\n"), 0644); err != nil {
86+
t.Fatal(err)
87+
}
88+
89+
runner := NewGoTest(tmpDir)
90+
91+
// Non-recursive should only find root
92+
packages, err := runner.DiscoverPackages(tmpDir, false)
93+
if err != nil {
94+
t.Fatalf("unexpected error: %v", err)
95+
}
96+
if len(packages) != 1 {
97+
t.Fatalf("expected 1 package, got %d: %v", len(packages), packages)
98+
}
99+
if packages[0] != "./." {
100+
t.Errorf("expected './.', got %s", packages[0])
101+
}
102+
103+
// Recursive should find both
104+
packages, err = runner.DiscoverPackages(tmpDir, true)
105+
if err != nil {
106+
t.Fatalf("unexpected error: %v", err)
107+
}
108+
if len(packages) != 2 {
109+
t.Fatalf("expected 2 packages, got %d: %v", len(packages), packages)
110+
}
111+
}
112+
74113
func TestGoTestPackageHasTests(t *testing.T) {
75114
tmpDir := t.TempDir()
76115
pkgDir := filepath.Join(tmpDir, "pkg")
@@ -214,7 +253,7 @@ func TestExample(t *testing.T) {}
214253
}
215254

216255
runner := NewGoTest(tmpDir)
217-
packages, err := runner.DiscoverPackages(tmpDir)
256+
packages, err := runner.DiscoverPackages(tmpDir, true)
218257
if err != nil {
219258
t.Fatalf("unexpected error: %v", err)
220259
}

testrunner/runners/types.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ type Runner interface {
1818
// Detect checks if this framework is used in the given working directory.
1919
Detect(workDir string) (bool, error)
2020

21-
// DiscoverPackages returns all packages with tests for this framework.
22-
DiscoverPackages(workDir string) ([]string, error)
21+
// DiscoverPackages returns packages with tests for this framework.
22+
// When recursive is true, it walks subdirectories; otherwise only the given directory.
23+
DiscoverPackages(workDir string, recursive bool) ([]string, error)
2324

2425
// PackageHasTests checks if a package has tests for this framework.
2526
PackageHasTests(packagePath string) (bool, error)

0 commit comments

Comments
 (0)