Skip to content

Commit da5779a

Browse files
authored
Merge pull request #41 from NickHackman/support-executing-directories-of-tests
feat: Support executing directories of tests + GitHub Actions
2 parents 6dda254 + c35fe64 commit da5779a

25 files changed

+376
-174
lines changed

.github/workflows/test.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Tests
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
tests:
7+
name: tests
8+
runs-on: ${{ matrix.os }}
9+
strategy:
10+
fail-fast: false
11+
matrix:
12+
os: [ubuntu-22.04]
13+
rev: [nightly]
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- uses: rhysd/action-setup-vim@v1
19+
with:
20+
neovim: true
21+
version: ${{ matrix.rev }}
22+
23+
- name: Run tests
24+
run: |
25+
nvim --version
26+
make test

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
/lua/tests/example_project/gradle/wrapper/**
21
.luarc.json
2+
.tests

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.PHONY: test clean
2+
3+
test:
4+
nvim --headless --noplugin -u tests/bootstrap_init.lua -c "PlenaryBustedDirectory tests/ { minimal_init = './tests/minimal_init.lua', timeout = 50000 }"
5+
6+
clean:
7+
rm -rf .tests

lua/neotest-kotlin/init.lua

Lines changed: 82 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ M.Adapter = { name = "neotest-kotest" }
2020
---@param dir string @Directory to treat as cwd
2121
---@return string | nil @Absolute root dir of test suite
2222
function M.Adapter.root(dir)
23-
return lib.files.match_root_pattern("gradlew")(dir)
23+
return lib.files.match_root_pattern("gradlew")(dir)
2424
end
2525

2626
---Filter directories when searching for test files
@@ -30,84 +30,100 @@ end
3030
---@param root string Root directory of project
3131
---@return boolean
3232
function M.Adapter.filter_dir(name, rel_path, root)
33-
return filter.test_directory(name)
33+
return filter.test_directory(name)
3434
end
3535

3636
---@async
3737
---@param file_path string
3838
---@return boolean
3939
function M.Adapter.is_test_file(file_path)
40-
return filter.is_test_file(file_path)
40+
return filter.is_test_file(file_path)
4141
end
4242

4343
---Given a file path, parse all the tests within it.
4444
---@async
4545
---@param file_path string Absolute file path
4646
---@return neotest.Tree | nil
4747
function M.Adapter.discover_positions(file_path)
48-
local positions = lib.treesitter.parse_positions(file_path, treesitter_query, {
49-
nested_namespaces = true,
50-
nested_tests = false,
51-
})
48+
local positions = lib.treesitter.parse_positions(file_path, treesitter_query, {
49+
nested_namespaces = true,
50+
nested_tests = false,
51+
})
5252

53-
return positions
53+
return positions
5454
end
5555

56+
---Determines the package of a directory
57+
---@param dir string
58+
---@return string? package
59+
local function dir_determine_package(dir)
60+
if not lib.files.is_dir(dir) then
61+
error(string.format("expected '%s' be a directory, but it's not", dir))
62+
end
63+
64+
local test_file = nil
65+
local files = vim.fn.globpath(dir, "**/*.kt", false, true)
66+
for _, file in ipairs(files) do
67+
if filter.is_test_file(file) then
68+
test_file = file
69+
break
70+
end
71+
end
72+
73+
if test_file == nil then
74+
return nil
75+
end
76+
77+
return position_parser.get_first_match_string(test_file, package_query)
78+
end
79+
80+
---@class Context
81+
---@field results_path string path to the results file
82+
---@field path string path to the directory/file
83+
84+
---@class neotest.RunSpec
85+
---@field cwd string?
86+
---@field context Context
87+
---@field command string
88+
5689
---@param args neotest.RunArgs
5790
---@return nil | neotest.RunSpec | neotest.RunSpec[]
5891
function M.Adapter.build_spec(args)
59-
local results_path = async.fn.tempname() .. ".json"
60-
61-
-- Write something so there is a place to stream to...
62-
lib.files.write(results_path, "")
63-
64-
local tree = args.tree
65-
66-
if not tree then
67-
return
68-
end
69-
70-
local pos = tree:data()
71-
72-
local root = M.Adapter.root(pos.path)
73-
local pkg = position_parser.get_first_match_string(pos.path, package_query)
74-
local className = position_parser.get_first_match_string(pos.path, class_query)
75-
local specPackage = pkg .. "." .. className
76-
local tests = "*"
77-
78-
local gradle_command = command.parse(tests, specPackage, results_path)
79-
80-
local stream_data, stop_stream = lib.files.stream_lines(results_path)
81-
82-
print("command: " .. gradle_command)
83-
84-
local all_results = {}
85-
86-
return {
87-
command = gradle_command,
88-
cwd = root,
89-
context = {
90-
all_results = all_results,
91-
results_path = results_path,
92-
file = pos.path,
93-
stop_stream = stop_stream,
94-
},
95-
stream = function()
96-
return function()
97-
local new_results = stream_data()
98-
local success, parsed_result = pcall(output_parser.parse_lines, new_results, pos.path, specPackage)
99-
if not success then
100-
print("An error ocurred while attempting to stream data to result: " ..
101-
vim.inspect(err) .. " new_results: " .. vim.inspect(new_results))
102-
return nil
103-
else
104-
-- merge the parsed results with all results...
105-
for k, v in pairs(parsed_result) do all_results[k] = v end
106-
return parsed_result
107-
end
108-
end
109-
end,
110-
}
92+
local tree = args.tree
93+
if not tree then
94+
return
95+
end
96+
97+
---@type string
98+
local results_path = async.fn.tempname() .. ".txt"
99+
local pos = tree:data()
100+
local tests = "*"
101+
102+
---@type neotest.RunSpec
103+
local run_spec = {
104+
cwd = M.Adapter.root(pos.path),
105+
context = {
106+
results_path = results_path,
107+
path = pos.path,
108+
},
109+
}
110+
111+
if pos.type == "dir" then
112+
local package = dir_determine_package(pos.path) .. ".*"
113+
run_spec.command = command.parse(tests, package, results_path)
114+
elseif pos.type == "file" or pos.type == "namespace" or pos.type == "test" then
115+
local package = string.format(
116+
"%s.%s",
117+
position_parser.get_first_match_string(pos.path, package_query),
118+
position_parser.get_first_match_string(pos.path, class_query)
119+
)
120+
121+
run_spec.command = command.parse(tests, package, results_path)
122+
end
123+
124+
print(run_spec.command)
125+
126+
return run_spec
111127
end
112128

