Skip to content

Commit

Permalink
test parsing working
Browse files Browse the repository at this point in the history
Signed-off-by: Max Englander <[email protected]>
  • Loading branch information
maxenglander committed Dec 25, 2024
1 parent a123ad9 commit c1b569c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 28 deletions.
60 changes: 46 additions & 14 deletions lua/neotest-minitest/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,40 @@ function NeotestAdapter.build_spec(args)
end
end

local iter_test_output_name_status = function(output)
local iter_test_output_error = function(output)
local header_pattern = "Failure:%s*"
local filepath_pattern = "%s+%[([^%]]+)]:%s*"
local result_pattern = "Expected:%s*(.-)%s*Actual:%s*(.-)%s"

-- keep track of last test error position
local last_pos = 0

return function()
-- find error header
local h_start, h_end = string.find(output, header_pattern, last_pos)
if h_start == nil or h_end == nil then return nil, nil, nil, nil end

-- find file path
local f_start, f_end = string.find(output, filepath_pattern, h_end)
if f_start == nil or f_end == nil then return nil, nil, nil, nil end

-- extract file path
local filepath = string.match(output, filepath_pattern, f_start)

-- extract test name
local test_name = string.sub(output, h_end + 1, f_start - 1)

-- find expected and result
local expected, actual = string.match(output, result_pattern, f_end)

-- keep track of last test error position
last_pos = f_end

return test_name, filepath, expected, actual
end
end

local iter_test_output_status = function(output)
local pattern = "%s*=%s*[%d.]+%s*s%s*=%s*([FE.])"

-- keep track of last test result position
Expand Down Expand Up @@ -257,13 +290,13 @@ local iter_test_output_name_status = function(output)
return test_name, test_status
end
end

function NeotestAdapter._parse_test_output(output, name_mappings)
local results = {}
local failure_pattern = "Failure:%s*([%w#_:]+)%s*%[([^%]]+)%]:%s*Expected:%s*(.-)%s*Actual:%s*(.-)%s"
local error_pattern = "Error:%s*([%w:#_]+):%s*(.-)\n[%w%W]-%.rb:(%d+):"
local traceback_pattern = "(%d+:[^:]+:%d+:in `[^']+')%s+([^:]+):(%d+):(in `[^']+':[^\n]+)"

