Skip to content

Commit 5a255f6

Browse files
committed
feature: lute test runs specific tests
1 parent 5b0676b commit 5a255f6

File tree

5 files changed

+176
-18
lines changed

5 files changed

+176
-18
lines changed

lute/cli/commands/test/filter.luau

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
local testtypes = require("@std/test/types")
2+
3+
local function filtertests(
4+
env: testtypes.testenvironment,
5+
suiteName: string?,
6+
caseName: string?
7+
): testtypes.testenvironment
8+
local filtered: testtypes.testenvironment = {
9+
anonymous = {},
10+
suites = {},
11+
suiteindex = {},
12+
caseindex = {},
13+
}
14+
15+
-- No filters: return all tests
16+
if not suiteName and not caseName then
17+
return env
18+
end
19+
20+
-- Filter by case name only
21+
if caseName and not suiteName then
22+
local caseEntry = env.caseindex[caseName]
23+
if caseEntry then
24+
-- Add all anonymous tests with this case name
25+
filtered.anonymous = caseEntry.anonymous
26+
27+
-- Add all suites that have tests with this case name
28+
for sName, cases in caseEntry.suites do
29+
local suite = env.suiteindex[sName]
30+
local filteredSuite = {
31+
name = suite.name,
32+
cases = cases,
33+
_beforeeach = suite._beforeeach,
34+
_beforeall = suite._beforeall,
35+
_aftereach = suite._aftereach,
36+
_afterall = suite._afterall,
37+
}
38+
table.insert(filtered.suites, filteredSuite)
39+
end
40+
end
41+
return filtered
42+
end
43+
44+
-- Filter by suite name only
45+
if suiteName and not caseName then
46+
local suite = env.suiteindex[suiteName]
47+
if suite then
48+
table.insert(filtered.suites, suite)
49+
end
50+
return filtered
51+
end
52+
53+
-- Filter by both suite and case name
54+
if suiteName and caseName then
55+
local caseEntry = env.caseindex[caseName]
56+
if caseEntry and caseEntry.suites[suiteName] then
57+
local suite = env.suiteindex[suiteName]
58+
local filteredSuite = {
59+
name = suite.name,
60+
cases = caseEntry.suites[suiteName],
61+
_beforeeach = suite._beforeeach,
62+
_beforeall = suite._beforeall,
63+
_aftereach = suite._aftereach,
64+
_afterall = suite._afterall,
65+
}
66+
table.insert(filtered.suites, filteredSuite)
67+
end
68+
return filtered
69+
end
70+
71+
return filtered
72+
end
73+
74+
return table.freeze({ filtertests = filtertests })

lute/cli/commands/test/init.luau

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local finder = require("@self/finder")
2+
local filter = require("@self/filter")
23
local luau = require("@std/luau")
34
local path = require("@std/path")
45
local ps = require("@std/process")
@@ -16,14 +17,10 @@ Run tests discovered in .test.luau and .spec.luau files.
1617
OPTIONS:
1718
-h, --help Show this help message
1819
--list List all discovered test cases without running them
19-
--filter=PATTERN Run only tests matching PATTERN
20-
Examples:
21-
--filter=SuiteName (run all tests in suite)
22-
--filter=SuiteName.testname (run specific test)
23-
--filter=testname (run anonymous test)
24-
--reporter=REPORTER Choose test reporter (default: rich)
20+
-s, --suite=SUITE Run only tests in the specified suite
21+
-c, --case=CASE Run only test cases matching the specified name
22+
--reporter=REPORTER Choose test reporter (default: simple)
2523
Options:
26-
rich - Color output with visual diffs
2724
simple - Plain text output
2825
<path> - Custom reporter from file path
2926
@@ -33,8 +30,9 @@ PATHS:
3330
EXAMPLES:
3431
lute test Run all tests in ./tests
3532
lute test --list List all test cases
36-
lute test --filter=MyTestSuite Run all tests in MyTestSuite
37-
lute test --reporter=simple src/ Run tests with simple reporter
33+
lute test -s MyTestSuite Run all tests in MyTestSuite
34+
lute test --suite MyTestSuite --case mytest Run specific test in suite
35+
lute test --case "some case" Run all test cases named "some case"
3836
]])
3937
end
4038

@@ -102,25 +100,52 @@ local function main(...: string)
102100
local args = { ... }
103101
local testPaths = {}
104102
local isListMode = false
103+
local suiteName: string? = nil
104+
local caseName: string? = nil
105+
106+
local i = 1
107+
while i <= #args do
108+
local arg = args[i]
105109

106-
for _, arg in args do
107110
if arg == "-h" or arg == "--help" then
108111
printHelp()
109112
return
110113
elseif arg == "--list" then
111114
isListMode = true
115+
i += 1
116+
elseif arg == "-s" or arg == "--suite" then
117+
i += 1
118+
if i <= #args then
119+
suiteName = args[i]
120+
i += 1
121+
else
122+
print(`Error: {arg} requires a value`)
123+
ps.exit(1)
124+
end
125+
elseif arg == "-c" or arg == "--case" then
126+
i += 1
127+
if i <= #args then
128+
caseName = args[i]
129+
i += 1
130+
else
131+
print(`Error: {arg} requires a value`)
132+
ps.exit(1)
133+
end
112134
else
113135
table.insert(testPaths, arg)
136+
i += 1
114137
end
115138
end
139+
116140
local searchpath = if #testPaths > 0 then testPaths else { "./tests" }
117141
loadtests(finder.findtestfiles(searchpath))
118142

