Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Busted Output (wip) #86

Closed
Closed
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
135 changes: 96 additions & 39 deletions lua/plenary/busted.lua
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
local Path = require('plenary.path')

local dirname = function(p)
return vim.fn.fnamemodify(p, ":h")
end

local function get_trace(element, level, msg)

local function trimTrace(info)
local index = info.traceback:find('\n%s*%[C]')
info.traceback = info.traceback:sub(1, index)
local start_index = info.traceback:find('/')
local end_index = info.traceback:find(': in')
info.traceback = info.traceback:sub(start_index, end_index)
Comment on lines +11 to +12
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are subbing away here, only busted traceback?


return info
end

level = level or 3

local thisdir = dirname(debug.getinfo(1, 'Sl').source, ":h")
local thisdir = dirname(debug.getinfo(1, 'Sl').source)
local info = debug.getinfo(level, 'Sl')
while info.what == 'C' or info.short_src:match('luassert[/\\].*%.lua$') or
(info.source:sub(1,1) == '@' and thisdir == dirname(info.source)) do
(info.source:sub(1,1) == '@' and thisdir == dirname(info.source)) do
level = level + 1
info = debug.getinfo(level, 'Sl')
end
Expand All @@ -22,12 +28,44 @@ local function get_trace(element, level, msg)
info.message = msg

-- local file = busted.getFile(element)
local file = false
return file and file.getTrace(file.name, info) or trimTrace(info)
-- local file = false
-- local file = false
-- return file and file.getTrace(file.name, info) or trimTrace(info)
return trimTrace(info)
end

local function get_file_and_line_number()

local function trimTrace(trace)
local start_index = trace:find('/')
local end_index = trace:find(': in')
trace = trace:sub(start_index, end_index)

local split_str = vim.split(trace, ':')
local spec = {}
spec.file = split_str[1]
spec.linenumber = split_str[2]

return spec
end

local level = 3

local thisdir = dirname(debug.getinfo(1, 'Sl').source)
local info = debug.getinfo(level, 'Sl')
while info.what == 'C' or info.short_src:match('luassert[/\\].*%.lua$') or
(info.source:sub(1,1) == '@' and thisdir == dirname(info.source)) do
level = level + 1
info = debug.getinfo(level, 'Sl')
end

local trace = debug.traceback('', level)

return trimTrace(trace)
end

--[[ is_headless is always true
-- running in nvim or in terminal --]]
local is_headless = require('plenary.nvim_meta').is_headless

local print = function(...)
Expand Down Expand Up @@ -70,61 +108,69 @@ local call_inner = function(desc, func)
local desc_stack = add_description(desc)
add_new_each()
local ok, msg = xpcall(func, function(msg)
-- debug.traceback
-- return vim.inspect(get_trace(nil, 3, msg))
local trace = get_trace(nil, 3, msg)
return trace.message .. "\n" .. trace.traceback
-- return trace.message .. "\n" .. trace.traceback
return trace.message
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep the traceback

end)
clear_last_each()
pop_description()

return ok, msg, desc_stack
end

