Skip to content

Commit 86e2fd3

Browse files
authored
Merge pull request #35 from NickHackman/fix-test-parsing
fix: identify tests
2 parents b21e810 + 3838a7c commit 86e2fd3

File tree

12 files changed

+108
-159
lines changed

12 files changed

+108
-159
lines changed

lua/neotest-kotlin/init.lua

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,54 @@ local filter = require("neotest-kotlin.src.filter")
88
local treesitter_query = require("neotest-kotlin.kotest-treesitter-query")
99
local output_parser = require("neotest-kotlin.src.output-parser")
1010

11-
local adapter = { name = "neotest-kotest" }
11+
local M = {}
1212

13-
function adapter.root(dir)
13+
---@class neotest.Adapter
14+
---@field name string
15+
M.Adapter = { name = "neotest-kotest" }
16+
17+
---Find the project root directory given a current directory to work from.
18+
---Should no root be found, the adapter can still be used in a non-project context if a test file matches.
19+
---@async
20+
---@param dir string @Directory to treat as cwd
21+
---@return string | nil @Absolute root dir of test suite
22+
function M.Adapter.root(dir)
1423
return lib.files.match_root_pattern("gradlew")(dir)
1524
end
1625

17-
function adapter.filter_dir(name, rel_path, root)
26+
---Filter directories when searching for test files
27+
---@async
28+
---@param name string Name of directory
29+
---@param rel_path string Path to directory, relative to root
30+
---@param root string Root directory of project
31+
---@return boolean
32+
function M.Adapter.filter_dir(name, rel_path, root)
1833
return filter.test_directory(name)
1934
end
2035

21-
function adapter.is_test_file(file_path)
36+
---@async
37+
---@param file_path string
38+
---@return boolean
39+
function M.Adapter.is_test_file(file_path)
2240
return filter.is_test_file(file_path)
2341
end
2442

