123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- local pretty = require 'pl.pretty'
- local t_global = require('test.testutil')
- local colors = setmetatable({}, {
- __index = function()
- return function(s)
- return s == nil and '' or tostring(s)
- end
- end,
- })
- local enable_colors = true
- if os.getenv 'TEST_COLORS' then
- local test_colors = os.getenv('TEST_COLORS'):lower()
- local disable_colors = test_colors == 'false'
- or test_colors == '0'
- or test_colors == 'no'
- or test_colors == 'off'
- enable_colors = not disable_colors
- end
- if enable_colors then
- colors = require 'term.colors'
- end
- return function(options)
- local busted = require 'busted'
- local handler = require 'busted.outputHandlers.base'()
- local c = {
- succ = function(s)
- return colors.bright(colors.green(s))
- end,
- skip = function(s)
- return colors.bright(colors.yellow(s))
- end,
- fail = function(s)
- return colors.bright(colors.magenta(s))
- end,
- errr = function(s)
- return colors.bright(colors.red(s))
- end,
- test = tostring,
- file = colors.cyan,
- time = colors.dim,
- note = colors.yellow,
- sect = function(s)
- return colors.green(colors.dim(s))
- end,
- nmbr = colors.bright,
- }
- local repeatSuiteString = '\nRepeating all tests (run %d of %d) . . .\n\n'
- local randomizeString = c.note('Note: Randomizing test order with a seed of %d.\n')
- local globalSetup = c.sect('--------') .. ' Global test environment setup.\n'
- local fileStartString = c.sect('--------') .. ' Running tests from ' .. c.file('%s') .. '\n'
- local runString = c.sect('RUN ') .. ' ' .. c.test('%s') .. ': '
- local successString = c.succ('OK') .. '\n'
- local skippedString = c.skip('SKIP') .. '\n'
- local failureString = c.fail('FAIL') .. '\n'
- local errorString = c.errr('ERR') .. '\n'
- local fileEndString = c.sect('--------')
- .. ' '
- .. c.nmbr('%d')
- .. ' %s from '
- .. c.file('%s')
- .. ' '
- .. c.time('(%.2f ms total)')
- .. '\n\n'
- local globalTeardown = c.sect('--------') .. ' Global test environment teardown.\n'
- local suiteEndString = c.sect('========')
- .. ' '
- .. c.nmbr('%d')
- .. ' %s from '
- .. c.nmbr('%d')
- .. ' test %s ran. '
- .. c.time('(%.2f ms total)')
- .. '\n'
- local successStatus = c.succ('PASSED ') .. ' ' .. c.nmbr('%d') .. ' %s.\n'
- local timeString = c.time('%.2f ms')
- local summaryStrings = {
- skipped = {
- header = c.skip('SKIPPED ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
- test = c.skip('SKIPPED ') .. ' %s\n',
- footer = ' ' .. c.nmbr('%d') .. ' SKIPPED %s\n',
- },
- failure = {
- header = c.fail('FAILED ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
- test = c.fail('FAILED ') .. ' %s\n',
- footer = ' ' .. c.nmbr('%d') .. ' FAILED %s\n',
- },
- error = {
- header = c.errr('ERROR ') .. ' ' .. c.nmbr('%d') .. ' %s, listed below:\n',
- test = c.errr('ERROR ') .. ' %s\n',
- footer = ' ' .. c.nmbr('%d') .. ' %s\n',
- },
- }
- local fileCount = 0
- local fileTestCount = 0
- local testCount = 0
- local successCount = 0
- local skippedCount = 0
- local failureCount = 0
- local errorCount = 0
- local pendingDescription = function(pending)
- local string = ''
- if type(pending.message) == 'string' then
- string = string .. pending.message .. '\n'
- elseif pending.message ~= nil then
- string = string .. pretty.write(pending.message) .. '\n'
- end
- return string
- end
- local failureDescription = function(failure)
- local string = failure.randomseed and ('Random seed: ' .. failure.randomseed .. '\n') or ''
- if type(failure.message) == 'string' then
- string = string .. failure.message
- elseif failure.message == nil then
- string = string .. 'Nil error'
- else
- string = string .. pretty.write(failure.message)
- end
- string = string .. '\n'
- if options.verbose and failure.trace and failure.trace.traceback then
- string = string .. failure.trace.traceback .. '\n'
- end
- return string
- end
- local getFileLine = function(element)
- local fileline = ''
- if element.trace or element.trace.short_src then
- fileline = colors.cyan(element.trace.short_src)
- .. ' @ '
- .. colors.cyan(element.trace.currentline)
- .. ': '
- end
- return fileline
- end
- local getTestList = function(status, count, list, getDescription)
- local string = ''
- local header = summaryStrings[status].header
- if count > 0 and header then
- local tests = (count == 1 and 'test' or 'tests')
- local errors = (count == 1 and 'error' or 'errors')
- string = header:format(count, status == 'error' and errors or tests)
- local testString = summaryStrings[status].test
- if testString then
- for _, t in ipairs(list) do
- local fullname = getFileLine(t.element) .. colors.bright(t.name)
- string = string .. testString:format(fullname)
- string = string .. getDescription(t)
- end
- end
- end
- return string
- end
- local getSummary = function(status, count)
- local string = ''
- local footer = summaryStrings[status].footer
- if count > 0 and footer then
- local tests = (count == 1 and 'TEST' or 'TESTS')
- local errors = (count == 1 and 'ERROR' or 'ERRORS')
- string = footer:format(count, status == 'error' and errors or tests)
- end
- return string
- end
- local getSummaryString = function()
- local tests = (successCount == 1 and 'test' or 'tests')
- local string = successStatus:format(successCount, tests)
- string = string .. getTestList('skipped', skippedCount, handler.pendings, pendingDescription)
- string = string .. getTestList('failure', failureCount, handler.failures, failureDescription)
- string = string .. getTestList('error', errorCount, handler.errors, failureDescription)
- string = string .. ((skippedCount + failureCount + errorCount) > 0 and '\n' or '')
- string = string .. getSummary('skipped', skippedCount)
- string = string .. getSummary('failure', failureCount)
- string = string .. getSummary('error', errorCount)
- return string
- end
- handler.suiteReset = function()
- fileCount = 0
- fileTestCount = 0
- testCount = 0
- successCount = 0
- skippedCount = 0
- failureCount = 0
- errorCount = 0
- return nil, true
- end
- handler.suiteStart = function(_suite, count, total, randomseed)
- if total > 1 then
- io.write(repeatSuiteString:format(count, total))
- end
- if randomseed then
- io.write(randomizeString:format(randomseed))
- end
- io.write(globalSetup)
- io.flush()
- return nil, true
- end
- local function getElapsedTime(tbl)
- if tbl.duration then
- return tbl.duration * 1000
- else
- return tonumber('nan')
- end
- end
- handler.suiteEnd = function(suite, _count, _total)
- local elapsedTime_ms = getElapsedTime(suite)
- local tests = (testCount == 1 and 'test' or 'tests')
- local files = (fileCount == 1 and 'file' or 'files')
- io.write(globalTeardown)
- io.write(suiteEndString:format(testCount, tests, fileCount, files, elapsedTime_ms))
- io.write(getSummaryString())
- if failureCount > 0 or errorCount > 0 then
- io.write(t_global.read_nvim_log(nil, true))
- end
- io.flush()
- return nil, true
- end
- handler.fileStart = function(file)
- fileTestCount = 0
- io.write(fileStartString:format(vim.fs.normalize(file.name)))
- io.flush()
- return nil, true
- end
- handler.fileEnd = function(file)
- local elapsedTime_ms = getElapsedTime(file)
- local tests = (fileTestCount == 1 and 'test' or 'tests')
- fileCount = fileCount + 1
- io.write(
- fileEndString:format(fileTestCount, tests, vim.fs.normalize(file.name), elapsedTime_ms)
- )
- io.flush()
- return nil, true
- end
- handler.testStart = function(element, _parent)
- local testid = _G._nvim_test_id or ''
- local desc = ('%s %s'):format(testid, handler.getFullName(element))
- io.write(runString:format(desc))
- io.flush()
- return nil, true
- end
- local function write_status(element, string)
- io.write(timeString:format(getElapsedTime(element)) .. ' ' .. string)
- io.flush()
- end
- handler.testEnd = function(element, _parent, status, _debug)
- local string
- fileTestCount = fileTestCount + 1
- testCount = testCount + 1
- if status == 'success' then
- successCount = successCount + 1
- string = successString
- elseif status == 'pending' then
- skippedCount = skippedCount + 1
- string = skippedString
- elseif status == 'failure' then
- failureCount = failureCount + 1
- string = failureString .. failureDescription(handler.failures[#handler.failures])
- elseif status == 'error' then
- errorCount = errorCount + 1
- string = errorString .. failureDescription(handler.errors[#handler.errors])
- else
- string = 'unexpected test status! (' .. status .. ')'
- end
- write_status(element, string)
- return nil, true
- end
- handler.error = function(element, _parent, _message, _debug)
- if element.descriptor ~= 'it' then
- write_status(element, failureDescription(handler.errors[#handler.errors]))
- errorCount = errorCount + 1
- end
- return nil, true
- end
- busted.subscribe({ 'suite', 'reset' }, handler.suiteReset)
- busted.subscribe({ 'suite', 'start' }, handler.suiteStart)
- busted.subscribe({ 'suite', 'end' }, handler.suiteEnd)
- busted.subscribe({ 'file', 'start' }, handler.fileStart)
- busted.subscribe({ 'file', 'end' }, handler.fileEnd)
- busted.subscribe({ 'test', 'start' }, handler.testStart, { predicate = handler.cancelOnPending })
- busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending })
- busted.subscribe({ 'failure' }, handler.error)
- busted.subscribe({ 'error' }, handler.error)
- return handler
- end
|