local color_table = {
local ansi_color_table = {
cyan = 36,
magenta = 35,
yellow = 33,
green = 32,
red = 31,
}

local color_string = function(color, str)
if not is_headless then
-- This is never being called
return str
end

return string.format("%s[%sm%s%s[%sm",
string.char(27),
color_table[color] or 0,
str,
string.char(27),
0
string.char(27),
ansi_color_table[color] or 0,
str,
string.char(27),
0
)
end

local bold_string = function(str)
local ansi_bold = "\027[1m"
local ansi_clear = "\027[0m"

return ansi_bold .. str .. ansi_clear
end

-- local SUCCESS = color_string("green", "Success")
local FAIL = color_string("red", "Failure")
local SUCCESS = color_string("green", "Success")
local FAIL = color_string("red", "Fail")
local PENDING = color_string("yellow", "Pending")

local HEADER = string.rep("=", 40)
local HORIZONTALRULER = string.rep("─", 80)

mod.format_results = function(result)

mod.format_results = function(res)
local num_pass = #res.pass
local num_fail = #res.fail
local num_errs = #res.errs
local num_pass = color_string("green", #result.pass)
local num_fail = color_string("red", #result.fail)
local num_errs = color_string("magenta", #result.errs)

print("")
print(color_string("green", "Success: "), num_pass)
print(color_string("red", "Failed : "), num_fail)
print(color_string("red", "Errors : "), num_errs)
print(HEADER)
print(string.format(" %s successes / %s failures / %s errors", num_pass, num_fail, num_errs))
end

mod.describe = function(desc, func)
results.pass = results.pass or {}
results.fail = results.fail or {}
results.errs = results.errs or {}
results.fatal = results.fatal or {}
results.pass = {}
results.fail = {}
results.errs = {}

print("\n" .. HORIZONTALRULER .."\n ")
-- print("Testing: ", debug.getinfo(2, 'Sl').source)
describe = mod.inner_describe
local ok, msg = call_inner(desc, func)
describe = mod.describe
Expand Down Expand Up @@ -185,17 +231,27 @@ mod.it = function(desc, func)

-- TODO: We should figure out how to determine whether
-- and assert failed or whether it was an error...

local to_insert, printed
if not ok then
to_insert = results.fail
test_result.msg = msg

print(FAIL, "||", table.concat(test_result.descriptions, " "))
print(indent(msg, 12))
-- print(FAIL, " → " .. color_string("cyan", "spec/foo/bar_spec.lua @ 7") .. "\n")

print("{SPEC: FAIL}")
local spec = get_file_and_line_number()

print(FAIL, " → " .. color_string("cyan", spec.file) .. " @ " .. color_string("cyan", spec.linenumber) .. "\n")

print(bold_string(table.concat(test_result.descriptions)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing " " for concat

print(indent("\n" .. msg, 7))

print("{ENDOFSPEC}")

else
to_insert = results.pass
print(SUCCESS, "||", table.concat(test_result.descriptions, " "))
print("{SPEC: SUCCESS}")
print(SUCCESS, " → ", table.concat(test_result.descriptions, " "))
print("{ENDOFSPEC}")
end

table.insert(to_insert, test_result)
Expand All @@ -219,16 +275,17 @@ clear = mod.clear
assert = require("luassert")

mod.run = function(file)
print("\n" .. HEADER)
print("Testing: ", file)
-- print("Testing: ", file)

local ok, msg = pcall(dofile, file)

if not ok then
print(HEADER)
print(HORIZONTALRULER)
print("FAILED TO LOAD FILE")
print(color_string("red", msg))
print(HEADER)
print(HORIZONTALRULER)

os.exit(2)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be here

I am looking at the whole code now. Give me between 30 ~ 60 mins for an answer. I wanna play around with the code for a bit

Copy link
Collaborator

@Conni2461 Conni2461 Mar 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay i am trying to get nvim --headless --noplugin -u scripts/minimal.vim -c "PlenaryBustedFile tests/plenary/simple_busted_spec.lua" to work first. After that i am trying to deal with PlenaryBustedDirectory and test harness

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh i see what you doing. I am not sure if i like it i kinda wanna keep the current way of starting a test, with just executing busted itself. We not always wanna go thought test_harness

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's exactly the reason for reaching out.
I don't like it either.
I'm afk for +/- 2 hours. So will respond later.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is what i think we could do. When executing the file with PlenaryBustedFile we have basically the same output, a little bit cleaned up and for Directory we can then get the SUCCESS, FAILURE and PENDING outputs and count them and also convert them to dots.

For stacktrace and error, the line always starts with a and those always come after a FAILURE so, if we read failure we go in another state that captures all further lines that start with and memorizes them for later output. And on exit we just draw it. Colored dots, Failures and summarize. We also can have simple_busted print the summarize and just realize that its a summarize and capture it that way.

If you wanna have a more detailed talk, you can write me on gitter in private.

if is_headless then
os.exit(2)
else
Expand Down
64 changes: 64 additions & 0 deletions lua/plenary/busted_dots.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
local ansi_color_table = {
cyan = 36,
magenta = 35,
yellow = 33,
green = 32,
red = 31,
}

local color_string = function(color, str)
return string.format("%s[%sm%s%s[%sm",
string.char(27),
ansi_color_table[color] or 0,
str,
string.char(27),
0
)
end

local successDot = color_string('green', '●')
local failureDot = color_string('red', '◼')
local errorDot = color_string('magenta', '✱')
local pendingDot = color_string('yellow', '◌')

local function generate_dots(results, status_str, dot)
local dot_count = 0
for _, spec in pairs(results) do
if spec.status == status_str then
io.stdout:write(dot)
dot_count = dot_count + 1
end
end
return dot_count
end

local function generate_score(n_succ, n_fail, n_err, n_pend)

local success = n_succ == 1 and ' success' or ' successes'
local fail = n_fail == 1 and ' failure' or ' failures'
local error = n_err == 1 and ' error' or ' errors'

local score =
color_string('green', n_succ) .. success .. ' / ' ..
color_string('red', n_fail) .. fail .. ' / ' ..
color_string('magenta', n_err) .. error .. ' / ' ..
color_string('yellow', n_pend) .. ' pending'

io.stdout:write(score)
end

local Score = {}

function Score.draw(results)

local n_pend = generate_dots(results, 'pending', pendingDot)
local n_err = generate_dots(results, 'error', errorDot)
local n_fail = generate_dots(results, 'failed', failureDot)
local n_succ = generate_dots(results, 'success', successDot)

io.stdout:write('\n')
generate_score(n_succ, n_fail, n_err, n_pend)
io.stdout:write('\n\n')
end

return Score
49 changes: 49 additions & 0 deletions lua/plenary/busted_reader.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
BustedOutputReader = {}

local spec_results = {}
local current_spec = {}

local function set_spec_status(line, find_str, status_str)
if line:find(find_str) then
current_spec.status = status_str
return true
end
end

local is_spec_result
local cat_line

local function reset()
current_spec, cat_line, is_spec_result = {}, nil, nil
end

BustedOutputReader.output_to_table = function(...)

for _, line in pairs({...}) do

if is_spec_result then

if line:find('{ENDOFSPEC}') then
current_spec.content = cat_line
table.insert(spec_results, current_spec)

reset()
else
if not cat_line then cat_line = "" end
cat_line = cat_line .. line .. '\n'
end
end

if not is_spec_result then
is_spec_result = set_spec_status(line, '{SPEC: FAIL}', 'failed') or
set_spec_status(line, '{SPEC: ERROR}', 'error') or
set_spec_status(line, '{SPEC: SUCCESS}', 'success') or
set_spec_status(line, '{SPEC: PENDING}', 'pending')
end
end

return spec_results
end

return BustedOutputReader

1 change: 1 addition & 0 deletions lua/plenary/job.lua
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ end
---@field on_exit function : (self, code: number, signal: number)
---@field maximum_results number : stop processing results after this number
---@field writer Job|table|string : Job that writes to stdin of this job.

function Job:new(o)
if not o then
error(debug.traceback("Options are required for Job:new"))
Expand Down
Loading