for last_traceback, file_name, line_str, message in string.gmatch(output, traceback_pattern) do
for _, _, line_str, message in string.gmatch(output, traceback_pattern) do
local line = tonumber(line_str)
for _, pos_id in pairs(name_mappings) do
results[pos_id] = {
Expand All @@ -278,29 +311,28 @@ function NeotestAdapter._parse_test_output(output, name_mappings)
end
end

for test_name, status in iter_test_output_name_status(output) do
for test_name, status in iter_test_output_status(output) do
local pos_id = name_mappings[test_name]
if not pos_id then
-- try chopping of [module]:: parts
--
-- TODO: replace this with a Treesitter query that captures the modules
-- as a @namespace.definition instead.
local test_name_without_modules = utils.replace_module_namespace(test_name)
if name_mappings[test_name_without_modules] then pos_id = name_mappings[test_name_without_modules] end
test_name = utils.replace_module_namespace(test_name)
if name_mappings[test_name] then pos_id = name_mappings[test_name] end
end

if pos_id then results[pos_id] = {
status = status == "." and "passed" or "failed",
} end
end

for test_name, filepath, expected, actual in string.gmatch(output, failure_pattern) do
test_name = utils.replace_module_namespace(test_name)

local line = tonumber(string.match(filepath, ":(%d+)$"))
for test_name, filepath, expected, actual in iter_test_output_error(output) do
local message = string.format("Expected: %s\n Actual: %s", expected, actual)

local pos_id = name_mappings[test_name]
if not pos_id then
test_name = utils.replace_module_namespace(test_name)
pos_id = name_mappings[test_name]
end

local line = tonumber(string.match(filepath, ":(%d+)$"))
if results[pos_id] then
results[pos_id].status = "failed"
results[pos_id].errors = {
Expand Down
33 changes: 22 additions & 11 deletions lua/neotest-minitest/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ M.replace_module_namespace = function(test_name)
end

---@param position neotest.Position The position to return an ID for
---@param namespace neotest.Position[] Any namespaces the position is within
---@param parents neotest.Position[] Parent positions for the position
---@return string
M.generate_treesitter_id = function(position)
M.generate_treesitter_id = function(position, parents)
local cwd = async.fn.getcwd()
local test_path = "." .. replace_paths(position.path, cwd, "")
-- Treesitter starts line numbers from 0 so we subtract 1
Expand All @@ -40,15 +40,21 @@ M.generate_treesitter_id = function(position)
return id
end

---@param s string
local function unquote(s)
local r, _ = s:gsub("^[']*([^']*)[']*$", "%1")
return r
end

M.full_spec_name = function(tree)
local name = tree:data().name
local name = unquote(tree:data().name)
local namespaces = {}
local num_namespaces = 0

for parent_node in tree:iter_parents() do
local data = parent_node:data()
if data.type == "namespace" then
table.insert(namespaces, 1, parent_node:data().name)
table.insert(namespaces, 1, unquote(parent_node:data().name))
num_namespaces = num_namespaces + 1
else
break
Expand All @@ -57,21 +63,23 @@ M.full_spec_name = function(tree)

if num_namespaces == 0 then return name end

-- build result
local result = ""
-- Assemble namespaces
-- assemble namespaces
result = table.concat(namespaces, "::")
-- Add # separator.
-- add # separator
result = result .. "#"
-- Add test_ prefix.
-- add test_ prefix
result = result .. "test_"
-- Add index.
-- add index
for i, child_tree in ipairs(tree:parent():children()) do
for _, node in child_tree:iter_nodes() do
if node:data().id == tree:data().id then result = result .. string.format("%04d", i) end
end
end
-- Add _[name].
-- add _[name]
result = result .. "_" .. name

return result
end

Expand Down Expand Up @@ -103,8 +111,11 @@ M.get_mappings = function(tree)
local function name_map(tree)
local data = tree:data()
if data.type == "test" then
local full_name = M.full_test_name(tree)
mappings[full_name] = data.id
local full_spec_name = M.full_spec_name(tree)
mappings[full_spec_name] = data.id

local full_test_name = M.full_test_name(tree)
mappings[full_test_name] = data.id
end

for _, child in ipairs(tree:children()) do
Expand Down
7 changes: 4 additions & 3 deletions tests/adapter/spec_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Expected: 4
plugin._parse_test_output(output, { ["SpecTest::addition#test_0001_adds two numbers"] = "testing" })

assert.are.same(
{ ["testing"] = { status = "failed", errors = { { message = "Expected: 4\n Actual: 5", line = 7 } } } },
{ ["testing"] = { status = "failed", errors = { { message = "Expected: 4\n Actual: 5", line = 8 } } } },
results
)
end)
Expand Down Expand Up @@ -142,11 +142,12 @@ tests/minitest_examples/rails_unit_erroring_test.rb:1:in `require': cannot load

describe("single passing test", function()
local output = [[
SpecTest#test_subtracts_two_numbers = 0.00 s = .
SpecTest::subtraction#test_0001_subtracts two numbers = 0.00 s = .
]]

it("parses the results correctly", function()
local results = plugin._parse_test_output(output, { ["SpecTest#test_subtracts_two_numbers"] = "testing" })
local results =
plugin._parse_test_output(output, { ["SpecTest::subtraction#test_0001_subtracts two numbers"] = "testing" })

assert.are.same({ ["testing"] = { status = "passed" } }, results)
end)
Expand Down

0 comments on commit c1b569c

Please sign in to comment.