113129
---@class neotest.Result
@@ -122,8 +138,12 @@ end
122138
---@param tree neotest.Tree
123139
---@return table<string, neotest.Result>
124140
function M.Adapter.results(spec, result, tree)
125-
spec.context.stop_stream()
126-
return spec.context.all_results
141+
local result_path = spec.context.results_path
142+
local path = spec.context.path
143+
144+
---@type string[]
145+
local lines = lib.files.read_lines(result_path)
146+
return output_parser.parse_lines(lines, path)
127147
end
128148

129149
return M.Adapter

lua/neotest-kotlin/kotest-treesitter-query.lua

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ return [[
3535
3636
; todo Matches xit("context") { /** body **/ }
3737
38-
;; -- todo FUN SPEC --
38+
;; --- FUN SPEC ---
3939
4040
; Matches namespace context("context") { /** body **/ }
4141
@@ -63,20 +63,7 @@ return [[
6363
)
6464
) @test.definition
6565
66-
;; -- todo SHOULD SPEC --
67-
68-
; Matches namespace context("context") { /** body **/ }
69-
70-
(call_expression
71-
(simple_identifier) @function_name (#eq? @function_name "context")
72-
(call_suffix
73-
(value_arguments
74-
(value_argument
75-
(string_literal) @namespace.name
76-
)
77-
) (annotated_lambda)
78-
)
79-
) @namespace.definition
66+
;; --- SHOULD SPEC ---
8067
8168
; Matches test should("context") { /** body **/ }
8269

0 commit comments

Comments
 (0)