Skip to content
This repository was archived by the owner on Nov 30, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
vendor/plenary.nvim
.test_plugins
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
PREPARE_CONFIG=tests/prepare-config.lua
TEST_CONFIG=tests/test-config.lua
SETUP_TEST=tests/setup-test.lua
BEFORE_TEST=tests/before-test.lua
TESTS_DIR=tests/

.PHONY: test

test:
@nvim \
--headless \
-u ${PREPARE_CONFIG} \
"+PlenaryBustedDirectory ${TESTS_DIR} { minimal_init = '${TEST_CONFIG}' }"
-u ${SETUP_TEST} \
"+PlenaryBustedDirectory ${TESTS_DIR} { minimal_init = '${BEFORE_TEST}' }"
3 changes: 3 additions & 0 deletions lua/java-test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
local M = {}

return M
73 changes: 73 additions & 0 deletions lua/java-test/reports/junit.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
local class = require('java-core.utils.class')
local log = require('java-core.utils.log')

---@class java_test.JUnitTestReport
---@field private conn uv_tcp_t
---@field private result_parser java_test.TestParser
---@field private result_parser_fac java_test.TestParserFactory
---@field private report_viewer java_test.ReportViewer
---@overload fun(result_parser_factory: java_test.TestParserFactory, test_viewer: java_test.ReportViewer)
local JUnitReport = class()

---Init
---@param result_parser_factory java_test.TestParserFactory
function JUnitReport:_init(result_parser_factory, report_viewer)
self.conn = nil
self.result_parser_fac = result_parser_factory
self.report_viewer = report_viewer
end

---Returns the test results
---@return java_test.TestResults[]
function JUnitReport:get_results()
return self.result_parser:get_test_details()
end

---Shows the test report
function JUnitReport:show_report()
self.report_viewer:show(self:get_results())
end

---Returns a stream reader function
---@param conn uv_tcp_t
---@return fun(err: string, buffer: string) # callback function
function JUnitReport:get_stream_reader(conn)
self.conn = conn
self.result_parser = self.result_parser_fac:get_parser()

return vim.schedule_wrap(function(err, buffer)
if err then
self:on_error(err)
self:on_close()
self.conn:close()
return
end

if buffer then
self:on_update(buffer)
else
self:on_close()
self.conn:close()
end
end)
end

---Runs on connection update
---@private
---@param text string
function JUnitReport:on_update(text)
self.result_parser:parse(text)
end

---Runs on connection close
---@private
function JUnitReport:on_close() end

---Runs on connection error
---@private
---@param err string error
function JUnitReport:on_error(err)
log.error('Error while running test', err)
end

return JUnitReport
7 changes: 7 additions & 0 deletions lua/java-test/results/execution-status.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---@enum java_test.TestExecutionStatus
local TestStatus = {
Started = 'started',
Ended = 'ended',
}

return TestStatus
46 changes: 46 additions & 0 deletions lua/java-test/results/message-id.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---@enum MessageId
local MessageId = {
-- Notification about a test inside the test suite.
-- TEST_TREE + testId + "," + testName + "," + isSuite + "," + testCount + "," + isDynamicTest +
-- "," + parentId + "," + displayName + "," + parameterTypes + "," + uniqueId

-- isSuite = "true" or "false"
-- isDynamicTest = "true" or "false"
-- parentId = the unique id of its parent if it is a dynamic test, otherwise can be "-1"
-- displayName = the display name of the test
-- parameterTypes = comma-separated list of method parameter types if applicable, otherwise an
-- empty string
-- uniqueId = the unique ID of the test provided by JUnit launcher, otherwise an empty string

TestTree = '%TSTTREE',
TestStart = '%TESTS',
TestEnd = '%TESTE',
TestFailed = '%FAILED',
TestError = '%ERROR',
ExpectStart = '%EXPECTS',
ExpectEnd = '%EXPECTE',
ActualStart = '%ACTUALS',
ActualEnd = '%ACTUALE',
TraceStart = '%TRACES',
TraceEnd = '%TRACEE',
IGNORE_TEST_PREFIX = '@Ignore: ',
ASSUMPTION_FAILED_TEST_PREFIX = '@AssumptionFailure: ',
}

--[[
*************
%TESTC 2 v2
%TSTTREE2,com.example.demo.DemoApplicationTests,true,2,false,1,DemoApplicationTests,,[engine:junit-jupiter]/[class:com.example.demo.DemoApplicationTests]
%TSTTREE3,anotherTest(com.example.demo.DemoApplicationTests),false,1,false,2,anotherTest(),,[engine:junit-jupiter]/[class:com.example.demo.DemoApplicationTests]/[method:anotherTest()]
%TSTTREE4,contextLoads(com.example.demo.DemoApplicationTests),false,1,false,2,contextLoads(),,[engine:junit-jupiter]/[class:com.example.demo.DemoApplicationTests]/[method:contextLoads()]
%TESTS 3,anotherTest(com.example.demo.DemoApplicationTests)
*************
%TESTE 3,anotherTest(com.example.demo.DemoApplicationTests)
*************
%TESTS 4,contextLoads(com.example.demo.DemoApplicationTests)
*************
%TESTE 4,contextLoads(com.example.demo.DemoApplicationTests)
%RUNTIME2281
--]]