119143
if isListMode then
120144
listtests()
121145
else
122146
local env = test._registered()
123-
runtests(env, runner.run, reporter.simple)
147+
local filteredEnv = filter.filtertests(env, suiteName, caseName)
148+
runtests(filteredEnv, runner.run, reporter.simple)
124149
end
125150
end
126151

lute/std/libs/test/init.luau

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,52 @@ function TestSuite:afterall(hook: () -> ())
4949
self._afterall = hook
5050
end
5151

52-
local env: TestEnvironment = { anonymous = {}, suites = {}, reporter = reporter.simple }
52+
local env: TestEnvironment = { anonymous = {}, suites = {}, suiteindex = {}, caseindex = {} }
5353

5454
-- Test library export surface
5555
local test = {}
5656

57-
function test.setreporter(reporter: TestReporter)
58-
test.reporter = reporter
59-
end
60-
6157
-- register an anonymous test case
6258
function test.case(name: string, case: Test)
6359
local testcase = { name = name, case = case }
6460
table.insert(env.anonymous, testcase)
61+
62+
-- Update caseindex
63+
if not env.caseindex[name] then
64+
env.caseindex[name] = { anonymous = {}, suites = {} }
65+
end
66+
table.insert(env.caseindex[name].anonymous, testcase)
6567
end
6668

6769
-- register a test suite
6870
function test.suite(name: string, registerFn: (TestSuite) -> ())
6971
local suite = TestSuite.new(name)
7072
registerFn(suite)
7173
table.insert(env.suites, suite)
74+
75+
-- Update suiteindex
76+
env.suiteindex[name] = suite
77+
78+
-- Update caseindex for all cases in this suite
79+
for _, testcase in suite.cases do
80+
if not env.caseindex[testcase.name] then
81+
env.caseindex[testcase.name] = { anonymous = {}, suites = {} }
82+
end
83+
if not env.caseindex[testcase.name].suites[name] then
84+
env.caseindex[testcase.name].suites[name] = {}
85+
end
86+
table.insert(env.caseindex[testcase.name].suites[name], testcase)
87+
end
7288
end
7389

7490
-- get all registered tests without running them (internal)
7591
function test._registered()
76-
return table.freeze({ anonymous = env.anonymous, suites = env.suites })
92+
return table.freeze({
93+
anonymous = env.anonymous,
94+
suites = env.suites,
95+
suiteindex = env.suiteindex,
96+
caseindex = env.caseindex,
97+
})
7798
end
7899

79100
-- run all the tests

lute/std/libs/test/types.luau

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,16 @@ export type testrunresult = {
4141
export type testreporter = (testrunresult) -> ()
4242

4343
-- Testing Environment
44+
export type caseindexentry = {
45+
anonymous: { testcase },
46+
suites: { [string]: { testcase } },
47+
}
48+
4449
export type testenvironment = {
4550
anonymous: { testcase },
4651
suites: { testsuite },
47-
reporter: testreporter,
52+
suiteindex: { [string]: testsuite },
53+
caseindex: { [string]: caseindexentry },
4854
}
4955

5056
export type testrunner = (testenvironment) -> testrunresult

tests/cli/test.test.luau

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ test.run()
127127

128128
fs.remove(testFilePath)
129129
end)
130+
<<<<<<< Conflict 1 of 1
131+
+++++++ Contents of side #1
130132

131133
suite:case("assert.eq_error_message_in_case", function(assert)
132134
assertErrorsWithMsg(
@@ -368,6 +370,36 @@ test.run()
368370
assert
369371
)
370372
end)
373+
374+
suite:case("lute test filters by suite name", function(assert)
375+
-- Run lute test with --suite flag
376+
local result = process.run({ lutePath, "test", "-s", "SmokeSuite", "tests/cli/discovery" })
377+
378+
-- Check that it runs only tests in SmokeSuite
379+
assert.eq(result.exitcode, 0)
380+
assert.neq(result.stdout:find("Total: 2", 1, true), nil)
381+
assert.neq(result.stdout:find("Passed: 2", 1, true), nil)
382+
end)
383+
384+
suite:case("lute test filters by case name", function(assert)
385+
-- Run lute test with --case flag
386+
local result = process.run({ lutePath, "test", "--case", "make_pass", "tests/cli/discovery" })
387+
388+
-- Check that it runs only tests named make_pass
389+
assert.eq(result.exitcode, 0)
390+
assert.neq(result.stdout:find("Total: 1", 1, true), nil)
391+
assert.neq(result.stdout:find("Passed: 1", 1, true), nil)
392+
end)
393+
394+
suite:case("lute test filters by suite and case name", function(assert)
395+
-- Run lute test with both --suite and --case flags
396+
local result = process.run({ lutePath, "test", "-s", "SmokeSuite", "-c", "make_fail", "tests/cli/discovery" })
397+
398+
-- Check that it runs only make_fail in SmokeSuite
399+
assert.eq(result.exitcode, 0)
400+
assert.neq(result.stdout:find("Total: 1", 1, true), nil)
401+
assert.neq(result.stdout:find("Passed: 1", 1, true), nil)
402+
end)
371403
end)
372404

373405
test.run()

0 commit comments

Comments
 (0)