Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Discover Go tests by static regex instead of go test -list
GoDirectory.tests used to spin up a Go container and parse
'go test -list' stdout to enumerate test names. Replace that with a
single Workspace.directory(...).search(pattern: "^func Test\\w+\\(")
over the directory's *_test.go files and parse names from the matched
lines in Dang.

This is much faster (no compile, no container exec) and matches the
same set go test -list does for top-level test functions. Test methods
on testify-style suites are intentionally not listed: they cannot be
selected with go test -run independently of their parent function.

Add moduleIntrospectionCheck assertions covering the new GoTestDirs
and GoTests collection surface end-to-end: keys/get round-trip on both
levels, multi-test discovery from a single *_test.go file (cross-
include-b has two top-level tests), and that a skip-configured
directory produces an empty tests collection.

Signed-off-by: Solomon Hykes <solomon@dagger.io>
  • Loading branch information
shykes committed Jun 11, 2026
commit 6ea03faa593c2fb80c9333d4934547831e70ef79
50 changes: 50 additions & 0 deletions .dagger/modules/e2e/main.dang
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,56 @@ type E2e {
),
"go:generate scoped include did not include generator support files",
)

# Test directory collection surface and static test discovery.
# cross-include-b has two top-level Test functions in a single file —
# exercises ripgrep-based listing and multi-match handling.
let crossB = tool.module(ws, "fixtures/go-module-cross-include-b")
let crossBTestDirs = crossB.testDirs(ws)
assert(
containsPath(crossBTestDirs.keys, "fixtures/go-module-cross-include-b"),
"testDirs collection did not include expected test directory",
)
assert(
crossBTestDirs
.get(key: "fixtures/go-module-cross-include-b")
.path == "fixtures/go-module-cross-include-b",
"testDirs collection get did not return the requested directory",
)

let crossBTests = crossBTestDirs
.get(key: "fixtures/go-module-cross-include-b")
.tests()
assert(
crossBTests.keys.length == 2,
"tests collection did not discover expected number of tests",
)
assert(
containsPath(crossBTests.keys, "TestSiblingModuleDirectiveIncludeIsNotMounted"),
"tests collection did not include expected test name",
)
assert(
containsPath(crossBTests.keys, "TestSiblingModuleTestdataIsNotMounted"),
"tests collection did not include second test from same file",
)
assert(
crossBTests
.get(key: "TestSiblingModuleDirectiveIncludeIsNotMounted")
.name == "TestSiblingModuleDirectiveIncludeIsNotMounted",
"tests collection get did not return the requested test",
)

# Skip-configured directory should report no tests at all.
let skippedTests = tool
.module(ws, "testdata/go-module-excluded")
.testDirs(ws)
.get(key: "testdata/go-module-excluded")
.tests()
assert(
skippedTests.keys.length == 0,
"tests collection on a skipped directory was not empty",
)

null
}

Expand Down
19 changes: 15 additions & 4 deletions go.dang
Original file line number Diff line number Diff line change
Expand Up @@ -865,15 +865,26 @@ type GoDirectory {
"""
The tests in this directory, as a collection keyed by test name.
Skipped directories produce an empty collection.

Test names are discovered by statically matching top-level test function
declarations in *_test.go files, without compiling or running go test.
"""
pub tests(): GoTests! {
let names = if (skipTest) {
[]
} else {
testContainer()
.withExec(["sh", "-c", "go test -list '.*' . | grep '^Test' || true"])
.stdout
.split("\n")
let glob = if (path == ".") {
"*_test.go"
} else {
path.trimSuffix("/") + "/*_test.go"
}
ws
.directory("/", include: [glob])
.search(pattern: "^func Test\\w+\\(")
.{matchedLines}
.map { result =>
result.matchedLines.trimSpace.trimPrefix("func ").split("(")[0] ?? ""
}
.filter { name => name != "" }
}
GoTests(names: names, dir: self)
Expand Down