return MessageId
14 changes: 14 additions & 0 deletions lua/java-test/results/result-parser-factory.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
local class = require('java-core.utils.class')
local TestParser = require('java-test.results.result-parser')

---@class java_test.TestParserFactory
local TestParserFactory = class()

---Returns a test parser of given type
---@param args any
---@return java_test.TestParser
function TestParserFactory.get_parser(args)
return TestParser()
end

return TestParserFactory
200 changes: 200 additions & 0 deletions lua/java-test/results/result-parser.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
local class = require('java-core.utils.class')

local MessageId = require('java-test.results.message-id')
local TestStatus = require('java-test.results.result-status')
local TestExecStatus = require('java-test.results.execution-status')

---@class java_test.TestParser
---@field private test_details java_test.TestResults[]
local TestParser = class()

---Init
---@private
function TestParser:_init()
self.test_details = {}
end

---@private
TestParser.node_parsers = {
[MessageId.TestTree] = 'parse_test_tree',
[MessageId.TestStart] = 'parse_test_start',
[MessageId.TestEnd] = 'parse_test_end',
[MessageId.TestFailed] = 'parse_test_failed',
}

---@private
TestParser.strtobool = {
['true'] = true,
['false'] = false,
}

---Parse a given text into test details
---@param text string test result buffer
function TestParser:parse(text)
if text:sub(-1) ~= '\n' then
text = text .. '\n'
end

local line_iter = text:gmatch('(.-)\n')

local line = line_iter()

while line ~= nil do
local message_id = line:sub(1, 8):gsub('%s+', '')
local content = line:sub(9)

local node_parser = TestParser.node_parsers[message_id]

if node_parser then
local data = vim.split(content, ',', { plain = true, trimempty = true })

if self[TestParser.node_parsers[message_id]] then
self[TestParser.node_parsers[message_id]](self, data, line_iter)
end
end

line = line_iter()
end
end

---Returns the parsed test details
---@return java_test.TestResults # parsed test details
function TestParser:get_test_details()
return self.test_details
end

---@private
function TestParser:parse_test_tree(data)
local node = {
test_id = tonumber(data[1]),
test_name = data[2],
is_suite = TestParser.strtobool[data[3]],
test_count = tonumber(data[4]),
is_dynamic_test = TestParser.strtobool[data[5]],
parent_id = tonumber(data[6]),
display_name = data[7],
parameter_types = data[8],
unique_id = data[9],
}

local parent = self:find_result_node(node.parent_id)

if not parent then
table.insert(self.test_details, node)
else
parent.children = parent.children or {}
table.insert(parent.children, node)
end
end

---@private
function TestParser:parse_test_start(data)
local test_id = tonumber(data[1])
local node = self:find_result_node(test_id)
assert(node)
node.result = {}
node.result.execution = TestExecStatus.Started
end

---@private
function TestParser:parse_test_end(data)
local test_id = tonumber(data[1])
local node = self:find_result_node(test_id)
assert(node)
node.result.execution = TestExecStatus.Ended
end

---@private
function TestParser:parse_test_failed(data, line_iter)
local test_id = tonumber(data[1])
local node = self:find_result_node(test_id)
assert(node)

node.result.status = TestStatus.Failed

while true do
local line = line_iter()

if line == nil then
break
end

-- EXPECTED
if vim.startswith(line, MessageId.ExpectStart) then
node.result.expected =
self:get_content_until_end_tag(MessageId.ExpectEnd, line_iter)

-- ACTUAL
elseif vim.startswith(line, MessageId.ActualStart) then
node.result.actual =
self:get_content_until_end_tag(MessageId.ActualEnd, line_iter)

-- TRACE
elseif vim.startswith(line, MessageId.TraceStart) then
node.result.trace =
self:get_content_until_end_tag(MessageId.TraceEnd, line_iter)
end
end
end

---@private
function TestParser:get_content_until_end_tag(end_tag, line_iter)
local content = {}

while true do
local line = line_iter()

if line == nil or vim.startswith(line, end_tag) then
break
end

table.insert(content, line)
end

return content
end

---@private
function TestParser:find_result_node(id)
local function find_node(nodes)
if not nodes or #nodes == 0 then
return
end

for _, node in ipairs(nodes) do
if node.test_id == id then
return node
end

local _node = find_node(node.children)

if _node then
return _node
end
end
end

return find_node(self.test_details)
end

return TestParser

---@class java_test.TestResultExecutionDetails
---@field actual string[] lines
---@field expected string[] lines
---@field status java_test.TestStatus
---@field execution java_test.TestExecutionStatus
---@field trace string[] lines

---@class java_test.TestResults
---@field display_name string
---@field is_dynamic_test boolean
---@field is_suite boolean
---@field parameter_types string
---@field parent_id integer
---@field test_count integer
---@field test_id integer
---@field test_name string
---@field unique_id string
---@field result java_test.TestResultExecutionDetails
---@field children java_test.TestResults[]
7 changes: 7 additions & 0 deletions lua/java-test/results/result-status.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---@enum java_test.TestStatus
local TestStatus = {
Failed = 'failed',
Skipped = 'skipped',
}

return TestStatus
Loading