25-
local function get_match_type(captured_nodes)
26-
if captured_nodes["namespace.name"] then
27-
return "namespace"
28-
end
29-
if captured_nodes["test.name"] then
30-
return "test"
31-
end
32-
end
33-
34-
function adapter.build_position(file_path, source, captured_nodes)
35-
local match_type = get_match_type(captured_nodes)
36-
local definition = captured_nodes[match_type .. ".definition"]
37-
38-
local build_position = {
39-
type = match_type,
40-
path = file_path,
41-
range = { definition:range() },
42-
}
43-
44-
return build_position
45-
end
46-
47-
--- a file path, parse all the tests within it.
43+
---Given a file path, parse all the tests within it.
4844
---@async
4945
---@param file_path string Absolute file path
5046
---@return neotest.Tree | nil
51-
function adapter.discover_positions(path)
52-
local positions = lib.treesitter.parse_positions(path, treesitter_query.value, {
47+
function M.Adapter.discover_positions(file_path)
48+
local positions = lib.treesitter.parse_positions(file_path, treesitter_query, {
5349
nested_namespaces = true,
5450
nested_tests = false,
5551
})
52+
5653
return positions
5754
end
5855

59-
---@param args neotest.run.RunArgs
56+
---@param args neotest.RunArgs
6057
---@return nil | neotest.RunSpec | neotest.RunSpec[]
61-
function adapter.build_spec(args)
58+
function M.Adapter.build_spec(args)
6259
local results_path = async.fn.tempname() .. ".json"
6360

6461
-- Write something so there is a place to stream to...
@@ -72,7 +69,7 @@ function adapter.build_spec(args)
7269

7370
local pos = tree:data()
7471

75-
local root = adapter.root(pos.path)
72+
local root = M.Adapter.root(pos.path)
7673
local pkg = position_parser.get_first_match_string(pos.path, package_query)
7774
local className = position_parser.get_first_match_string(pos.path, class_query)
7875
local specPackage = pkg .. "." .. className
@@ -124,9 +121,9 @@ end
124121
---@param result neotest.StrategyResult
125122
---@param tree neotest.Tree
126123
---@return table<string, neotest.Result>
127-
function adapter.results(spec, result, tree)
124+
function M.Adapter.results(spec, result, tree)
128125
spec.context.stop_stream()
129126
return spec.context.all_results
130127
end
131128

132-
return adapter
129+
return M.Adapter
Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,39 @@
1-
local TreesitterQuery = {}
2-
3-
-- These are treesitter queries for pulling data of of the AST,
1+
-- These are treesitter queries for pulling data out of the AST,
42
-- More information on how this works over here: https://neovim.io/doc/user/treesitter.html
5-
-- And you can interactively play around with these kotlin queries here: https://fwcd.dev/tree-sitter-kotlin/
6-
7-
TreesitterQuery.value = [[
3+
-- And you can interactively play around with these kotlin queries here: https://fwcd.github.io/tree-sitter-kotlin/
4+
return [[
85
96
;; --- DESCRIBE SPEC ---
107
118
; Matches namespace describe("context") { /** body **/ }
129
1310
(call_expression
14-
(call_expression
15-
(simple_identifier) @func_name (#eq? @func_name "describe")
16-
(call_suffix
17-
(value_arguments
18-
(value_argument
19-
(string_literal) @namespace.name
20-
)
11+
(simple_identifier) @function_name (#eq? @function_name "describe")
12+
(call_suffix
13+
(value_arguments
14+
(value_argument
15+
(string_literal) @namespace.name
2116
)
22-
)
17+
) (annotated_lambda)
2318
)
2419
) @namespace.definition
2520
2621
; Matches test it("context") { /** body **/ }
2722
2823
(call_expression
29-
(call_expression
30-
(simple_identifier) @func_name (#eq? @func_name "it")
31-
(call_suffix
32-
(value_arguments
33-
(value_argument
34-
(string_literal) @test.name
35-
)
24+
(simple_identifier) @function_name (#eq? @function_name "it")
25+
(call_suffix
26+
(value_arguments
27+
(value_argument
28+
(string_literal) @test.name
3629
)
37-
)
38-
)
30+
) (annotated_lambda)
31+
)
3932
) @test.definition
4033
41-
; todo Mathes xdescribe("context") { /** body **/ }
34+
; todo Matches xdescribe("context") { /** body **/ }
4235
43-
; todo Mathes xit("context") { /** body **/ }
36+
; todo Matches xit("context") { /** body **/ }
4437
4538
;; -- todo FUN SPEC --
4639
;; -- todo SHOULD SPEC --
@@ -53,5 +46,3 @@ TreesitterQuery.value = [[
5346
;; -- todo ANNOTATION SPEC --
5447
5548
]]
56-
57-
return TreesitterQuery

lua/neotest-kotlin/src/position-parser.lua

Lines changed: 38 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,74 +3,60 @@ local lib = require("neotest.lib")
33
local M = {}
44

55
M.parse = function(path, query)
6-
local positions = lib.treesitter.parse_positions(path, query, {
7-
nested_namespaces = true,
8-
nested_tests = false,
9-
fast = false,
10-
})
6+
local positions = lib.treesitter.parse_positions(path, query, {
7+
nested_namespaces = true,
8+
nested_tests = false,
9+
fast = false,
10+
})
1111

12-
return positions
12+
return positions
1313
end
1414

1515
M.get_all_matches_as_string = function(path, query)
16-
local results = {}
16+
local language = "kotlin"
1717

18-
local file = io.open(path)
18+
local bufnr = vim.api.nvim_create_buf(false, true)
19+
local content = vim.fn.readfile(path)
20+
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, content)
21+
vim.api.nvim_set_option_value("filetype", language, { buf = bufnr })
1922

20-
if file == nil then
21-
error("File not found at path: " .. path)
22-
end
23+
local parser = vim.treesitter.get_parser(bufnr, language, {})
24+
if not parser then
25+
error("Kotlin parser is not available. Please ensure it's installed.")
26+
end
2327

24-
local code = file:read("*all")
28+
local tree = parser:parse()[1]
2529

26-
local new_buffer_number = vim.api.nvim_create_buf(false, true)
27-
vim.api.nvim_buf_set_lines(new_buffer_number, 1, -1, false, vim.split(code, "\n"))
30+
---@type vim.treesitter.Query
31+
local treesitter_query = vim.treesitter.query.parse(language, query)
32+
local results = {}
2833

29-
file:close()
34+
for _, match, _ in treesitter_query:iter_matches(tree:root(), bufnr, 0, -1) do
35+
for _, nodes in pairs(match) do
36+
for _, node in ipairs(nodes) do
37+
local text = vim.treesitter.get_node_text(node, bufnr)
3038

31-
local language = "kotlin"
39+
if type(text) == "table" then
40+
table.insert(results, table.concat(text, "\n"))
41+
else
42+
table.insert(results, text)
43+
end
44+
end
45+
end
46+
end
3247

33-
local parser = vim.treesitter.get_string_parser(code, language)
34-
local tree = parser:parse()
35-
local root = tree[1]:root()
48+
vim.api.nvim_buf_delete(bufnr, { force = true })
3649

37-
local query = vim.treesitter.query.parse(language, query)
38-
39-
for _, match, _ in query:iter_matches(root, new_buffer_number, root:start(), root:end_(), {}) do
40-
for _, node in pairs(match) do
41-
local start_row, start_col = node:start()
42-
local end_row, end_col = node:end_()
43-
44-
local row_lines = vim.api.nvim_buf_get_lines(new_buffer_number, start_row + 1, end_row + 2, false)
45-
46-
if #row_lines == 0 then
47-
print("Error: position parser could not match the passed query.")
48-
return ""
49-
end
50-
51-
if #row_lines > 1 then
52-
print("Error: position parser currently only supports single line results.")
53-
return ""
54-
end
55-
56-
local found_line = row_lines[1]
57-
58-
local result = found_line:sub(start_col + 1, end_col)
59-
60-
results[#results + 1] = result
61-
end
62-
end
63-
64-
return results
50+
return results
6551
end
6652

6753
-- This will take in a path to a file, run a treesitter query on it, and return the first match as a string.
6854
M.get_first_match_string = function(path, query)
69-
local results = M.get_all_matches_as_string(path, query)
70-
if #results > 0 then
71-
return results[1]
72-
end
73-
return nil
55+
local results = M.get_all_matches_as_string(path, query)
56+
if #results > 0 then
57+
return results[1]
58+
end
59+
return nil
7460
end
7561

7662
return M
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
local M = "(class_declaration (type_identifier) @package.class)"
2-
3-
return M
1+
return "(class_declaration (type_identifier) @package.class)"
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
local M = "(package_header (identifier) @package.name)"
2-
3-
return M
1+
return "(package_header (identifier) @package.name)"

lua/neotest-kotlin/src/treesitter/spec-query.lua

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
local M = {}
2-
3-
-- These are treesitter queries for pulling data of of the AST,
1+
-- These are treesitter queries for pulling data out of the AST,
42
-- More information on how this works over here: https://neovim.io/doc/user/treesitter.html
5-
-- And you can interactively play around with these kotlin queries here: https://fwcd.dev/tree-sitter-kotlin/
3+
-- And you can interactively play around with these kotlin queries here: https://fwcd.github.io/tree-sitter-kotlin/
64

7-
M.value = [[
5+
return [[
86
97
;; --- DESCRIBE SPEC ---
108
@@ -137,5 +135,3 @@ M.value = [[
137135
;; -- todo ANNOTATION SPEC --
138136
139137
]]
140-
141-
return M

lua/tests/example_project/app/build.gradle.kts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,12 @@ repositories {
2222
dependencies {
2323
// This dependency is used by the application.
2424
implementation(libs.guava)
25+
testImplementation("io.kotest:kotest-runner-junit5:5.9.0")
26+
testImplementation("io.kotest:kotest-assertions-core:5.9.0")
2527
}
2628

27-
testing {
28-
suites {
29-
// Configure the built-in test suite
30-
val test by getting(JvmTestSuite::class) {
31-
// Use Kotlin Test test framework
32-
useKotlinTest("1.9.22")
33-
}
34-
}
29+
tasks.withType<Test>().configureEach {
30+
useJUnitPlatform()
3531
}
3632

3733
// Apply a specific Java toolchain to ease working on different environments.

lua/tests/example_project/app/src/main/kotlin/org/example/App.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
/*
2-
* This source file was generated by the Gradle 'init' task
3-
*/
41
package org.example
52

63
class App {

lua/tests/example_project/app/src/test/kotlin/org/example/KotestDescribeExample.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
package kotlin.org.example
2+
package org.example
33

44
import io.kotest.core.spec.style.DescribeSpec
55
import io.kotest.matchers.collections.shouldHaveSize

lua/tests/example_project/app/src/test/kotlin/org/example/KotlinTestExample.kt

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)