From: Vsevolod Stakhov Date: Fri, 27 Feb 2015 15:55:46 +0000 (+0000) Subject: Reorganize lua tests. X-Git-Tag: 0.9.0~595 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=349bc58cf6d025e4e50a325bda48d318b23b55fb;p=rspamd.git Reorganize lua tests. --- diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9932fb87d..32d5334cb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,9 +23,17 @@ TARGET_LINK_LIBRARIES(rspamd-test ${RSPAMD_REQUIRED_LIBRARIES}) TARGET_LINK_LIBRARIES(rspamd-test stemmer) IF(NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) - FILE(COPY "${CMAKE_CURRENT_SOURCE_DIR}/busted.lua" - "${CMAKE_CURRENT_SOURCE_DIR}/busted" - "${CMAKE_CURRENT_SOURCE_DIR}/lua" + FILE(COPY "${CMAKE_CURRENT_SOURCE_DIR}/lua" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + + # Also add dependencies for convenience + FILE(GLOB LUA_TESTS "${CMAKE_CURRENT_SOURCE_DIR}/lua/*") + FOREACH(_LF IN LISTS "${LUA_TESTS}") + GET_FILENAME_COMPONENT(_NM _LF NAME) + ADD_CUSTOM_COMMAND(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${_NM}" + "${CMAKE_COMMAND} -E copy_if_different ${_LF} ${CMAKE_CURRENT_BINARY_DIR}/${_NM}" + ) + ADD_DEPENDENCIES(rspamd-test "${CMAKE_CURRENT_BINARY_DIR}/${_NM}") + ENDFOREACH() ENDIF() \ No newline at end of file diff --git a/test/busted.lua b/test/busted.lua deleted file mode 100644 index 917d21d82..000000000 --- a/test/busted.lua +++ /dev/null @@ -1,3 +0,0 @@ --- This is a dummy file so it can be used in busted's specs --- without adding ./?/init.lua to the lua path -return require 'busted.init' diff --git a/test/busted/compatibility.lua b/test/busted/compatibility.lua deleted file mode 100644 index 7837656fb..000000000 --- a/test/busted/compatibility.lua +++ /dev/null @@ -1,60 +0,0 @@ -return { - getfenv = getfenv or function(f) - f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) - local name, value - local up = 0 - - repeat - up = up + 1 - name, value = debug.getupvalue(f, up) - until name == '_ENV' or name == nil - - return value - end, - - setfenv = setfenv or function(f, t) - f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) - local name - local up = 0 - - repeat - up = up + 1 - name = debug.getupvalue(f, up) - until name == '_ENV' or name == nil - - if name then - debug.upvaluejoin(f, up, function() return name end, 1) - debug.setupvalue(f, up, t) - end - - if f ~= 0 then return f end - end, - - unpack = table.unpack or unpack, - - osexit = function(code, close) - if close and _VERSION == 'Lua 5.1' then - -- From Lua 5.1 manual: - -- > The userdata itself is freed only in the next - -- > garbage-collection cycle. - -- Call collectgarbage() while collectgarbage('count') - -- changes + 3 times, at least 3 times, - -- at max 100 times (to prevent infinite loop). - local times_const = 0 - for i = 1, 100 do - local count_before = collectgarbage("count") - collectgarbage() - local count_after = collectgarbage("count") - if count_after == count_before then - times_const = times_const + 1 - if times_const > 3 then - break - end - else - times_const = 0 - end - end - end - os.exit(code, close) - end, -} diff --git a/test/busted/context.lua b/test/busted/context.lua deleted file mode 100644 index 4cf0f3b53..000000000 --- a/test/busted/context.lua +++ /dev/null @@ -1,91 +0,0 @@ -local tablex = require 'pl.tablex' - -local function save() - local g = {} - for k,_ in next, _G, nil do - g[k] = rawget(_G, k) - end - return { - gmt = getmetatable(_G), - g = g, - loaded = tablex.copy(package.loaded) - } -end - -local function restore(state) - setmetatable(_G, state.gmt) - for k,_ in next, _G, nil do - rawset(_G, k, state.g[k]) - end - for k,_ in pairs(package.loaded) do - package.loaded[k] = state.loaded[k] - end -end - -return function() - local context = {} - - local data = {} - local parents = {} - local children = {} - local stack = {} - - function context.ref() - local ref = {} - local ctx = data - - function ref.get(key) - if not key then return ctx end - return ctx[key] - end - - function ref.set(key, value) - ctx[key] = value - end - - function ref.clear() - data = {} - parents = {} - children = {} - stack = {} - ctx = data - end - - function ref.attach(child) - if not children[ctx] then children[ctx] = {} end - parents[child] = ctx - table.insert(children[ctx], child) - end - - function ref.children(parent) - return children[parent] or {} - end - - function ref.parent(child) - return parents[child] - end - - function ref.push(current) - if not parents[current] then error('Detached child. Cannot push.') end - if ctx ~= current and current.descriptor == 'file' then - current.state = save() - end - table.insert(stack, ctx) - ctx = current - end - - function ref.pop() - local current = ctx - ctx = table.remove(stack) - if ctx ~= current and current.state then - restore(current.state) - current.state = nil - end - if not ctx then error('Context stack empty. Cannot pop.') end - end - - return ref - end - - return context -end diff --git a/test/busted/core.lua b/test/busted/core.lua deleted file mode 100644 index 802e60c80..000000000 --- a/test/busted/core.lua +++ /dev/null @@ -1,237 +0,0 @@ -local getfenv = require 'busted.compatibility'.getfenv -local setfenv = require 'busted.compatibility'.setfenv -local unpack = require 'busted.compatibility'.unpack -local path = require 'pl.path' -local pretty = require 'pl.pretty' -local throw = error - -local failureMt = { - __index = {}, - __tostring = function(e) return tostring(e.message) end, - __type = 'failure' -} - -local failureMtNoString = { - __index = {}, - __type = 'failure' -} - -local pendingMt = { - __index = {}, - __tostring = function(p) return p.message end, - __type = 'pending' -} - -local function metatype(obj) - local otype = type(obj) - return otype == 'table' and (getmetatable(obj) or {}).__type or otype -end - -local function hasToString(obj) - return type(obj) == 'string' or (getmetatable(obj) or {}).__tostring -end - -return function() - local mediator = require 'mediator'() - - local busted = {} - busted.version = '2.0.rc6-0' - - local root = require 'busted.context'() - busted.context = root.ref() - - local environment = require 'busted.environment'(busted.context) - busted.environment = { - set = environment.set, - - wrap = function(callable) - if (type(callable) == 'function' or getmetatable(callable).__call) then - -- prioritize __call if it exists, like in files - environment.wrap((getmetatable(callable) or {}).__call or callable) - end - end - } - - busted.executors = {} - local executors = {} - - busted.status = require 'busted.status' - - function busted.getTrace(element, level, msg) - level = level or 3 - - local thisdir = path.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 == path.dirname(info.source)) do - level = level + 1 - info = debug.getinfo(level, 'Sl') - end - - info.traceback = debug.traceback('', level) - info.message = msg - - local file = busted.getFile(element) - return file.getTrace(file.name, info) - end - - function busted.rewriteMessage(element, message, trace) - local file = busted.getFile(element) - local msg = hasToString(message) and tostring(message) - msg = msg or (message ~= nil and pretty.write(message) or 'Nil error') - msg = (file.rewriteMessage and file.rewriteMessage(file.name, msg) or msg) - - local hasFileLine = msg:match('^[^\n]-:%d+: .*') - if not hasFileLine then - local trace = trace or busted.getTrace(element, 3, message) - local fileline = trace.short_src .. ':' .. trace.currentline .. ': ' - msg = fileline .. msg - end - - return msg - end - - function busted.publish(...) - return mediator:publish(...) - end - - function busted.subscribe(...) - return mediator:subscribe(...) - end - - function busted.getFile(element) - local parent = busted.context.parent(element) - - while parent do - if parent.file then - local file = parent.file[1] - return { - name = file.name, - getTrace = file.run.getTrace, - rewriteMessage = file.run.rewriteMessage - } - end - - if parent.descriptor == 'file' then - return { - name = parent.name, - getTrace = parent.run.getTrace, - rewriteMessage = parent.run.rewriteMessage - } - end - - parent = busted.context.parent(parent) - end - - return parent - end - - function busted.fail(msg, level) - local rawlevel = (type(level) ~= 'number' or level <= 0) and level - local level = level or 1 - local _, emsg = pcall(throw, msg, rawlevel or level+2) - local e = { message = emsg } - setmetatable(e, hasToString(msg) and failureMt or failureMtNoString) - throw(e, rawlevel or level+1) - end - - function busted.pending(msg) - local p = { message = msg } - setmetatable(p, pendingMt) - throw(p) - end - - function busted.replaceErrorWithFail(callable) - local env = {} - local f = (getmetatable(callable) or {}).__call or callable - setmetatable(env, { __index = getfenv(f) }) - env.error = busted.fail - setfenv(f, env) - end - - function busted.safe(descriptor, run, element) - busted.context.push(element) - local trace, message - local status = 'success' - - local ret = { xpcall(run, function(msg) - local errType = metatype(msg) - status = ((errType == 'pending' or errType == 'failure') and errType or 'error') - trace = busted.getTrace(element, 3, msg) - message = busted.rewriteMessage(element, msg, trace) - end) } - - if not ret[1] then - busted.publish({ status, descriptor }, element, busted.context.parent(element), message, trace) - end - ret[1] = busted.status(status) - - busted.context.pop() - return unpack(ret) - end - - function busted.register(descriptor, executor) - executors[descriptor] = executor - - local publisher = function(name, fn) - if not fn and type(name) == 'function' then - fn = name - name = nil - end - - local trace - - local ctx = busted.context.get() - if busted.context.parent(ctx) then - trace = busted.getTrace(ctx, 3, name) - end - - local publish = function(f) - busted.publish({ 'register', descriptor }, name, f, trace) - end - - if fn then publish(fn) else return publish end - end - - busted.executors[descriptor] = publisher - if descriptor ~= 'file' then - environment.set(descriptor, publisher) - end - - busted.subscribe({ 'register', descriptor }, function(name, fn, trace) - local ctx = busted.context.get() - local plugin = { - descriptor = descriptor, - name = name, - run = fn, - trace = trace - } - - busted.context.attach(plugin) - - if not ctx[descriptor] then - ctx[descriptor] = { plugin } - else - ctx[descriptor][#ctx[descriptor]+1] = plugin - end - end) - end - - function busted.alias(alias, descriptor) - local publisher = busted.executors[descriptor] - busted.executors[alias] = publisher - environment.set(alias, publisher) - end - - function busted.execute(current) - if not current then current = busted.context.get() end - for _, v in pairs(busted.context.children(current)) do - local executor = executors[v.descriptor] - if executor and not busted.skipAll then - busted.safe(v.descriptor, function() executor(v) end, v) - end - end - end - - return busted -end diff --git a/test/busted/done.lua b/test/busted/done.lua deleted file mode 100644 index e6319044d..000000000 --- a/test/busted/done.lua +++ /dev/null @@ -1,121 +0,0 @@ -local M = {} - --- adds tokens to the current wait list, does not change order/unordered -M.wait = function(self, ...) - local tlist = { ... } - - for _, token in ipairs(tlist) do - if type(token) ~= 'string' then - error('Wait tokens must be strings. Got '..type(token), 2) - end - table.insert(self.tokens, token) - end -end - --- set list as unordered, adds tokens to current wait list -M.wait_unordered = function(self, ...) - self.ordered = false - self:wait(...) -end - --- set list as ordered, adds tokens to current wait list -M.wait_ordered = function(self, ...) - self.ordered = true - self:wait(...) -end - --- generates a message listing tokens received/open -M.tokenlist = function(self) - local list - - if #self.tokens_done == 0 then - list = 'No tokens received.' - else - list = 'Tokens received ('..tostring(#self.tokens_done)..')' - local s = ': ' - - for _,t in ipairs(self.tokens_done) do - list = list .. s .. '\''..t..'\'' - s = ', ' - end - - list = list .. '.' - end - - if #self.tokens == 0 then - list = list .. ' No more tokens expected.' - else - list = list .. ' Tokens not received ('..tostring(#self.tokens)..')' - local s = ': ' - - for _, t in ipairs(self.tokens) do - list = list .. s .. '\''..t..'\'' - s = ', ' - end - - list = list .. '.' - end - - return list -end - --- marks a token as completed, checks for ordered/unordered, checks for completeness -M.done = function(self, ...) self:_done(...) end -- extra wrapper for same error level constant as __call method -M._done = function(self, token) - if token then - if type(token) ~= 'string' then - error('Wait tokens must be strings. Got '..type(token), 3) - end - - if self.ordered then - if self.tokens[1] == token then - table.remove(self.tokens, 1) - table.insert(self.tokens_done, token) - else - if self.tokens[1] then - error(('Bad token, expected \'%s\' got \'%s\'. %s'):format(self.tokens[1], token, self:tokenlist()), 3) - else - error(('Bad token (no more tokens expected) got \'%s\'. %s'):format(token, self:tokenlist()), 3) - end - end - else - -- unordered - for i, t in ipairs(self.tokens) do - if t == token then - table.remove(self.tokens, i) - table.insert(self.tokens_done, token) - token = nil - break - end - end - - if token then - error(('Unknown token \'%s\'. %s'):format(token, self:tokenlist()), 3) - end - end - end - if not next(self.tokens) then - -- no more tokens, so we're really done... - self.done_cb() - end -end - - --- wraps a done callback into a done-object supporting tokens to sign-off -M.new = function(done_callback) - local obj = { - tokens = {}, - tokens_done = {}, - done_cb = done_callback, - ordered = true, -- default for sign off of tokens - } - - return setmetatable( obj, { - __call = function(self, ...) - self:_done(...) - end, - __index = M, - }) -end - -return M diff --git a/test/busted/environment.lua b/test/busted/environment.lua deleted file mode 100644 index 686b1388f..000000000 --- a/test/busted/environment.lua +++ /dev/null @@ -1,45 +0,0 @@ -local setfenv = require 'busted.compatibility'.setfenv - -return function(context) - - local environment = {} - - local function getEnv(self, key) - if not self then return nil end - return - self.env and self.env[key] or - getEnv(context.parent(self), key) or - _G[key] - end - - local function setEnv(self, key, value) - if not self.env then self.env = {} end - self.env[key] = value - end - - local function __index(self, key) - return getEnv(context.get(), key) - end - - local function __newindex(self, key, value) - setEnv(context.get(), key, value) - end - - local env = setmetatable({}, { __index=__index, __newindex=__newindex }) - - function environment.wrap(fn) - return setfenv(fn, env) - end - - function environment.set(key, value) - local env = context.get('env') - - if not env then - env = {} - context.set('env', env) - end - - env[key] = value - end - return environment -end diff --git a/test/busted/init.lua b/test/busted/init.lua deleted file mode 100644 index da052f503..000000000 --- a/test/busted/init.lua +++ /dev/null @@ -1,227 +0,0 @@ -local unpack = require 'busted.compatibility'.unpack -local shuffle = require 'busted.utils'.shuffle - -local function sort(elements) - table.sort(elements, function(t1, t2) - if t1.name and t2.name then - return t1.name < t2.name - end - return t2.name ~= nil - end) - return elements -end - -local function remove(descriptors, element) - for _, descriptor in ipairs(descriptors) do - element.env[descriptor] = function(...) - error("'" .. descriptor .. "' not supported inside current context block", 2) - end - end -end - -local function init(busted) - local function exec(descriptor, element) - if not element.env then element.env = {} end - - remove({ 'randomize' }, element) - remove({ 'pending' }, element) - remove({ 'describe', 'context', 'it', 'spec', 'test' }, element) - remove({ 'setup', 'teardown', 'before_each', 'after_each' }, element) - - local parent = busted.context.parent(element) - setmetatable(element.env, { - __newindex = function(self, key, value) - if not parent.env then parent.env = {} end - parent.env[key] = value - end - }) - - local ret = { busted.safe(descriptor, element.run, element) } - return unpack(ret) - end - - local function execAll(descriptor, current, propagate) - local parent = busted.context.parent(current) - - if propagate and parent then - local success, ancestor = execAll(descriptor, parent, propagate) - if not success then - return success, ancestor - end - end - - local list = current[descriptor] or {} - - local success = true - for _, v in pairs(list) do - if not exec(descriptor, v):success() then - success = nil - end - end - return success, current - end - - local function dexecAll(descriptor, current, propagate) - local parent = busted.context.parent(current) - local list = current[descriptor] or {} - - local success = true - for _, v in pairs(list) do - if not exec(descriptor, v):success() then - success = nil - end - end - - if propagate and parent then - if not dexecAll(descriptor, parent, propagate) then - success = nil - end - end - return success - end - - local file = function(file) - busted.publish({ 'file', 'start' }, file.name) - - busted.environment.wrap(file.run) - if not file.env then file.env = {} end - - local randomize = busted.randomize - file.env.randomize = function() randomize = true end - - if busted.safe('file', file.run, file):success() then - if randomize then - file.randomseed = busted.randomseed - shuffle(busted.context.children(file), busted.randomseed) - elseif busted.sort then - sort(busted.context.children(file)) - end - if execAll('setup', file) then - busted.execute(file) - end - dexecAll('teardown', file) - end - - busted.publish({ 'file', 'end' }, file.name) - end - - local describe = function(describe) - local parent = busted.context.parent(describe) - - busted.publish({ 'describe', 'start' }, describe, parent) - - if not describe.env then describe.env = {} end - - local randomize = busted.randomize - describe.env.randomize = function() randomize = true end - - if busted.safe('describe', describe.run, describe):success() then - if randomize then - describe.randomseed = busted.randomseed - shuffle(busted.context.children(describe), busted.randomseed) - elseif busted.sort then - sort(busted.context.children(describe)) - end - if execAll('setup', describe) then - busted.execute(describe) - end - dexecAll('teardown', describe) - end - - busted.publish({ 'describe', 'end' }, describe, parent) - end - - local it = function(element) - local parent = busted.context.parent(element) - local finally - - busted.publish({ 'test', 'start' }, element, parent) - - if not element.env then element.env = {} end - - remove({ 'randomize' }, element) - remove({ 'describe', 'context', 'it', 'spec', 'test' }, element) - remove({ 'setup', 'teardown', 'before_each', 'after_each' }, element) - element.env.finally = function(fn) finally = fn end - element.env.pending = function(msg) busted.pending(msg) end - - local status = busted.status('success') - local updateErrorStatus = function(descriptor) - if element.message then element.message = element.message .. '\n' end - element.message = (element.message or '') .. 'Error in ' .. descriptor - status:update('error') - end - - local pass, ancestor = execAll('before_each', parent, true) - if pass then - status:update(busted.safe('it', element.run, element)) - else - updateErrorStatus('before_each') - end - - if not element.env.done then - remove({ 'pending' }, element) - if finally then status:update(busted.safe('finally', finally, element)) end - if not dexecAll('after_each', ancestor, true) then - updateErrorStatus('after_each') - end - - busted.publish({ 'test', 'end' }, element, parent, tostring(status)) - end - end - - local pending = function(element) - local parent = busted.context.parent(element) - busted.publish({ 'test', 'start' }, element, parent) - busted.publish({ 'test', 'end' }, element, parent, 'pending') - end - - busted.register('file', file) - - busted.register('describe', describe) - - busted.register('it', it) - - busted.register('pending', pending) - - busted.register('setup') - busted.register('teardown') - busted.register('before_each') - busted.register('after_each') - - busted.alias('context', 'describe') - busted.alias('spec', 'it') - busted.alias('test', 'it') - - local assert = require 'luassert' - local spy = require 'luassert.spy' - local mock = require 'luassert.mock' - local stub = require 'luassert.stub' - - busted.environment.set('assert', assert) - busted.environment.set('spy', spy) - busted.environment.set('mock', mock) - busted.environment.set('stub', stub) - - busted.replaceErrorWithFail(assert) - busted.replaceErrorWithFail(assert.True) - - return busted -end - -return setmetatable({}, { - __call = function(self, busted) - local root = busted.context.get() - init(busted) - - return setmetatable(self, { - __index = function(self, key) - return rawget(root.env, key) or busted.executors[key] - end, - - __newindex = function(self, key, value) - error('Attempt to modify busted') - end - }) - end -}) diff --git a/test/busted/languages/ar.lua b/test/busted/languages/ar.lua deleted file mode 100644 index d0b96e65c..000000000 --- a/test/busted/languages/ar.lua +++ /dev/null @@ -1,42 +0,0 @@ -local s = require('say') - -s:set_namespace('ar') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', 'عالِق') -s:set('output.failure', 'فَشَل') -s:set('output.failure', 'نَجاح') - -s:set('output.pending_plural', 'عالِق') -s:set('output.failure_plural', 'إخْفاقات') -s:set('output.success_plural', 'نَجاحات') - -s:set('output.pending_zero', 'عالِق') -s:set('output.failure_zero', 'إخْفاقات') -s:set('output.success_zero', 'نَجاحات') - -s:set('output.pending_single', 'عالِق') -s:set('output.failure_single', 'فَشَل') -s:set('output.success_single', 'نَجاح') - -s:set('output.seconds', 'ثَوانٍ') - --- definitions following are not used within the 'say' namespace -return { - failure_messages = { - 'فَشِلَت %d مِنْ الإِختِبارات', - 'فَشِلَت إخْتِباراتُك', - 'برمجيَّتُكَ ضَعيْفة، أنْصَحُكَ بالتَّقاعُد', - 'تقع برمجيَّتُكَ في مَنطِقَةِ الخَطَر', - 'أقترِحُ ألّا تَتَقَدَّم بالإختِبار، علَّ يبْقى الطابِقُ مَستوراَ', - 'جَدَّتي، فِي أَثْناءِ نَومِها، تَكتبُ بَرمَجياتٍ أفْضلُ مِن هذه', - 'يَوَدُّ ليْ مُساعَدَتُكْ، لَكِنّْ...' - }, - success_messages = { - 'رائِع! تَمَّ إجْتِيازُ جَميعُ الإختِباراتِ بِنَجاحٍ', - 'قُل ما شِئت، لا أكتَرِث: busted شَهِدَ لي!', - 'حَقَّ عَليْكَ الإفتِخار', - 'نَجاحٌ مُبْهِر!', - 'عَليكَ بالإحتِفال؛ نَجَحَت جَميعُ التَجارُب' - } -} diff --git a/test/busted/languages/de.lua b/test/busted/languages/de.lua deleted file mode 100644 index e624a465e..000000000 --- a/test/busted/languages/de.lua +++ /dev/null @@ -1,42 +0,0 @@ -local s = require('say') - -s:set_namespace('de') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', 'Noch nicht erledigt') -s:set('output.failure', 'Fehlgeschlagen') -s:set('output.success', 'Erfolgreich') - -s:set('output.pending_plural', 'übersprungen') -s:set('output.failure_plural', 'fehlgeschlagen') -s:set('output.success_plural', 'erfolgreich') - -s:set('output.pending_zero', 'übersprungen') -s:set('output.failure_zero', 'fehlgeschlagen') -s:set('output.success_zero', 'erfolgreich') - -s:set('output.pending_single', 'übersprungen') -s:set('output.failure_single', 'fehlgeschlagen') -s:set('output.success_single', 'erfolgreich') - -s:set('output.seconds', 'Sekunden') - --- definitions following are not used within the 'say' namespace -return { - failure_messages = { - 'Du hast %d kaputte Tests.', - 'Deine Tests sind kaputt.', - 'Dein Code ist schlecht; du solltest dich schlecht fühlen.', - 'Dein Code befindet sich in der Gefahrenzone.', - 'Ein seltsames Spiel. Der einzig gewinnbringende Zug ist nicht zu testen.', - 'Meine Großmutter hat auf einem 386er bessere Tests geschrieben.', - 'Immer wenn ein Test fehlschlägt, stirbt ein kleines Kätzchen.', - 'Das fühlt sich schlecht an, oder?' - }, - success_messages = { - 'Yeah, die Tests laufen durch.', - 'Fühlt sich gut an, oder?', - 'Großartig!', - 'Tests sind durchgelaufen, Zeit für ein Bier.', - } -} diff --git a/test/busted/languages/en.lua b/test/busted/languages/en.lua deleted file mode 100644 index 285d1ba14..000000000 --- a/test/busted/languages/en.lua +++ /dev/null @@ -1,49 +0,0 @@ -local s = require('say') - -s:set_namespace('en') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', 'Pending') -s:set('output.failure', 'Failure') -s:set('output.error', 'Error') -s:set('output.success', 'Success') - -s:set('output.pending_plural', 'pending') -s:set('output.failure_plural', 'failures') -s:set('output.error_plural', 'errors') -s:set('output.success_plural', 'successes') - -s:set('output.pending_zero', 'pending') -s:set('output.failure_zero', 'failures') -s:set('output.error_zero', 'errors') -s:set('output.success_zero', 'successes') - -s:set('output.pending_single', 'pending') -s:set('output.failure_single', 'failure') -s:set('output.error_single', 'error') -s:set('output.success_single', 'success') - -s:set('output.seconds', 'seconds') - -s:set('output.no_test_files_match', 'No test files found matching Lua pattern: %s') - --- definitions following are not used within the 'say' namespace -return { - failure_messages = { - 'You have %d busted specs', - 'Your specs are busted', - 'Your code is bad and you should feel bad', - 'Your code is in the Danger Zone', - 'Strange game. The only way to win is not to test', - 'My grandmother wrote better specs on a 3 86', - 'Every time there\'s a failure, drink another beer', - 'Feels bad man' - }, - success_messages = { - 'Aww yeah, passing specs', - 'Doesn\'t matter, had specs', - 'Feels good, man', - 'Great success', - 'Tests pass, drink another beer', - } -} diff --git a/test/busted/languages/fr.lua b/test/busted/languages/fr.lua deleted file mode 100644 index 236d87c89..000000000 --- a/test/busted/languages/fr.lua +++ /dev/null @@ -1,45 +0,0 @@ -local s = require('say') - -s:set_namespace('fr') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', 'En attente') -s:set('output.failure', 'Echec') -s:set('output.success', 'Reussite') - -s:set('output.pending_plural', 'en attente') -s:set('output.failure_plural', 'echecs') -s:set('output.success_plural', 'reussites') - -s:set('output.pending_zero', 'en attente') -s:set('output.failure_zero', 'echec') -s:set('output.success_zero', 'reussite') - -s:set('output.pending_single', 'en attente') -s:set('output.failure_single', 'echec') -s:set('output.success_single', 'reussite') - -s:set('output.seconds', 'secondes') - -s:set('output.no_test_files_match', 'Aucun test n\'est pourrait trouvé qui corresponde au motif de Lua: %s') - --- definitions following are not used within the 'say' namespace -return { - failure_messages = { - 'Vous avez %d test(s) qui a/ont echoue(s)', - 'Vos tests ont echoue.', - 'Votre code source est mauvais et vous devrez vous sentir mal', - 'Vous avez un code source de Destruction Massive', - 'Jeu plutot etrange game. Le seul moyen de gagner est de ne pas l\'essayer', - 'Meme ma grand-mere ecrivait de meilleurs tests sur un PIII x86', - 'A chaque erreur, prenez une biere', - 'Ca craint, mon pote' - }, - success_messages = { - 'Oh yeah, tests reussis', - 'Pas grave, y\'a eu du succes', - 'C\'est du bon, mon pote. Que du bon!', - 'Reussi, haut la main!', - 'Test reussi. Un de plus. Offre toi une biere, sur mon compte!', - } -} diff --git a/test/busted/languages/ja.lua b/test/busted/languages/ja.lua deleted file mode 100644 index d726fa8f9..000000000 --- a/test/busted/languages/ja.lua +++ /dev/null @@ -1,43 +0,0 @@ -local s = require('say') - -s:set_namespace('ja') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', '保留') -s:set('output.failure', '失敗') -s:set('output.success', '成功') - -s:set('output.pending_plural', '保留') -s:set('output.failure_plural', '失敗') -s:set('output.success_plural', '成功') - -s:set('output.pending_zero', '保留') -s:set('output.failure_zero', '失敗') -s:set('output.success_zero', '成功') - -s:set('output.pending_single', '保留') -s:set('output.failure_single', '失敗') -s:set('output.success_single', '成功') - -s:set('output.seconds', '秒') - --- definitions following are not used within the 'say' namespace -return { - failure_messages = { - '%d個の仕様が破綻しています', - '仕様が破綻しています', - 'あなたの書くコードは良くないので反省するべきです', - 'あなたの書くコードは危険地帯にあります', - 'おかしなゲームです。勝利する唯一の方法はテストをしないことです', - '私の祖母でもPentium Pentium III x86の上でもっといいコードを書いていましたよ', - 'いつも失敗しているのでビールでも飲みましょう', - '罪悪感を持ちましょう', - }, - success_messages = { - 'オォーイェー、テストが通った', - '問題ない、テストがある', - '順調ですね', - '大成功', - 'テストが通ったし、ビールでも飲もう', - } -} diff --git a/test/busted/languages/nl.lua b/test/busted/languages/nl.lua deleted file mode 100644 index 6173320bd..000000000 --- a/test/busted/languages/nl.lua +++ /dev/null @@ -1,43 +0,0 @@ -local s = require('say') - -s:set_namespace('nl') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', 'Hangend') -s:set('output.failure', 'Mislukt') -s:set('output.success', 'Succes') - -s:set('output.pending_plural', 'hangenden') -s:set('output.failure_plural', 'mislukkingen') -s:set('output.success_plural', 'successen') - -s:set('output.pending_zero', 'hangend') -s:set('output.failure_zero', 'mislukt') -s:set('output.success_zero', 'successen') - -s:set('output.pending_single', 'hangt') -s:set('output.failure_single', 'mislukt') -s:set('output.success_single', 'succes') - -s:set('output.seconds', 'seconden') - --- definitions following are not used within the 'say' namespace -return { - failure_messages = { - 'Je hebt %d busted specs', - 'Je specs zijn busted', - 'Je code is slecht en zo zou jij je ook moeten voelen', - 'Je code zit in de Gevaren Zone', - 'Vreemd spelletje. The enige manier om te winnen is door niet te testen', - 'Mijn oma schreef betere specs op een 3 86', - 'Elke keer dat iets mislukt, nog een biertje drinken', - 'Voelt klote man' - }, - success_messages = { - 'Joeperdepoep, de specs zijn er door', - 'Doet er niet toe, had specs', - 'Voelt goed, man', - 'Fantastisch success', - 'Testen geslaagd, neem nog een biertje', - } -} diff --git a/test/busted/languages/ru.lua b/test/busted/languages/ru.lua deleted file mode 100644 index d0f403440..000000000 --- a/test/busted/languages/ru.lua +++ /dev/null @@ -1,37 +0,0 @@ -local s = require('say') - -s:set_namespace('ru') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', 'Ожидает') -s:set('output.failure', 'Поломалcя') -s:set('output.success', 'Прошeл') - -s:set('output.pending_plural', 'ожидают') -s:set('output.failure_plural', 'поломалиcь') -s:set('output.success_plural', 'прошли') - -s:set('output.pending_zero', 'ожидающих') -s:set('output.failure_zero', 'поломанных') -s:set('output.success_zero', 'прошедших') - -s:set('output.pending_single', 'ожидает') -s:set('output.failure_single', 'поломался') -s:set('output.success_single', 'прошел') - -s:set('output.seconds', 'секунд') - ----- definitions following are not used within the 'say' namespace -return { - failure_messages = { - 'У тебя %d просратых тестов', - 'Твои тесты поломаны', - 'Твой код говеный - пойди напейся!' - }, - success_messages = { - 'Поехали!', - 'Жизнь - хороша!', - 'Ффух в этот раз пронесло!', - 'Ура!' - } -} diff --git a/test/busted/languages/th.lua b/test/busted/languages/th.lua deleted file mode 100644 index 0f8e0a13a..000000000 --- a/test/busted/languages/th.lua +++ /dev/null @@ -1,43 +0,0 @@ -local s = require('say') - -s:set_namespace('th') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', 'อยู่ระหว่างดำเนินการ') -s:set('output.failure', 'ล้มเหลว') -s:set('output.success', 'สำเร็จ') - -s:set('output.pending_plural', 'อยู่ระหว่างดำเนินการ') -s:set('output.failure_plural', 'ล้มเหลว') -s:set('output.success_plural', 'สำเร็จ') - -s:set('output.pending_zero', 'อยู่ระหว่างดำเนินการ') -s:set('output.failure_zero', 'ล้มเหลว') -s:set('output.success_zero', 'สำเร็จ') - -s:set('output.pending_single', 'อยู่ระหว่างดำเนินการ') -s:set('output.failure_single', 'ล้มเหลว') -s:set('output.success_single', 'สำเร็จ') - -s:set('output.seconds', 'วินาที') - --- definitions following are not used within the 'say' namespace -return { - failure_messages = { - 'คุณมี %d บัสเต็ดสเปค', - 'สเปคของคุณคือ บัสเต็ด', - 'โค้ดของคุณไม่ดีเลย คุณควรรู้สึกแย่น่ะ', - 'โค้ดของคุณอยู่ในเขตอันตราย!', - 'มันแปลกๆน่ะ วิธีที่จะชนะไม่ได้มีแค่เทสอย่างเดียว', - 'ยายผมเขียนสเปคดีกว่านี้อีก บนเครื่อง 386', - 'ทุกๆครั้งที่ล้มเหลว, ดื่มเบียร์แก้วใหม่', - 'แย่จัง นายท่าน' - }, - success_messages = { - 'อุ๊ตะ!!!, สเปคผ่าน!', - 'ไม่สำคัญ, มีสเปค', - 'ฟินเลยดิ นายท่าน', - 'สำเร็จ ยอดเยี่ยม', - 'เทสผ่าน, ดื่มเบียร์ๆๆๆ', - } -} diff --git a/test/busted/languages/ua.lua b/test/busted/languages/ua.lua deleted file mode 100644 index 40afcfbab..000000000 --- a/test/busted/languages/ua.lua +++ /dev/null @@ -1,37 +0,0 @@ -local s = require('say') - -s:set_namespace('ua') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', 'Очікує') -s:set('output.failure', 'Зламався') -s:set('output.success', 'Пройшов') - -s:set('output.pending_plural', 'очікують') -s:set('output.failure_plural', 'зламались') -s:set('output.success_plural', 'пройшли') - -s:set('output.pending_zero', 'очікуючих') -s:set('output.failure_zero', 'зламаних') -s:set('output.success_zero', 'пройдених') - -s:set('output.pending_single', 'очікує') -s:set('output.failure_single', 'зламався') -s:set('output.success_single', 'пройшов') - -s:set('output.seconds', 'секунд') - - ----- definitions following are not used within the 'say' namespace -return { - failure_messages = { - 'Ти зрадив %d тестів!', - 'Ой йо..', - 'Вороги поламали наші тести!' - }, - success_messages = { - 'Слава Україні! Героям Слава!', - 'Тестування успішно пройдено!', - 'Всі баги знищено!' - } -} diff --git a/test/busted/languages/zh.lua b/test/busted/languages/zh.lua deleted file mode 100644 index 21f97e5cb..000000000 --- a/test/busted/languages/zh.lua +++ /dev/null @@ -1,43 +0,0 @@ -local s = require('say') - -s:set_namespace('zh') - --- 'Pending: test.lua @ 12 \n description -s:set('output.pending', '开发中') -s:set('output.failure', '失败') -s:set('output.success', '成功') - -s:set('output.pending_plural', '开发中') -s:set('output.failure_plural', '失败') -s:set('output.success_plural', '成功') - -s:set('output.pending_zero', '开发中') -s:set('output.failure_zero', '失败') -s:set('output.success_zero', '成功') - -s:set('output.pending_single', '开发中') -s:set('output.failure_single', '失败') -s:set('output.success_single', '成功') - -s:set('output.seconds', '秒') - --- definitions following are not used within the 'say' namespace -return { - failure_messages = { - '你一共提交了[%d]个测试用例', - '又出错了!', - '到底哪里不对呢?', - '出错了,又要加班了!', - '囧,出Bug了!', - '据说比尔盖兹也写了一堆Bug,别灰心!', - '又出错了,休息一下吧', - 'Bug好多,心情好坏!' - }, - success_messages = { - '牛X,测试通过了!', - '测试通过了,感觉不错吧,兄弟!', - '哥们,干得漂亮!', - '终于通过了!干一杯先!', - '阿弥陀佛~,菩萨显灵了!', - } -} diff --git a/test/busted/modules/configuration_loader.lua b/test/busted/modules/configuration_loader.lua deleted file mode 100644 index f437a04cd..000000000 --- a/test/busted/modules/configuration_loader.lua +++ /dev/null @@ -1,34 +0,0 @@ -return function() - local tablex = require 'pl.tablex' - - -- Function to load the .busted configuration file if available - local loadBustedConfigurationFile = function(configFile, config, defaults) - if type(configFile) ~= 'table' then - return config, '.busted file does not return a table.' - end - - local defaults = defaults or {} - local run = config.run or defaults.run - - if run and run ~= '' then - local runConfig = configFile[run] - - if type(runConfig) == 'table' then - config = tablex.merge(runConfig, config, true) - else - return config, 'Task `' .. run .. '` not found, or not a table.' - end - end - - if type(configFile.default) == 'table' then - config = tablex.merge(configFile.default, config, true) - end - - config = tablex.merge(defaults, config, true) - - return config - end - - return loadBustedConfigurationFile -end - diff --git a/test/busted/modules/files/lua.lua b/test/busted/modules/files/lua.lua deleted file mode 100644 index a4218f6d4..000000000 --- a/test/busted/modules/files/lua.lua +++ /dev/null @@ -1,24 +0,0 @@ -local path = require 'pl.path' - -local ret = {} - -local getTrace = function(filename, info) - local index = info.traceback:find('\n%s*%[C]') - info.traceback = info.traceback:sub(1, index) - return info, false -end - -ret.match = function(busted, filename) - return path.extension(filename) == '.lua' -end - - -ret.load = function(busted, filename) - local file, err = loadfile(filename) - if not file then - busted.publish({ 'error', 'file' }, { descriptor = 'file', name = filename }, nil, err, {}) - end - return file, getTrace -end - -return ret diff --git a/test/busted/modules/files/moonscript.lua b/test/busted/modules/files/moonscript.lua deleted file mode 100644 index 145b942f5..000000000 --- a/test/busted/modules/files/moonscript.lua +++ /dev/null @@ -1,108 +0,0 @@ -local path = require 'pl.path' - -local ok, moonscript, line_tables, util = pcall(function() - return require 'moonscript', require 'moonscript.line_tables', require 'moonscript.util' -end) - -local _cache = {} - --- find the line number of `pos` chars into fname -local lookup_line = function(fname, pos) - if not _cache[fname] then - local f = io.open(fname) - _cache[fname] = f:read('*a') - f:close() - end - - return util.pos_to_line(_cache[fname], pos) -end - -local rewrite_linenumber = function(fname, lineno) - local tbl = line_tables['@' .. fname] - if fname and tbl then - for i = lineno, 0 ,-1 do - if tbl[i] then - return lookup_line(fname, tbl[i]) - end - end - end - - return lineno -end - -local rewrite_filename = function(filename) - -- sometimes moonscript gives files like [string "./filename.moon"], so - -- we'll chop it up to only get the filename. - return filename:match('string "(.+)"') or filename -end - -local rewrite_traceback = function(fname, trace) - local rewrite_one = function(line, pattern, sub) - if line == nil then return '' end - - local fname, lineno = line:match(pattern) - - if fname and lineno then - fname = rewrite_filename(fname) - local new_lineno = rewrite_linenumber(fname, tonumber(lineno)) - if new_lineno then - line = line:gsub(sub:format(tonumber(lineno)), sub:format(tonumber(new_lineno))) - end - end - - return line - end - - local lines = {} - local j = 0 - - for line in trace:gmatch('[^\r\n]+') do - j = j + 1 - line = rewrite_one(line, '%s*(.-):(%d+): ', ':%d:') - line = rewrite_one(line, '<(.*):(%d+)>', ':%d>') - lines[j] = line - end - - return '\n' .. table.concat(lines, trace:match('[\r\n]+')) .. '\n' -end - -local ret = {} - -local getTrace = function(filename, info) - local index = info.traceback:find('\n%s*%[C]') - info.traceback = info.traceback:sub(1, index) - - info.short_src = rewrite_filename(info.short_src) - info.traceback = rewrite_traceback(filename, info.traceback) - info.linedefined = rewrite_linenumber(filename, info.linedefined) - info.currentline = rewrite_linenumber(filename, info.currentline) - - return info -end - -local rewriteMessage = function(filename, message) - local fname, line, msg = message:match('^([^\n]-):(%d+): (.*)') - if not fname then - return message - end - - fname = rewrite_filename(fname) - line = rewrite_linenumber(fname, tonumber(line)) - - return fname .. ':' .. tostring(line) .. ': ' .. msg -end - -ret.match = function(busted, filename) - return ok and path.extension(filename) == '.moon' -end - - -ret.load = function(busted, filename) - local file, err = moonscript.loadfile(filename) - if not file then - busted.publish({ 'error', 'file' }, { descriptor = 'file', name = filename }, nil, err, {}) - end - return file, getTrace, rewriteMessage -end - -return ret diff --git a/test/busted/modules/files/terra.lua b/test/busted/modules/files/terra.lua deleted file mode 100644 index 02956f76a..000000000 --- a/test/busted/modules/files/terra.lua +++ /dev/null @@ -1,24 +0,0 @@ -local path = require 'pl.path' - -local ret = {} -local ok, terralib = pcall(function() return require 'terralib' end) - -local getTrace = function(filename, info) - local index = info.traceback:find('\n%s*%[C]') - info.traceback = info.traceback:sub(1, index) - return info -end - -ret.match = function(busted, filename) - return ok and path.extension(filename) == '.t' -end - -ret.load = function(busted, filename) - local file, err = terralib.loadfile(filename) - if not file then - busted.publish({ 'error', 'file' }, { descriptor = 'file', name = filename }, nil, err, {}) - end - return file, getTrace -end - -return ret diff --git a/test/busted/modules/helper_loader.lua b/test/busted/modules/helper_loader.lua deleted file mode 100644 index 166f7ccd1..000000000 --- a/test/busted/modules/helper_loader.lua +++ /dev/null @@ -1,23 +0,0 @@ -local utils = require 'busted.utils' -local hasMoon, moonscript = pcall(require, 'moonscript') - -return function() - local loadHelper = function(helper, hpath, options, busted) - local success, err = pcall(function() - arg = options.arguments - if helper:match('%.lua$') then - dofile(utils.normpath(hpath)) - elseif hasMoon and helper:match('%.moon$') then - moonscript.dofile(utils.normpath(hpath)) - else - require(helper) - end - end) - - if not success then - busted.publish({ 'error', 'helper' }, { descriptor = 'helper', name = helper }, nil, err, {}) - end - end - - return loadHelper -end diff --git a/test/busted/modules/luacov.lua b/test/busted/modules/luacov.lua deleted file mode 100644 index 99cfc8f56..000000000 --- a/test/busted/modules/luacov.lua +++ /dev/null @@ -1,22 +0,0 @@ -return function() - -- Function to initialize luacov if available - local loadLuaCov = function() - local result, luacov = pcall(require, 'luacov.runner') - - if not result then - return print('LuaCov not found on the system, try running without --coverage option, or install LuaCov first') - end - - -- call it to start - luacov() - - -- exclude busted files - table.insert(luacov.configuration.exclude, 'busted_bootstrap$') - table.insert(luacov.configuration.exclude, 'busted%.') - table.insert(luacov.configuration.exclude, 'luassert%.') - table.insert(luacov.configuration.exclude, 'say%.') - table.insert(luacov.configuration.exclude, 'pl%.') - end - - return loadLuaCov -end diff --git a/test/busted/modules/output_handler_loader.lua b/test/busted/modules/output_handler_loader.lua deleted file mode 100644 index d52211846..000000000 --- a/test/busted/modules/output_handler_loader.lua +++ /dev/null @@ -1,31 +0,0 @@ -local utils = require 'busted.utils' -local hasMoon, moonscript = pcall(require, 'moonscript') - -return function() - local loadOutputHandler = function(output, opath, options, busted, defaultOutput) - local handler - - local success, err = pcall(function() - if output:match('%.lua$') then - handler = dofile(utils.normpath(opath)) - elseif hasMoon and output:match('%.moon$') then - handler = moonscript.dofile(utils.normpath(opath)) - else - handler = require('busted.outputHandlers.' .. output) - end - end) - - if not success and err:match("module '.-' not found:") then - success, err = pcall(function() handler = require(output) end) - end - - if not success then - busted.publish({ 'error', 'output' }, { descriptor = 'output', name = output }, nil, err, {}) - handler = require('busted.outputHandlers.' .. defaultOutput) - end - - return handler(options, busted) - end - - return loadOutputHandler -end diff --git a/test/busted/modules/test_file_loader.lua b/test/busted/modules/test_file_loader.lua deleted file mode 100644 index 391cce501..000000000 --- a/test/busted/modules/test_file_loader.lua +++ /dev/null @@ -1,84 +0,0 @@ -local s = require 'say' - -return function(busted, loaders, options) - local path = require 'pl.path' - local dir = require 'pl.dir' - local tablex = require 'pl.tablex' - local shuffle = require 'busted.utils'.shuffle - local fileLoaders = {} - - for _, v in pairs(loaders) do - local loader = require('busted.modules.files.'..v) - fileLoaders[#fileLoaders+1] = loader - end - - local getTestFiles = function(rootFile, pattern) - local fileList - - if path.isfile(rootFile) then - fileList = { rootFile } - elseif path.isdir(rootFile) then - local getfiles = options.recursive and dir.getallfiles or dir.getfiles - fileList = getfiles(rootFile) - - fileList = tablex.filter(fileList, function(filename) - return path.basename(filename):find(pattern) - end) - - fileList = tablex.filter(fileList, function(filename) - if path.is_windows then - return not filename:find('%\\%.%w+.%w+') - else - return not filename:find('/%.%w+.%w+') - end - end) - else - fileList = {} - end - - return fileList - end - - -- runs a testfile, loading its tests - local loadTestFile = function(busted, filename) - for _, v in pairs(fileLoaders) do - if v.match(busted, filename) then - return v.load(busted, filename) - end - end - end - - local loadTestFiles = function(rootFile, pattern, loaders) - local fileList = getTestFiles(rootFile, pattern) - - if options.shuffle then - shuffle(fileList, options.seed) - elseif options.sort then - table.sort(fileList) - end - - for i, fileName in ipairs(fileList) do - local testFile, getTrace, rewriteMessage = loadTestFile(busted, fileName, loaders) - - if testFile then - local file = setmetatable({ - getTrace = getTrace, - rewriteMessage = rewriteMessage - }, { - __call = testFile - }) - - busted.executors.file(fileName, file) - end - end - - if #fileList == 0 then - busted.publish({ 'error' }, {}, nil, s('output.no_test_files_match'):format(pattern), {}) - end - - return fileList - end - - return loadTestFiles, loadTestFile, getTestFiles -end - diff --git a/test/busted/outputHandlers/TAP.lua b/test/busted/outputHandlers/TAP.lua deleted file mode 100644 index b46350ec8..000000000 --- a/test/busted/outputHandlers/TAP.lua +++ /dev/null @@ -1,51 +0,0 @@ -local pretty = require 'pl.pretty' - -return function(options, busted) - local handler = require 'busted.outputHandlers.base'(busted) - - handler.suiteEnd = function() - local total = handler.successesCount + handler.errorsCount + handler.failuresCount - print('1..' .. total) - - local success = 'ok %u - %s' - local failure = 'not ' .. success - local counter = 0 - - for i,t in pairs(handler.successes) do - counter = counter + 1 - print(success:format(counter, t.name)) - end - - local showFailure = function(t) - counter = counter + 1 - local message = t.message - local trace = t.trace or {} - - if message == nil then - message = 'Nil error' - elseif type(message) ~= 'string' then - message = pretty.write(message) - end - - print(failure:format(counter, t.name)) - print('# ' .. t.element.trace.short_src .. ' @ ' .. t.element.trace.currentline) - print('# Failure message: ' .. message:gsub('\n', '\n# ')) - if options.verbose and trace.traceback then - print('# ' .. trace.traceback:gsub('^\n', '', 1):gsub('\n', '\n# ')) - end - end - - for i,t in pairs(handler.errors) do - showFailure(t) - end - for i,t in pairs(handler.failures) do - showFailure(t) - end - - return nil, true - end - - busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) - - return handler -end diff --git a/test/busted/outputHandlers/base.lua b/test/busted/outputHandlers/base.lua deleted file mode 100644 index 5ea9540fc..000000000 --- a/test/busted/outputHandlers/base.lua +++ /dev/null @@ -1,177 +0,0 @@ -return function(busted) - local handler = { - successes = {}, - successesCount = 0, - pendings = {}, - pendingsCount = 0, - failures = {}, - failuresCount = 0, - errors = {}, - errorsCount = 0, - inProgress = {} - } - - handler.cancelOnPending = function(element, parent, status) - return not ((element.descriptor == 'pending' or status == 'pending') and handler.options.suppressPending) - end - - handler.subscribe = function(handler, options) - require('busted.languages.en') - handler.options = options - - if options.language ~= 'en' then - require('busted.languages.' .. options.language) - end - - busted.subscribe({ 'suite', 'reinitialize' }, handler.baseSuiteRepeat, { priority = 1 }) - busted.subscribe({ 'suite', 'start' }, handler.baseSuiteStart, { priority = 1 }) - busted.subscribe({ 'suite', 'end' }, handler.baseSuiteEnd, { priority = 1 }) - busted.subscribe({ 'test', 'start' }, handler.baseTestStart, { priority = 1, predicate = handler.cancelOnPending }) - busted.subscribe({ 'test', 'end' }, handler.baseTestEnd, { priority = 1, predicate = handler.cancelOnPending }) - busted.subscribe({ 'pending' }, handler.basePending, { priority = 1, predicate = handler.cancelOnPending }) - busted.subscribe({ 'failure', 'it' }, handler.baseTestFailure, { priority = 1 }) - busted.subscribe({ 'error', 'it' }, handler.baseTestError, { priority = 1 }) - busted.subscribe({ 'failure' }, handler.baseError, { priority = 1 }) - busted.subscribe({ 'error' }, handler.baseError, { priority = 1 }) - end - - handler.getFullName = function(context) - local parent = busted.context.parent(context) - local names = { (context.name or context.descriptor) } - - while parent and (parent.name or parent.descriptor) and - parent.descriptor ~= 'file' do - - table.insert(names, 1, parent.name or parent.descriptor) - parent = busted.context.parent(parent) - end - - return table.concat(names, ' ') - end - - handler.format = function(element, parent, message, debug, isError) - local formatted = { - trace = debug or element.trace, - element = element, - name = handler.getFullName(element), - message = message, - isError = isError - } - formatted.element.trace = element.trace or debug - - return formatted - end - - handler.getDuration = function() - if not handler.endTime or not handler.startTime then - return 0 - end - - return handler.endTime - handler.startTime - end - - handler.baseSuiteStart = function() - handler.startTime = os.clock() - return nil, true - end - - handler.baseSuiteRepeat = function() - handler.successes = {} - handler.successesCount = 0 - handler.pendings = {} - handler.pendingsCount = 0 - handler.failures = {} - handler.failuresCount = 0 - handler.errors = {} - handler.errorsCount = 0 - handler.inProgress = {} - - return nil, true - end - - handler.baseSuiteEnd = function() - handler.endTime = os.clock() - return nil, true - end - - handler.baseTestStart = function(element, parent) - handler.inProgress[tostring(element)] = {} - return nil, true - end - - handler.baseTestEnd = function(element, parent, status, debug) - local isError - local insertTable - - if status == 'success' then - insertTable = handler.successes - handler.successesCount = handler.successesCount + 1 - elseif status == 'pending' then - insertTable = handler.pendings - handler.pendingsCount = handler.pendingsCount + 1 - elseif status == 'failure' then - insertTable = handler.failures - -- failure count already incremented in error handler - elseif status == 'error' then - -- error count already incremented in error handler - insertTable = handler.errors - isError = true - end - - local formatted = handler.format(element, parent, element.message, debug, isError) - - local id = tostring(element) - if handler.inProgress[id] then - for k, v in pairs(handler.inProgress[id]) do - formatted[k] = v - end - - handler.inProgress[id] = nil - end - - table.insert(insertTable, formatted) - - return nil, true - end - - local function saveInProgress(element, message, debug) - local id = tostring(element) - handler.inProgress[id].message = message - handler.inProgress[id].trace = debug - end - - local function saveError(element, parent, message, debug) - if parent.randomseed then - message = 'Random Seed: ' .. parent.randomseed .. '\n' .. message - end - saveInProgress(element, message, debug) - end - - handler.basePending = function(element, parent, message, debug) - saveInProgress(element, message, debug) - return nil, true - end - - handler.baseTestFailure = function(element, parent, message, debug) - handler.failuresCount = handler.failuresCount + 1 - saveError(element, parent, message, debug) - return nil, true - end - - handler.baseTestError = function(element, parent, message, debug) - handler.errorsCount = handler.errorsCount + 1 - saveError(element, parent, message, debug) - return nil, true - end - - handler.baseError = function(element, parent, message, debug) - if element.descriptor ~= 'it' then - handler.errorsCount = handler.errorsCount + 1 - table.insert(handler.errors, handler.format(element, parent, message, debug, true)) - end - - return nil, true - end - - return handler -end diff --git a/test/busted/outputHandlers/json.lua b/test/busted/outputHandlers/json.lua deleted file mode 100644 index f19a336aa..000000000 --- a/test/busted/outputHandlers/json.lua +++ /dev/null @@ -1,21 +0,0 @@ -local json = require 'dkjson' - -return function(options, busted) - local handler = require 'busted.outputHandlers.base'(busted) - - handler.suiteEnd = function() - print(json.encode({ - pendings = handler.pendings, - successes = handler.successes, - failures = handler.failures, - errors = handler.errors, - duration = handler.getDuration() - })) - - return nil, true - end - - busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) - - return handler -end diff --git a/test/busted/outputHandlers/junit.lua b/test/busted/outputHandlers/junit.lua deleted file mode 100644 index 36e78936e..000000000 --- a/test/busted/outputHandlers/junit.lua +++ /dev/null @@ -1,136 +0,0 @@ -local xml = require 'pl.xml' -local socket = require("socket") -local string = require("string") - -return function(options, busted) - local handler = require 'busted.outputHandlers.base'(busted) - local top = { - start_time = socket.gettime(), - xml_doc = xml.new('testsuites', { - tests = 0, - errors = 0, - failures = 0, - skip = 0, - }) - } - local stack = {} - local testStartTime - - handler.suiteStart = function(count, total) - local suite = { - start_time = socket.gettime(), - xml_doc = xml.new('testsuite', { - name = 'Run ' .. count .. ' of ' .. total, - tests = 0, - errors = 0, - failures = 0, - skip = 0, - timestamp = os.date('!%Y-%m-%dT%T'), - }) - } - top.xml_doc:add_direct_child(suite.xml_doc) - table.insert(stack, top) - top = suite - - return nil, true - end - - local function elapsed(start_time) - return string.format("%.2f", (socket.gettime() - start_time)) - end - - handler.suiteEnd = function(count, total) - local suite = top - suite.xml_doc.attr.time = elapsed(suite.start_time) - - top = table.remove(stack) - top.xml_doc.attr.tests = top.xml_doc.attr.tests + suite.xml_doc.attr.tests - top.xml_doc.attr.errors = top.xml_doc.attr.errors + suite.xml_doc.attr.errors - top.xml_doc.attr.failures = top.xml_doc.attr.failures + suite.xml_doc.attr.failures - top.xml_doc.attr.skip = top.xml_doc.attr.skip + suite.xml_doc.attr.skip - - return nil, true - end - - handler.exit = function() - top.xml_doc.attr.time = elapsed(top.start_time) - print(xml.tostring(top.xml_doc, '', '\t', nil, false)) - - return nil, true - end - - local function testStatus(element, parent, message, status, trace) - local testcase_node = xml.new('testcase', { - classname = element.trace.short_src .. ':' .. element.trace.currentline, - name = handler.getFullName(element), - time = elapsed(testStartTime) - }) - top.xml_doc:add_direct_child(testcase_node) - - if status ~= 'success' then - testcase_node:addtag(status) - if message then testcase_node:text(message) end - if trace and trace.traceback then testcase_node:text(trace.traceback) end - testcase_node:up() - end - end - - handler.testStart = function(element, parent) - testStartTime = socket.gettime() - - return nil, true - end - - handler.testEnd = function(element, parent, status) - top.xml_doc.attr.tests = top.xml_doc.attr.tests + 1 - - if status == 'success' then - testStatus(element, parent, nil, 'success') - elseif status == 'pending' then - top.xml_doc.attr.skip = top.xml_doc.attr.skip + 1 - local formatted = handler.pendings[#handler.pendings] - local trace = element.trace ~= formatted.trace and formatted.trace - testStatus(element, parent, formatted.message, 'skipped', trace) - end - - return nil, true - end - - handler.failureTest = function(element, parent, message, trace) - top.xml_doc.attr.failures = top.xml_doc.attr.failures + 1 - testStatus(element, parent, message, 'failure', trace) - return nil, true - end - - handler.errorTest = function(element, parent, message, trace) - top.xml_doc.attr.errors = top.xml_doc.attr.errors + 1 - testStatus(element, parent, message, 'error', trace) - return nil, true - end - - handler.error = function(element, parent, message, trace) - if element.descriptor ~= 'it' then - top.xml_doc.attr.errors = top.xml_doc.attr.errors + 1 - top.xml_doc:addtag('error') - top.xml_doc:text(message) - if trace and trace.traceback then - top.xml_doc:text(trace.traceback) - end - top.xml_doc:up() - end - - return nil, true - end - - busted.subscribe({ 'exit' }, handler.exit) - busted.subscribe({ 'suite', 'start' }, handler.suiteStart) - busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) - busted.subscribe({ 'test', 'start' }, handler.testStart, { predicate = handler.cancelOnPending }) - busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending }) - busted.subscribe({ 'error', 'it' }, handler.errorTest) - busted.subscribe({ 'failure', 'it' }, handler.failureTest) - busted.subscribe({ 'error' }, handler.error) - busted.subscribe({ 'failure' }, handler.error) - - return handler -end diff --git a/test/busted/outputHandlers/plainTerminal.lua b/test/busted/outputHandlers/plainTerminal.lua deleted file mode 100644 index fc4b092f2..000000000 --- a/test/busted/outputHandlers/plainTerminal.lua +++ /dev/null @@ -1,175 +0,0 @@ -local s = require 'say' -local pretty = require 'pl.pretty' - -return function(options, busted) - local handler = require 'busted.outputHandlers.base'(busted) - - local successDot = '+' - local failureDot = '-' - local errorDot = '*' - local pendingDot = '.' - - local pendingDescription = function(pending) - local name = pending.name - - local string = s('output.pending') .. ' → ' .. - pending.trace.short_src .. ' @ ' .. - pending.trace.currentline .. - '\n' .. name - - if type(pending.message) == 'string' then - string = string .. '\n' .. pending.message - elseif pending.message ~= nil then - string = string .. '\n' .. pretty.write(pending.message) - end - - return string - end - - local failureMessage = function(failure) - local string - if type(failure.message) == 'string' then - string = failure.message - elseif failure.message == nil then - string = 'Nil error' - else - string = pretty.write(failure.message) - end - - return string - end - - local failureDescription = function(failure, isError) - local string = s('output.failure') .. ' → ' - if isError then - string = s('output.error') .. ' → ' - end - - if not failure.element.trace or not failure.element.trace.short_src then - string = string .. - failureMessage(failure) .. '\n' .. - failure.name - else - string = string .. - failure.element.trace.short_src .. ' @ ' .. - failure.element.trace.currentline .. '\n' .. - failure.name .. '\n' .. - failureMessage(failure) - end - - if options.verbose and failure.trace and failure.trace.traceback then - string = string .. '\n' .. failure.trace.traceback - end - - return string - end - - local statusString = function() - local successString = s('output.success_plural') - local failureString = s('output.failure_plural') - local pendingString = s('output.pending_plural') - local errorString = s('output.error_plural') - - local ms = handler.getDuration() - local successes = handler.successesCount - local pendings = handler.pendingsCount - local failures = handler.failuresCount - local errors = handler.errorsCount - - if successes == 0 then - successString = s('output.success_zero') - elseif successes == 1 then - successString = s('output.success_single') - end - - if failures == 0 then - failureString = s('output.failure_zero') - elseif failures == 1 then - failureString = s('output.failure_single') - end - - if pendings == 0 then - pendingString = s('output.pending_zero') - elseif pendings == 1 then - pendingString = s('output.pending_single') - end - - if errors == 0 then - errorString = s('output.error_zero') - elseif errors == 1 then - errorString = s('output.error_single') - end - - local formattedTime = ('%.6f'):format(ms):gsub('([0-9])0+$', '%1') - - return successes .. ' ' .. successString .. ' / ' .. - failures .. ' ' .. failureString .. ' / ' .. - errors .. ' ' .. errorString .. ' / ' .. - pendings .. ' ' .. pendingString .. ' : ' .. - formattedTime .. ' ' .. s('output.seconds') - end - - handler.testEnd = function(element, parent, status, debug) - if not options.deferPrint then - local string = successDot - - if status == 'pending' then - string = pendingDot - elseif status == 'failure' then - string = failureDot - elseif status == 'error' then - string = errorDot - end - - io.write(string) - io.flush() - end - - return nil, true - end - - handler.suiteStart = function(count, total) - local runString = (total > 1 and '\nRepeating all tests (run %d of %d) . . .\n\n' or '') - io.write(runString:format(count, total)) - io.flush() - end - - handler.suiteEnd = function() - print('') - print(statusString()) - - for i, pending in pairs(handler.pendings) do - print('') - print(pendingDescription(pending)) - end - - for i, err in pairs(handler.failures) do - print('') - print(failureDescription(err)) - end - - for i, err in pairs(handler.errors) do - print('') - print(failureDescription(err, true)) - end - - return nil, true - end - - handler.error = function(element, parent, message, debug) - io.write(errorDot) - io.flush() - - return nil, true - end - - busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending }) - busted.subscribe({ 'suite', 'start' }, handler.suiteStart) - busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) - busted.subscribe({ 'error', 'file' }, handler.error) - busted.subscribe({ 'failure', 'file' }, handler.error) - busted.subscribe({ 'error', 'describe' }, handler.error) - busted.subscribe({ 'failure', 'describe' }, handler.error) - - return handler -end diff --git a/test/busted/outputHandlers/sound.lua b/test/busted/outputHandlers/sound.lua deleted file mode 100644 index 8ac1a46ce..000000000 --- a/test/busted/outputHandlers/sound.lua +++ /dev/null @@ -1,36 +0,0 @@ -local app = require 'pl.app' -return function(options, busted) - local handler = require 'busted.outputHandlers.base'(busted) - local language = require('busted.languages.' .. options.language) - - handler.suiteEnd = function() - local system = app.platform() - local sayer_pre, sayer_post - local messages - - if system == 'Linux' then - sayer_pre = 'espeak -s 160 ' - sayer_post = ' > /dev/null 2>&1' - elseif system and system:match('^Windows') then - sayer_pre = 'echo ' - sayer_post = ' | ptts' - else - sayer_pre = 'say ' - sayer_post = '' - end - - if handler.failuresCount > 0 then - messages = language.failure_messages - else - messages = language.success_messages - end - - io.popen(sayer_pre .. '"' .. messages[math.random(1, #messages)] .. '"' .. sayer_post) - - return nil, true - end - - busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) - - return handler -end diff --git a/test/busted/outputHandlers/utfTerminal.lua b/test/busted/outputHandlers/utfTerminal.lua deleted file mode 100644 index f34015cc4..000000000 --- a/test/busted/outputHandlers/utfTerminal.lua +++ /dev/null @@ -1,176 +0,0 @@ -local ansicolors = require 'ansicolors' -local s = require 'say' -local pretty = require 'pl.pretty' - -return function(options, busted) - local handler = require 'busted.outputHandlers.base'(busted) - - local successDot = ansicolors('%{green}●') - local failureDot = ansicolors('%{red}◼') - local errorDot = ansicolors('%{magenta}✱') - local pendingDot = ansicolors('%{yellow}◌') - - local pendingDescription = function(pending) - local name = pending.name - - local string = ansicolors('%{yellow}' .. s('output.pending')) .. ' → ' .. - ansicolors('%{cyan}' .. pending.trace.short_src) .. ' @ ' .. - ansicolors('%{cyan}' .. pending.trace.currentline) .. - '\n' .. ansicolors('%{bright}' .. name) - - if type(pending.message) == 'string' then - string = string .. '\n' .. pending.message - elseif pending.message ~= nil then - string = string .. '\n' .. pretty.write(pending.message) - end - - return string - end - - local failureMessage = function(failure) - local string - if type(failure.message) == 'string' then - string = failure.message - elseif failure.message == nil then - string = 'Nil error' - else - string = pretty.write(failure.message) - end - - return string - end - - local failureDescription = function(failure, isError) - local string = ansicolors('%{red}' .. s('output.failure')) .. ' → ' - if isError then - string = ansicolors('%{magenta}' .. s('output.error')) .. ' → ' - end - - if not failure.element.trace or not failure.element.trace.short_src then - string = string .. - ansicolors('%{cyan}' .. failureMessage(failure)) .. '\n' .. - ansicolors('%{bright}' .. failure.name) - else - string = string .. - ansicolors('%{cyan}' .. failure.element.trace.short_src) .. ' @ ' .. - ansicolors('%{cyan}' .. failure.element.trace.currentline) .. '\n' .. - ansicolors('%{bright}' .. failure.name) .. '\n' .. - failureMessage(failure) - end - - if options.verbose and failure.trace and failure.trace.traceback then - string = string .. '\n' .. failure.trace.traceback - end - - return string - end - - local statusString = function() - local successString = s('output.success_plural') - local failureString = s('output.failure_plural') - local pendingString = s('output.pending_plural') - local errorString = s('output.error_plural') - - local ms = handler.getDuration() - local successes = handler.successesCount - local pendings = handler.pendingsCount - local failures = handler.failuresCount - local errors = handler.errorsCount - - if successes == 0 then - successString = s('output.success_zero') - elseif successes == 1 then - successString = s('output.success_single') - end - - if failures == 0 then - failureString = s('output.failure_zero') - elseif failures == 1 then - failureString = s('output.failure_single') - end - - if pendings == 0 then - pendingString = s('output.pending_zero') - elseif pendings == 1 then - pendingString = s('output.pending_single') - end - - if errors == 0 then - errorString = s('output.error_zero') - elseif errors == 1 then - errorString = s('output.error_single') - end - - local formattedTime = ('%.6f'):format(ms):gsub('([0-9])0+$', '%1') - - return ansicolors('%{green}' .. successes) .. ' ' .. successString .. ' / ' .. - ansicolors('%{red}' .. failures) .. ' ' .. failureString .. ' / ' .. - ansicolors('%{magenta}' .. errors) .. ' ' .. errorString .. ' / ' .. - ansicolors('%{yellow}' .. pendings) .. ' ' .. pendingString .. ' : ' .. - ansicolors('%{bright}' .. formattedTime) .. ' ' .. s('output.seconds') - end - - handler.testEnd = function(element, parent, status, debug) - if not options.deferPrint then - local string = successDot - - if status == 'pending' then - string = pendingDot - elseif status == 'failure' then - string = failureDot - elseif status == 'error' then - string = errorDot - end - - io.write(string) - io.flush() - end - - return nil, true - end - - handler.suiteStart = function(count, total) - local runString = (total > 1 and '\nRepeating all tests (run %d of %d) . . .\n\n' or '') - io.write(runString:format(count, total)) - io.flush() - end - - handler.suiteEnd = function(count, total) - print('') - print(statusString()) - - for i, pending in pairs(handler.pendings) do - print('') - print(pendingDescription(pending)) - end - - for i, err in pairs(handler.failures) do - print('') - print(failureDescription(err)) - end - - for i, err in pairs(handler.errors) do - print('') - print(failureDescription(err, true)) - end - - return nil, true - end - - handler.error = function(element, parent, message, debug) - io.write(errorDot) - io.flush() - - return nil, true - end - - busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending }) - busted.subscribe({ 'suite', 'start' }, handler.suiteStart) - busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) - busted.subscribe({ 'error', 'file' }, handler.error) - busted.subscribe({ 'failure', 'file' }, handler.error) - busted.subscribe({ 'error', 'describe' }, handler.error) - busted.subscribe({ 'failure', 'describe' }, handler.error) - - return handler -end diff --git a/test/busted/runner.lua b/test/busted/runner.lua deleted file mode 100644 index 91ce94e50..000000000 --- a/test/busted/runner.lua +++ /dev/null @@ -1,400 +0,0 @@ --- Busted command-line runner - -local path = require 'pl.path' -local term = require 'term' -local utils = require 'busted.utils' -local loaded = false - -return function(options) - if loaded then return else loaded = true end - - local opt = options or {} - local isBatch = opt.batch - local cli = require 'cliargs' - local busted = require 'busted.core'() - - local configLoader = require 'busted.modules.configuration_loader'() - local helperLoader = require 'busted.modules.helper_loader'() - local outputHandlerLoader = require 'busted.modules.output_handler_loader'() - - local luacov = require 'busted.modules.luacov'() - - local osexit = require 'busted.compatibility'.osexit - - require 'busted'(busted) - - -- Default cli arg values - local defaultOutput = term.isatty(io.stdout) and 'utfTerminal' or 'plainTerminal' - local defaultLoaders = 'lua,moonscript' - local defaultPattern = '_spec' - local defaultSeed = 'os.time()' - local lpathprefix = './src/?.lua;./src/?/?.lua;./src/?/init.lua' - local cpathprefix = path.is_windows and './csrc/?.dll;./csrc/?/?.dll;' or './csrc/?.so;./csrc/?/?.so;' - - local level = 2 - local info = debug.getinfo(level, 'Sf') - local source = info.source - local fileName = source:sub(1,1) == '@' and source:sub(2) or source - - local cliArgsParsed = {} - - local function processOption(key, value, altkey, opt) - if altkey then cliArgsParsed[altkey] = value end - cliArgsParsed[key] = value - return true - end - - local function processNumber(key, value, altkey, opt) - local number = tonumber(value) - if not number then - return nil, 'argument to ' .. opt:gsub('=.*', '') .. ' must be a number' - end - if altkey then cliArgsParsed[altkey] = number end - cliArgsParsed[key] = number - return true - end - - local function processVersion() - -- Return early if asked for the version - print(busted.version) - osexit(0, true) - end - - -- Load up the command-line interface options - cli:set_name(path.basename(fileName)) - cli:add_flag('--version', 'prints the program version and exits', processVersion) - - if isBatch then - cli:optarg('ROOT', 'test script file/folder. Folders will be traversed for any file that matches the --pattern option.', 'spec', 1) - - cli:add_option('-p, --pattern=PATTERN', 'only run test files matching the Lua pattern', defaultPattern, processOption) - end - - cli:add_option('-o, --output=LIBRARY', 'output library to load', defaultOutput, processOption) - cli:add_option('-d, --cwd=cwd', 'path to current working directory', './', processOption) - cli:add_option('-t, --tags=TAGS', 'only run tests with these #tags', nil, processOption) - cli:add_option('--exclude-tags=TAGS', 'do not run tests with these #tags, takes precedence over --tags', nil, processOption) - cli:add_option('--filter=PATTERN', 'only run test names matching the Lua pattern', nil, processOption) - cli:add_option('--filter-out=PATTERN', 'do not run test names matching the Lua pattern, takes precedence over --filter', nil, processOption) - cli:add_option('-m, --lpath=PATH', 'optional path to be prefixed to the Lua module search path', lpathprefix, processOption) - cli:add_option('--cpath=PATH', 'optional path to be prefixed to the Lua C module search path', cpathprefix, processOption) - cli:add_option('-r, --run=RUN', 'config to run from .busted file', nil, processOption) - cli:add_option('--repeat=COUNT', 'run the tests repeatedly', '1', processNumber) - cli:add_option('--seed=SEED', 'random seed value to use for shuffling test order', defaultSeed, processNumber) - cli:add_option('--lang=LANG', 'language for error messages', 'en', processOption) - cli:add_option('--loaders=NAME', 'test file loaders', defaultLoaders, processOption) - cli:add_option('--helper=PATH', 'A helper script that is run before tests', nil, processOption) - - cli:add_option('-Xoutput OPTION', 'pass `OPTION` as an option to the output handler. If `OPTION` contains commas, it is split into multiple options at the commas.', nil, processOption) - cli:add_option('-Xhelper OPTION', 'pass `OPTION` as an option to the helper script. If `OPTION` contains commas, it is split into multiple options at the commas.', nil, processOption) - - cli:add_flag('-c, --coverage', 'do code coverage analysis (requires `LuaCov` to be installed)', processOption) - cli:add_flag('-v, --verbose', 'verbose output of errors', processOption) - cli:add_flag('-s, --enable-sound', 'executes `say` command if available', processOption) - cli:add_flag('-l, --list', 'list the names of all tests instead of running them', processOption) - cli:add_flag('--no-keep-going', 'quit after first error or failure', processOption) - cli:add_flag('--no-recursive', 'do not recurse into subdirectories', processOption) - cli:add_flag('--shuffle', 'randomize file and test order, takes precedence over --sort (--shuffle-test and --shuffle-files)', processOption) - cli:add_flag('--shuffle-files', 'randomize file execution order, takes precedence over --sort-files', processOption) - cli:add_flag('--shuffle-tests', 'randomize test order within a file, takes precedence over --sort-tests', processOption) - cli:add_flag('--sort', 'sort file and test order (--sort-tests and --sort-files)', processOption) - cli:add_flag('--sort-files', 'sort file execution order', processOption) - cli:add_flag('--sort-tests', 'sort test order within a file', processOption) - cli:add_flag('--suppress-pending', 'suppress `pending` test output', processOption) - cli:add_flag('--defer-print', 'defer print to when test suite is complete', processOption) - - -- Parse the cli arguments - local cliArgs = cli:parse(arg) - if not cliArgs then - osexit(1, true) - end - - -- Load current working directory - local fpath = utils.normpath(cliArgs.cwd) - - -- Load busted config file if available - local configFile = { } - local bustedConfigFilePath = utils.normpath(path.join(fpath, '.busted')) - local bustedConfigFile = pcall(function() configFile = loadfile(bustedConfigFilePath)() end) - if bustedConfigFile then - local config, err = configLoader(configFile, cliArgsParsed, cliArgs) - if err then - print('Error: ' .. err) - osexit(1, true) - else - cliArgs = config - end - end - - local tags = {} - local excludeTags = {} - - if cliArgs.tags and cliArgs.tags ~= '' then - tags = utils.split(cliArgs.tags, ',') - end - - if cliArgs['exclude-tags'] and cliArgs['exclude-tags'] ~= '' then - excludeTags = utils.split(cliArgs['exclude-tags'], ',') - end - - -- If coverage arg is passed in, load LuaCovsupport - if cliArgs.coverage then - luacov() - end - - -- Add additional package paths based on lpath and cpath cliArgs - if #cliArgs.lpath > 0 then - lpathprefix = cliArgs.lpath - lpathprefix = lpathprefix:gsub('^%.([/%\\])', fpath .. '%1') - lpathprefix = lpathprefix:gsub(';%.([/%\\])', ';' .. fpath .. '%1') - package.path = (lpathprefix .. ';' .. package.path):gsub(';;',';') - end - - if #cliArgs.cpath > 0 then - cpathprefix = cliArgs.cpath - cpathprefix = cpathprefix:gsub('^%.([/%\\])', fpath .. '%1') - cpathprefix = cpathprefix:gsub(';%.([/%\\])', ';' .. fpath .. '%1') - package.cpath = (cpathprefix .. ';' .. package.cpath):gsub(';;',';') - end - - local loaders = {} - if #cliArgs.loaders > 0 then - string.gsub(cliArgs.loaders, '([^,]+)', function(c) loaders[#loaders+1] = c end) - end - - -- We report an error if the same tag appears in both `options.tags` - -- and `options.excluded_tags` because it does not make sense for the - -- user to tell Busted to include and exclude the same tests at the - -- same time. - for _, excluded in pairs(excludeTags) do - for _, included in pairs(tags) do - if excluded == included then - print('Error: Cannot use --tags and --exclude-tags for the same tags') - osexit(1, true) - end - end - end - - -- watch for test errors - local failures = 0 - local errors = 0 - local quitOnError = cliArgs['no-keep-going'] - - busted.subscribe({ 'error', 'output' }, function(element, parent, message) - print('Error: Cannot load output library: ' .. element.name .. '\n' .. message) - return nil, true - end) - - busted.subscribe({ 'error', 'helper' }, function(element, parent, message) - print('Error: Cannot load helper script: ' .. element.name .. '\n' .. message) - return nil, true - end) - - busted.subscribe({ 'error' }, function(element, parent, message) - errors = errors + 1 - busted.skipAll = quitOnError - return nil, true - end) - - busted.subscribe({ 'failure' }, function(element, parent, message) - if element.descriptor == 'it' then - failures = failures + 1 - else - errors = errors + 1 - end - busted.skipAll = quitOnError - return nil, true - end) - - -- Set up output handler to listen to events - local outputHandlerOptions = { - verbose = cliArgs.verbose, - suppressPending = cliArgs['suppress-pending'], - language = cliArgs.lang, - deferPrint = cliArgs['defer-print'], - arguments = utils.split(cliArgs.Xoutput or '', ',') or {} - } - - local opath = utils.normpath(path.join(fpath, cliArgs.output)) - local outputHandler = outputHandlerLoader(cliArgs.output, opath, outputHandlerOptions, busted, defaultOutput) - outputHandler:subscribe(outputHandlerOptions) - - if cliArgs['enable-sound'] then - require 'busted.outputHandlers.sound'(outputHandlerOptions, busted) - end - - -- Set up randomization options - busted.sort = cliArgs['sort-tests'] or cliArgs.sort - busted.randomize = cliArgs['shuffle-tests'] or cliArgs.shuffle - busted.randomseed = tonumber(cliArgs.seed) or os.time() - - local getFullName = function(name) - local parent = busted.context.get() - local names = { name } - - while parent and (parent.name or parent.descriptor) and - parent.descriptor ~= 'file' do - table.insert(names, 1, parent.name or parent.descriptor) - parent = busted.context.parent(parent) - end - - return table.concat(names, ' ') - end - - local hasTag = function(name, tag) - local found = name:find('#' .. tag) - return (found ~= nil) - end - - local filterExcludeTags = function(name) - for i, tag in pairs(excludeTags) do - if hasTag(name, tag) then - return nil, false - end - end - return nil, true - end - - local filterTags = function(name) - local fullname = getFullName(name) - for i, tag in pairs(tags) do - if hasTag(fullname, tag) then - return nil, true - end - end - return nil, (#tags == 0) - end - - local filterOutNames = function(name) - local found = (getFullName(name):find(cliArgs['filter-out']) ~= nil) - return nil, not found - end - - local filterNames = function(name) - local found = (getFullName(name):find(cliArgs.filter) ~= nil) - return nil, found - end - - local printNameOnly = function(name, fn, trace) - local fullname = getFullName(name) - if trace and trace.what == 'Lua' then - print(trace.short_src .. ':' .. trace.currentline .. ': ' .. fullname) - else - print(fullname) - end - return nil, false - end - - local ignoreAll = function() - return nil, false - end - - local skipOnError = function() - return nil, (failures == 0 and errors == 0) - end - - local applyFilter = function(descriptors, name, fn) - if cliArgs[name] and cliArgs[name] ~= '' then - for _, descriptor in ipairs(descriptors) do - busted.subscribe({ 'register', descriptor }, fn, { priority = 1 }) - end - end - end - - if cliArgs.list then - busted.subscribe({ 'suite', 'start' }, ignoreAll, { priority = 1 }) - busted.subscribe({ 'suite', 'end' }, ignoreAll, { priority = 1 }) - applyFilter({ 'setup', 'teardown', 'before_each', 'after_each' }, 'list', ignoreAll) - applyFilter({ 'it', 'pending' }, 'list', printNameOnly) - end - - applyFilter({ 'setup', 'teardown', 'before_each', 'after_each' }, 'no-keep-going', skipOnError) - applyFilter({ 'file', 'describe', 'it', 'pending' }, 'no-keep-going', skipOnError) - - -- The following filters are applied in reverse order - applyFilter({ 'it', 'pending' } , 'filter' , filterNames ) - applyFilter({ 'describe', 'it', 'pending' }, 'filter-out' , filterOutNames ) - applyFilter({ 'it', 'pending' } , 'tags' , filterTags ) - applyFilter({ 'describe', 'it', 'pending' }, 'exclude-tags', filterExcludeTags) - - -- Set up helper script - if cliArgs.helper and cliArgs.helper ~= '' then - local helperOptions = { - verbose = cliArgs.verbose, - language = cliArgs.lang, - arguments = utils.split(cliArgs.Xhelper or '', ',') or {} - } - - local hpath = utils.normpath(path.join(fpath, cliArgs.helper)) - helperLoader(cliArgs.helper, hpath, helperOptions, busted) - end - - -- Set up test loader options - local testFileLoaderOptions = { - verbose = cliArgs.verbose, - sort = cliArgs['sort-files'] or cliArgs.sort, - shuffle = cliArgs['shuffle-files'] or cliArgs.shuffle, - recursive = not cliArgs['no-recursive'], - seed = busted.randomseed - } - - -- Load test directory - local rootFile = cliArgs.ROOT and utils.normpath(path.join(fpath, cliArgs.ROOT)) or fileName - local pattern = cliArgs.pattern - local testFileLoader = require 'busted.modules.test_file_loader'(busted, loaders, testFileLoaderOptions) - local fileList = testFileLoader(rootFile, pattern) - - if not cliArgs.ROOT then - local ctx = busted.context.get() - local file = busted.context.children(ctx)[1] - getmetatable(file.run).__call = info.func - end - - busted.subscribe({'suite', 'reinitialize'}, function() - local oldctx = busted.context.get() - local children = busted.context.children(oldctx) - - busted.context.clear() - local ctx = busted.context.get() - for k, v in pairs(oldctx) do - ctx[k] = v - end - - for _, child in pairs(children) do - for descriptor, _ in pairs(busted.executors) do - child[descriptor] = nil - end - busted.context.attach(child) - end - - busted.randomseed = tonumber(cliArgs.seed) or os.time() - - return nil, true - end) - - local runs = tonumber(cliArgs['repeat']) or 1 - for i = 1, runs do - if i > 1 then - busted.publish({ 'suite', 'reinitialize' }) - end - - busted.publish({ 'suite', 'start' }, i, runs) - busted.execute() - busted.publish({ 'suite', 'end' }, i, runs) - - if quitOnError and (failures > 0 or errors > 0) then - break - end - end - - busted.publish({ 'exit' }) - - local exit = 0 - if failures > 0 or errors > 0 then - exit = failures + errors - if exit > 255 then - exit = 255 - end - end - osexit(exit, true) -end diff --git a/test/busted/status.lua b/test/busted/status.lua deleted file mode 100644 index c68ce7fc7..000000000 --- a/test/busted/status.lua +++ /dev/null @@ -1,43 +0,0 @@ -local function get_status(status) - local smap = { - ['success'] = 'success', - ['pending'] = 'pending', - ['failure'] = 'failure', - ['error'] = 'error', - ['true'] = 'success', - ['false'] = 'failure', - ['nil'] = 'error', - } - return smap[tostring(status)] or 'error' -end - -return function(inital_status) - local objstat = get_status(inital_status) - local obj = { - success = function(self) return (objstat == 'success') end, - pending = function(self) return (objstat == 'pending') end, - failure = function(self) return (objstat == 'failure') end, - error = function(self) return (objstat == 'error') end, - - get = function(self) - return objstat - end, - - set = function(self, status) - objstat = get_status(status) - end, - - update = function(self, status) - -- prefer current failure/error status over new status - status = get_status(status) - if objstat == 'success' or (objstat == 'pending' and status ~= 'success') then - objstat = status - end - end - } - - return setmetatable(obj, { - __index = {}, - __tostring = function(self) return objstat end - }) -end diff --git a/test/busted/utils.lua b/test/busted/utils.lua deleted file mode 100644 index 4e02bc036..000000000 --- a/test/busted/utils.lua +++ /dev/null @@ -1,53 +0,0 @@ -local path = require 'pl.path' - -math.randomseed(os.time()) - --- Do not use pl.path.normpath --- It is broken for paths with leading '../../' -local function normpath(fpath) - if type(fpath) ~= 'string' then - error(fpath .. ' is not a string') - end - local sep = '/' - if path.is_windows then - sep = '\\' - if fpath:match '^\\\\' then -- UNC - return '\\\\' .. normpath(fpath:sub(3)) - end - fpath = fpath:gsub('/','\\') - end - local np_gen1, np_gen2 = '([^SEP]+)SEP(%.%.SEP?)', 'SEP+%.?SEP' - local np_pat1 = np_gen1:gsub('SEP', sep) - local np_pat2 = np_gen2:gsub('SEP', sep) - local k - repeat -- /./ -> / - fpath, k = fpath:gsub(np_pat2, sep) - until k == 0 - repeat -- A/../ -> (empty) - local oldpath = fpath - fpath, k = fpath:gsub(np_pat1, function(d, up) - if d == '..' then return nil end - if d == '.' then return up end - return '' - end) - until k == 0 or oldpath == fpath - if fpath == '' then fpath = '.' end - return fpath -end - -return { - split = require 'pl.utils'.split, - - normpath = normpath, - - shuffle = function(t, seed) - if seed then math.randomseed(seed) end - local n = #t - while n >= 2 do - local k = math.random(n) - t[n], t[k] = t[k], t[n] - n = n - 1 - end - return t - end -} diff --git a/test/lua/busted.lua b/test/lua/busted.lua new file mode 100644 index 000000000..917d21d82 --- /dev/null +++ b/test/lua/busted.lua @@ -0,0 +1,3 @@ +-- This is a dummy file so it can be used in busted's specs +-- without adding ./?/init.lua to the lua path +return require 'busted.init' diff --git a/test/lua/busted/compatibility.lua b/test/lua/busted/compatibility.lua new file mode 100644 index 000000000..7837656fb --- /dev/null +++ b/test/lua/busted/compatibility.lua @@ -0,0 +1,60 @@ +return { + getfenv = getfenv or function(f) + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name, value + local up = 0 + + repeat + up = up + 1 + name, value = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + + return value + end, + + setfenv = setfenv or function(f, t) + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name + local up = 0 + + repeat + up = up + 1 + name = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + + if name then + debug.upvaluejoin(f, up, function() return name end, 1) + debug.setupvalue(f, up, t) + end + + if f ~= 0 then return f end + end, + + unpack = table.unpack or unpack, + + osexit = function(code, close) + if close and _VERSION == 'Lua 5.1' then + -- From Lua 5.1 manual: + -- > The userdata itself is freed only in the next + -- > garbage-collection cycle. + -- Call collectgarbage() while collectgarbage('count') + -- changes + 3 times, at least 3 times, + -- at max 100 times (to prevent infinite loop). + local times_const = 0 + for i = 1, 100 do + local count_before = collectgarbage("count") + collectgarbage() + local count_after = collectgarbage("count") + if count_after == count_before then + times_const = times_const + 1 + if times_const > 3 then + break + end + else + times_const = 0 + end + end + end + os.exit(code, close) + end, +} diff --git a/test/lua/busted/context.lua b/test/lua/busted/context.lua new file mode 100644 index 000000000..4cf0f3b53 --- /dev/null +++ b/test/lua/busted/context.lua @@ -0,0 +1,91 @@ +local tablex = require 'pl.tablex' + +local function save() + local g = {} + for k,_ in next, _G, nil do + g[k] = rawget(_G, k) + end + return { + gmt = getmetatable(_G), + g = g, + loaded = tablex.copy(package.loaded) + } +end + +local function restore(state) + setmetatable(_G, state.gmt) + for k,_ in next, _G, nil do + rawset(_G, k, state.g[k]) + end + for k,_ in pairs(package.loaded) do + package.loaded[k] = state.loaded[k] + end +end + +return function() + local context = {} + + local data = {} + local parents = {} + local children = {} + local stack = {} + + function context.ref() + local ref = {} + local ctx = data + + function ref.get(key) + if not key then return ctx end + return ctx[key] + end + + function ref.set(key, value) + ctx[key] = value + end + + function ref.clear() + data = {} + parents = {} + children = {} + stack = {} + ctx = data + end + + function ref.attach(child) + if not children[ctx] then children[ctx] = {} end + parents[child] = ctx + table.insert(children[ctx], child) + end + + function ref.children(parent) + return children[parent] or {} + end + + function ref.parent(child) + return parents[child] + end + + function ref.push(current) + if not parents[current] then error('Detached child. Cannot push.') end + if ctx ~= current and current.descriptor == 'file' then + current.state = save() + end + table.insert(stack, ctx) + ctx = current + end + + function ref.pop() + local current = ctx + ctx = table.remove(stack) + if ctx ~= current and current.state then + restore(current.state) + current.state = nil + end + if not ctx then error('Context stack empty. Cannot pop.') end + end + + return ref + end + + return context +end diff --git a/test/lua/busted/core.lua b/test/lua/busted/core.lua new file mode 100644 index 000000000..802e60c80 --- /dev/null +++ b/test/lua/busted/core.lua @@ -0,0 +1,237 @@ +local getfenv = require 'busted.compatibility'.getfenv +local setfenv = require 'busted.compatibility'.setfenv +local unpack = require 'busted.compatibility'.unpack +local path = require 'pl.path' +local pretty = require 'pl.pretty' +local throw = error + +local failureMt = { + __index = {}, + __tostring = function(e) return tostring(e.message) end, + __type = 'failure' +} + +local failureMtNoString = { + __index = {}, + __type = 'failure' +} + +local pendingMt = { + __index = {}, + __tostring = function(p) return p.message end, + __type = 'pending' +} + +local function metatype(obj) + local otype = type(obj) + return otype == 'table' and (getmetatable(obj) or {}).__type or otype +end + +local function hasToString(obj) + return type(obj) == 'string' or (getmetatable(obj) or {}).__tostring +end + +return function() + local mediator = require 'mediator'() + + local busted = {} + busted.version = '2.0.rc6-0' + + local root = require 'busted.context'() + busted.context = root.ref() + + local environment = require 'busted.environment'(busted.context) + busted.environment = { + set = environment.set, + + wrap = function(callable) + if (type(callable) == 'function' or getmetatable(callable).__call) then + -- prioritize __call if it exists, like in files + environment.wrap((getmetatable(callable) or {}).__call or callable) + end + end + } + + busted.executors = {} + local executors = {} + + busted.status = require 'busted.status' + + function busted.getTrace(element, level, msg) + level = level or 3 + + local thisdir = path.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 == path.dirname(info.source)) do + level = level + 1 + info = debug.getinfo(level, 'Sl') + end + + info.traceback = debug.traceback('', level) + info.message = msg + + local file = busted.getFile(element) + return file.getTrace(file.name, info) + end + + function busted.rewriteMessage(element, message, trace) + local file = busted.getFile(element) + local msg = hasToString(message) and tostring(message) + msg = msg or (message ~= nil and pretty.write(message) or 'Nil error') + msg = (file.rewriteMessage and file.rewriteMessage(file.name, msg) or msg) + + local hasFileLine = msg:match('^[^\n]-:%d+: .*') + if not hasFileLine then + local trace = trace or busted.getTrace(element, 3, message) + local fileline = trace.short_src .. ':' .. trace.currentline .. ': ' + msg = fileline .. msg + end + + return msg + end + + function busted.publish(...) + return mediator:publish(...) + end + + function busted.subscribe(...) + return mediator:subscribe(...) + end + + function busted.getFile(element) + local parent = busted.context.parent(element) + + while parent do + if parent.file then + local file = parent.file[1] + return { + name = file.name, + getTrace = file.run.getTrace, + rewriteMessage = file.run.rewriteMessage + } + end + + if parent.descriptor == 'file' then + return { + name = parent.name, + getTrace = parent.run.getTrace, + rewriteMessage = parent.run.rewriteMessage + } + end + + parent = busted.context.parent(parent) + end + + return parent + end + + function busted.fail(msg, level) + local rawlevel = (type(level) ~= 'number' or level <= 0) and level + local level = level or 1 + local _, emsg = pcall(throw, msg, rawlevel or level+2) + local e = { message = emsg } + setmetatable(e, hasToString(msg) and failureMt or failureMtNoString) + throw(e, rawlevel or level+1) + end + + function busted.pending(msg) + local p = { message = msg } + setmetatable(p, pendingMt) + throw(p) + end + + function busted.replaceErrorWithFail(callable) + local env = {} + local f = (getmetatable(callable) or {}).__call or callable + setmetatable(env, { __index = getfenv(f) }) + env.error = busted.fail + setfenv(f, env) + end + + function busted.safe(descriptor, run, element) + busted.context.push(element) + local trace, message + local status = 'success' + + local ret = { xpcall(run, function(msg) + local errType = metatype(msg) + status = ((errType == 'pending' or errType == 'failure') and errType or 'error') + trace = busted.getTrace(element, 3, msg) + message = busted.rewriteMessage(element, msg, trace) + end) } + + if not ret[1] then + busted.publish({ status, descriptor }, element, busted.context.parent(element), message, trace) + end + ret[1] = busted.status(status) + + busted.context.pop() + return unpack(ret) + end + + function busted.register(descriptor, executor) + executors[descriptor] = executor + + local publisher = function(name, fn) + if not fn and type(name) == 'function' then + fn = name + name = nil + end + + local trace + + local ctx = busted.context.get() + if busted.context.parent(ctx) then + trace = busted.getTrace(ctx, 3, name) + end + + local publish = function(f) + busted.publish({ 'register', descriptor }, name, f, trace) + end + + if fn then publish(fn) else return publish end + end + + busted.executors[descriptor] = publisher + if descriptor ~= 'file' then + environment.set(descriptor, publisher) + end + + busted.subscribe({ 'register', descriptor }, function(name, fn, trace) + local ctx = busted.context.get() + local plugin = { + descriptor = descriptor, + name = name, + run = fn, + trace = trace + } + + busted.context.attach(plugin) + + if not ctx[descriptor] then + ctx[descriptor] = { plugin } + else + ctx[descriptor][#ctx[descriptor]+1] = plugin + end + end) + end + + function busted.alias(alias, descriptor) + local publisher = busted.executors[descriptor] + busted.executors[alias] = publisher + environment.set(alias, publisher) + end + + function busted.execute(current) + if not current then current = busted.context.get() end + for _, v in pairs(busted.context.children(current)) do + local executor = executors[v.descriptor] + if executor and not busted.skipAll then + busted.safe(v.descriptor, function() executor(v) end, v) + end + end + end + + return busted +end diff --git a/test/lua/busted/done.lua b/test/lua/busted/done.lua new file mode 100644 index 000000000..e6319044d --- /dev/null +++ b/test/lua/busted/done.lua @@ -0,0 +1,121 @@ +local M = {} + +-- adds tokens to the current wait list, does not change order/unordered +M.wait = function(self, ...) + local tlist = { ... } + + for _, token in ipairs(tlist) do + if type(token) ~= 'string' then + error('Wait tokens must be strings. Got '..type(token), 2) + end + table.insert(self.tokens, token) + end +end + +-- set list as unordered, adds tokens to current wait list +M.wait_unordered = function(self, ...) + self.ordered = false + self:wait(...) +end + +-- set list as ordered, adds tokens to current wait list +M.wait_ordered = function(self, ...) + self.ordered = true + self:wait(...) +end + +-- generates a message listing tokens received/open +M.tokenlist = function(self) + local list + + if #self.tokens_done == 0 then + list = 'No tokens received.' + else + list = 'Tokens received ('..tostring(#self.tokens_done)..')' + local s = ': ' + + for _,t in ipairs(self.tokens_done) do + list = list .. s .. '\''..t..'\'' + s = ', ' + end + + list = list .. '.' + end + + if #self.tokens == 0 then + list = list .. ' No more tokens expected.' + else + list = list .. ' Tokens not received ('..tostring(#self.tokens)..')' + local s = ': ' + + for _, t in ipairs(self.tokens) do + list = list .. s .. '\''..t..'\'' + s = ', ' + end + + list = list .. '.' + end + + return list +end + +-- marks a token as completed, checks for ordered/unordered, checks for completeness +M.done = function(self, ...) self:_done(...) end -- extra wrapper for same error level constant as __call method +M._done = function(self, token) + if token then + if type(token) ~= 'string' then + error('Wait tokens must be strings. Got '..type(token), 3) + end + + if self.ordered then + if self.tokens[1] == token then + table.remove(self.tokens, 1) + table.insert(self.tokens_done, token) + else + if self.tokens[1] then + error(('Bad token, expected \'%s\' got \'%s\'. %s'):format(self.tokens[1], token, self:tokenlist()), 3) + else + error(('Bad token (no more tokens expected) got \'%s\'. %s'):format(token, self:tokenlist()), 3) + end + end + else + -- unordered + for i, t in ipairs(self.tokens) do + if t == token then + table.remove(self.tokens, i) + table.insert(self.tokens_done, token) + token = nil + break + end + end + + if token then + error(('Unknown token \'%s\'. %s'):format(token, self:tokenlist()), 3) + end + end + end + if not next(self.tokens) then + -- no more tokens, so we're really done... + self.done_cb() + end +end + + +-- wraps a done callback into a done-object supporting tokens to sign-off +M.new = function(done_callback) + local obj = { + tokens = {}, + tokens_done = {}, + done_cb = done_callback, + ordered = true, -- default for sign off of tokens + } + + return setmetatable( obj, { + __call = function(self, ...) + self:_done(...) + end, + __index = M, + }) +end + +return M diff --git a/test/lua/busted/environment.lua b/test/lua/busted/environment.lua new file mode 100644 index 000000000..686b1388f --- /dev/null +++ b/test/lua/busted/environment.lua @@ -0,0 +1,45 @@ +local setfenv = require 'busted.compatibility'.setfenv + +return function(context) + + local environment = {} + + local function getEnv(self, key) + if not self then return nil end + return + self.env and self.env[key] or + getEnv(context.parent(self), key) or + _G[key] + end + + local function setEnv(self, key, value) + if not self.env then self.env = {} end + self.env[key] = value + end + + local function __index(self, key) + return getEnv(context.get(), key) + end + + local function __newindex(self, key, value) + setEnv(context.get(), key, value) + end + + local env = setmetatable({}, { __index=__index, __newindex=__newindex }) + + function environment.wrap(fn) + return setfenv(fn, env) + end + + function environment.set(key, value) + local env = context.get('env') + + if not env then + env = {} + context.set('env', env) + end + + env[key] = value + end + return environment +end diff --git a/test/lua/busted/init.lua b/test/lua/busted/init.lua new file mode 100644 index 000000000..da052f503 --- /dev/null +++ b/test/lua/busted/init.lua @@ -0,0 +1,227 @@ +local unpack = require 'busted.compatibility'.unpack +local shuffle = require 'busted.utils'.shuffle + +local function sort(elements) + table.sort(elements, function(t1, t2) + if t1.name and t2.name then + return t1.name < t2.name + end + return t2.name ~= nil + end) + return elements +end + +local function remove(descriptors, element) + for _, descriptor in ipairs(descriptors) do + element.env[descriptor] = function(...) + error("'" .. descriptor .. "' not supported inside current context block", 2) + end + end +end + +local function init(busted) + local function exec(descriptor, element) + if not element.env then element.env = {} end + + remove({ 'randomize' }, element) + remove({ 'pending' }, element) + remove({ 'describe', 'context', 'it', 'spec', 'test' }, element) + remove({ 'setup', 'teardown', 'before_each', 'after_each' }, element) + + local parent = busted.context.parent(element) + setmetatable(element.env, { + __newindex = function(self, key, value) + if not parent.env then parent.env = {} end + parent.env[key] = value + end + }) + + local ret = { busted.safe(descriptor, element.run, element) } + return unpack(ret) + end + + local function execAll(descriptor, current, propagate) + local parent = busted.context.parent(current) + + if propagate and parent then + local success, ancestor = execAll(descriptor, parent, propagate) + if not success then + return success, ancestor + end + end + + local list = current[descriptor] or {} + + local success = true + for _, v in pairs(list) do + if not exec(descriptor, v):success() then + success = nil + end + end + return success, current + end + + local function dexecAll(descriptor, current, propagate) + local parent = busted.context.parent(current) + local list = current[descriptor] or {} + + local success = true + for _, v in pairs(list) do + if not exec(descriptor, v):success() then + success = nil + end + end + + if propagate and parent then + if not dexecAll(descriptor, parent, propagate) then + success = nil + end + end + return success + end + + local file = function(file) + busted.publish({ 'file', 'start' }, file.name) + + busted.environment.wrap(file.run) + if not file.env then file.env = {} end + + local randomize = busted.randomize + file.env.randomize = function() randomize = true end + + if busted.safe('file', file.run, file):success() then + if randomize then + file.randomseed = busted.randomseed + shuffle(busted.context.children(file), busted.randomseed) + elseif busted.sort then + sort(busted.context.children(file)) + end + if execAll('setup', file) then + busted.execute(file) + end + dexecAll('teardown', file) + end + + busted.publish({ 'file', 'end' }, file.name) + end + + local describe = function(describe) + local parent = busted.context.parent(describe) + + busted.publish({ 'describe', 'start' }, describe, parent) + + if not describe.env then describe.env = {} end + + local randomize = busted.randomize + describe.env.randomize = function() randomize = true end + + if busted.safe('describe', describe.run, describe):success() then + if randomize then + describe.randomseed = busted.randomseed + shuffle(busted.context.children(describe), busted.randomseed) + elseif busted.sort then + sort(busted.context.children(describe)) + end + if execAll('setup', describe) then + busted.execute(describe) + end + dexecAll('teardown', describe) + end + + busted.publish({ 'describe', 'end' }, describe, parent) + end + + local it = function(element) + local parent = busted.context.parent(element) + local finally + + busted.publish({ 'test', 'start' }, element, parent) + + if not element.env then element.env = {} end + + remove({ 'randomize' }, element) + remove({ 'describe', 'context', 'it', 'spec', 'test' }, element) + remove({ 'setup', 'teardown', 'before_each', 'after_each' }, element) + element.env.finally = function(fn) finally = fn end + element.env.pending = function(msg) busted.pending(msg) end + + local status = busted.status('success') + local updateErrorStatus = function(descriptor) + if element.message then element.message = element.message .. '\n' end + element.message = (element.message or '') .. 'Error in ' .. descriptor + status:update('error') + end + + local pass, ancestor = execAll('before_each', parent, true) + if pass then + status:update(busted.safe('it', element.run, element)) + else + updateErrorStatus('before_each') + end + + if not element.env.done then + remove({ 'pending' }, element) + if finally then status:update(busted.safe('finally', finally, element)) end + if not dexecAll('after_each', ancestor, true) then + updateErrorStatus('after_each') + end + + busted.publish({ 'test', 'end' }, element, parent, tostring(status)) + end + end + + local pending = function(element) + local parent = busted.context.parent(element) + busted.publish({ 'test', 'start' }, element, parent) + busted.publish({ 'test', 'end' }, element, parent, 'pending') + end + + busted.register('file', file) + + busted.register('describe', describe) + + busted.register('it', it) + + busted.register('pending', pending) + + busted.register('setup') + busted.register('teardown') + busted.register('before_each') + busted.register('after_each') + + busted.alias('context', 'describe') + busted.alias('spec', 'it') + busted.alias('test', 'it') + + local assert = require 'luassert' + local spy = require 'luassert.spy' + local mock = require 'luassert.mock' + local stub = require 'luassert.stub' + + busted.environment.set('assert', assert) + busted.environment.set('spy', spy) + busted.environment.set('mock', mock) + busted.environment.set('stub', stub) + + busted.replaceErrorWithFail(assert) + busted.replaceErrorWithFail(assert.True) + + return busted +end + +return setmetatable({}, { + __call = function(self, busted) + local root = busted.context.get() + init(busted) + + return setmetatable(self, { + __index = function(self, key) + return rawget(root.env, key) or busted.executors[key] + end, + + __newindex = function(self, key, value) + error('Attempt to modify busted') + end + }) + end +}) diff --git a/test/lua/busted/languages/ar.lua b/test/lua/busted/languages/ar.lua new file mode 100644 index 000000000..d0b96e65c --- /dev/null +++ b/test/lua/busted/languages/ar.lua @@ -0,0 +1,42 @@ +local s = require('say') + +s:set_namespace('ar') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', 'عالِق') +s:set('output.failure', 'فَشَل') +s:set('output.failure', 'نَجاح') + +s:set('output.pending_plural', 'عالِق') +s:set('output.failure_plural', 'إخْفاقات') +s:set('output.success_plural', 'نَجاحات') + +s:set('output.pending_zero', 'عالِق') +s:set('output.failure_zero', 'إخْفاقات') +s:set('output.success_zero', 'نَجاحات') + +s:set('output.pending_single', 'عالِق') +s:set('output.failure_single', 'فَشَل') +s:set('output.success_single', 'نَجاح') + +s:set('output.seconds', 'ثَوانٍ') + +-- definitions following are not used within the 'say' namespace +return { + failure_messages = { + 'فَشِلَت %d مِنْ الإِختِبارات', + 'فَشِلَت إخْتِباراتُك', + 'برمجيَّتُكَ ضَعيْفة، أنْصَحُكَ بالتَّقاعُد', + 'تقع برمجيَّتُكَ في مَنطِقَةِ الخَطَر', + 'أقترِحُ ألّا تَتَقَدَّم بالإختِبار، علَّ يبْقى الطابِقُ مَستوراَ', + 'جَدَّتي، فِي أَثْناءِ نَومِها، تَكتبُ بَرمَجياتٍ أفْضلُ مِن هذه', + 'يَوَدُّ ليْ مُساعَدَتُكْ، لَكِنّْ...' + }, + success_messages = { + 'رائِع! تَمَّ إجْتِيازُ جَميعُ الإختِباراتِ بِنَجاحٍ', + 'قُل ما شِئت، لا أكتَرِث: busted شَهِدَ لي!', + 'حَقَّ عَليْكَ الإفتِخار', + 'نَجاحٌ مُبْهِر!', + 'عَليكَ بالإحتِفال؛ نَجَحَت جَميعُ التَجارُب' + } +} diff --git a/test/lua/busted/languages/de.lua b/test/lua/busted/languages/de.lua new file mode 100644 index 000000000..e624a465e --- /dev/null +++ b/test/lua/busted/languages/de.lua @@ -0,0 +1,42 @@ +local s = require('say') + +s:set_namespace('de') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', 'Noch nicht erledigt') +s:set('output.failure', 'Fehlgeschlagen') +s:set('output.success', 'Erfolgreich') + +s:set('output.pending_plural', 'übersprungen') +s:set('output.failure_plural', 'fehlgeschlagen') +s:set('output.success_plural', 'erfolgreich') + +s:set('output.pending_zero', 'übersprungen') +s:set('output.failure_zero', 'fehlgeschlagen') +s:set('output.success_zero', 'erfolgreich') + +s:set('output.pending_single', 'übersprungen') +s:set('output.failure_single', 'fehlgeschlagen') +s:set('output.success_single', 'erfolgreich') + +s:set('output.seconds', 'Sekunden') + +-- definitions following are not used within the 'say' namespace +return { + failure_messages = { + 'Du hast %d kaputte Tests.', + 'Deine Tests sind kaputt.', + 'Dein Code ist schlecht; du solltest dich schlecht fühlen.', + 'Dein Code befindet sich in der Gefahrenzone.', + 'Ein seltsames Spiel. Der einzig gewinnbringende Zug ist nicht zu testen.', + 'Meine Großmutter hat auf einem 386er bessere Tests geschrieben.', + 'Immer wenn ein Test fehlschlägt, stirbt ein kleines Kätzchen.', + 'Das fühlt sich schlecht an, oder?' + }, + success_messages = { + 'Yeah, die Tests laufen durch.', + 'Fühlt sich gut an, oder?', + 'Großartig!', + 'Tests sind durchgelaufen, Zeit für ein Bier.', + } +} diff --git a/test/lua/busted/languages/en.lua b/test/lua/busted/languages/en.lua new file mode 100644 index 000000000..285d1ba14 --- /dev/null +++ b/test/lua/busted/languages/en.lua @@ -0,0 +1,49 @@ +local s = require('say') + +s:set_namespace('en') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', 'Pending') +s:set('output.failure', 'Failure') +s:set('output.error', 'Error') +s:set('output.success', 'Success') + +s:set('output.pending_plural', 'pending') +s:set('output.failure_plural', 'failures') +s:set('output.error_plural', 'errors') +s:set('output.success_plural', 'successes') + +s:set('output.pending_zero', 'pending') +s:set('output.failure_zero', 'failures') +s:set('output.error_zero', 'errors') +s:set('output.success_zero', 'successes') + +s:set('output.pending_single', 'pending') +s:set('output.failure_single', 'failure') +s:set('output.error_single', 'error') +s:set('output.success_single', 'success') + +s:set('output.seconds', 'seconds') + +s:set('output.no_test_files_match', 'No test files found matching Lua pattern: %s') + +-- definitions following are not used within the 'say' namespace +return { + failure_messages = { + 'You have %d busted specs', + 'Your specs are busted', + 'Your code is bad and you should feel bad', + 'Your code is in the Danger Zone', + 'Strange game. The only way to win is not to test', + 'My grandmother wrote better specs on a 3 86', + 'Every time there\'s a failure, drink another beer', + 'Feels bad man' + }, + success_messages = { + 'Aww yeah, passing specs', + 'Doesn\'t matter, had specs', + 'Feels good, man', + 'Great success', + 'Tests pass, drink another beer', + } +} diff --git a/test/lua/busted/languages/fr.lua b/test/lua/busted/languages/fr.lua new file mode 100644 index 000000000..236d87c89 --- /dev/null +++ b/test/lua/busted/languages/fr.lua @@ -0,0 +1,45 @@ +local s = require('say') + +s:set_namespace('fr') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', 'En attente') +s:set('output.failure', 'Echec') +s:set('output.success', 'Reussite') + +s:set('output.pending_plural', 'en attente') +s:set('output.failure_plural', 'echecs') +s:set('output.success_plural', 'reussites') + +s:set('output.pending_zero', 'en attente') +s:set('output.failure_zero', 'echec') +s:set('output.success_zero', 'reussite') + +s:set('output.pending_single', 'en attente') +s:set('output.failure_single', 'echec') +s:set('output.success_single', 'reussite') + +s:set('output.seconds', 'secondes') + +s:set('output.no_test_files_match', 'Aucun test n\'est pourrait trouvé qui corresponde au motif de Lua: %s') + +-- definitions following are not used within the 'say' namespace +return { + failure_messages = { + 'Vous avez %d test(s) qui a/ont echoue(s)', + 'Vos tests ont echoue.', + 'Votre code source est mauvais et vous devrez vous sentir mal', + 'Vous avez un code source de Destruction Massive', + 'Jeu plutot etrange game. Le seul moyen de gagner est de ne pas l\'essayer', + 'Meme ma grand-mere ecrivait de meilleurs tests sur un PIII x86', + 'A chaque erreur, prenez une biere', + 'Ca craint, mon pote' + }, + success_messages = { + 'Oh yeah, tests reussis', + 'Pas grave, y\'a eu du succes', + 'C\'est du bon, mon pote. Que du bon!', + 'Reussi, haut la main!', + 'Test reussi. Un de plus. Offre toi une biere, sur mon compte!', + } +} diff --git a/test/lua/busted/languages/ja.lua b/test/lua/busted/languages/ja.lua new file mode 100644 index 000000000..d726fa8f9 --- /dev/null +++ b/test/lua/busted/languages/ja.lua @@ -0,0 +1,43 @@ +local s = require('say') + +s:set_namespace('ja') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', '保留') +s:set('output.failure', '失敗') +s:set('output.success', '成功') + +s:set('output.pending_plural', '保留') +s:set('output.failure_plural', '失敗') +s:set('output.success_plural', '成功') + +s:set('output.pending_zero', '保留') +s:set('output.failure_zero', '失敗') +s:set('output.success_zero', '成功') + +s:set('output.pending_single', '保留') +s:set('output.failure_single', '失敗') +s:set('output.success_single', '成功') + +s:set('output.seconds', '秒') + +-- definitions following are not used within the 'say' namespace +return { + failure_messages = { + '%d個の仕様が破綻しています', + '仕様が破綻しています', + 'あなたの書くコードは良くないので反省するべきです', + 'あなたの書くコードは危険地帯にあります', + 'おかしなゲームです。勝利する唯一の方法はテストをしないことです', + '私の祖母でもPentium Pentium III x86の上でもっといいコードを書いていましたよ', + 'いつも失敗しているのでビールでも飲みましょう', + '罪悪感を持ちましょう', + }, + success_messages = { + 'オォーイェー、テストが通った', + '問題ない、テストがある', + '順調ですね', + '大成功', + 'テストが通ったし、ビールでも飲もう', + } +} diff --git a/test/lua/busted/languages/nl.lua b/test/lua/busted/languages/nl.lua new file mode 100644 index 000000000..6173320bd --- /dev/null +++ b/test/lua/busted/languages/nl.lua @@ -0,0 +1,43 @@ +local s = require('say') + +s:set_namespace('nl') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', 'Hangend') +s:set('output.failure', 'Mislukt') +s:set('output.success', 'Succes') + +s:set('output.pending_plural', 'hangenden') +s:set('output.failure_plural', 'mislukkingen') +s:set('output.success_plural', 'successen') + +s:set('output.pending_zero', 'hangend') +s:set('output.failure_zero', 'mislukt') +s:set('output.success_zero', 'successen') + +s:set('output.pending_single', 'hangt') +s:set('output.failure_single', 'mislukt') +s:set('output.success_single', 'succes') + +s:set('output.seconds', 'seconden') + +-- definitions following are not used within the 'say' namespace +return { + failure_messages = { + 'Je hebt %d busted specs', + 'Je specs zijn busted', + 'Je code is slecht en zo zou jij je ook moeten voelen', + 'Je code zit in de Gevaren Zone', + 'Vreemd spelletje. The enige manier om te winnen is door niet te testen', + 'Mijn oma schreef betere specs op een 3 86', + 'Elke keer dat iets mislukt, nog een biertje drinken', + 'Voelt klote man' + }, + success_messages = { + 'Joeperdepoep, de specs zijn er door', + 'Doet er niet toe, had specs', + 'Voelt goed, man', + 'Fantastisch success', + 'Testen geslaagd, neem nog een biertje', + } +} diff --git a/test/lua/busted/languages/ru.lua b/test/lua/busted/languages/ru.lua new file mode 100644 index 000000000..d0f403440 --- /dev/null +++ b/test/lua/busted/languages/ru.lua @@ -0,0 +1,37 @@ +local s = require('say') + +s:set_namespace('ru') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', 'Ожидает') +s:set('output.failure', 'Поломалcя') +s:set('output.success', 'Прошeл') + +s:set('output.pending_plural', 'ожидают') +s:set('output.failure_plural', 'поломалиcь') +s:set('output.success_plural', 'прошли') + +s:set('output.pending_zero', 'ожидающих') +s:set('output.failure_zero', 'поломанных') +s:set('output.success_zero', 'прошедших') + +s:set('output.pending_single', 'ожидает') +s:set('output.failure_single', 'поломался') +s:set('output.success_single', 'прошел') + +s:set('output.seconds', 'секунд') + +---- definitions following are not used within the 'say' namespace +return { + failure_messages = { + 'У тебя %d просратых тестов', + 'Твои тесты поломаны', + 'Твой код говеный - пойди напейся!' + }, + success_messages = { + 'Поехали!', + 'Жизнь - хороша!', + 'Ффух в этот раз пронесло!', + 'Ура!' + } +} diff --git a/test/lua/busted/languages/th.lua b/test/lua/busted/languages/th.lua new file mode 100644 index 000000000..0f8e0a13a --- /dev/null +++ b/test/lua/busted/languages/th.lua @@ -0,0 +1,43 @@ +local s = require('say') + +s:set_namespace('th') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', 'อยู่ระหว่างดำเนินการ') +s:set('output.failure', 'ล้มเหลว') +s:set('output.success', 'สำเร็จ') + +s:set('output.pending_plural', 'อยู่ระหว่างดำเนินการ') +s:set('output.failure_plural', 'ล้มเหลว') +s:set('output.success_plural', 'สำเร็จ') + +s:set('output.pending_zero', 'อยู่ระหว่างดำเนินการ') +s:set('output.failure_zero', 'ล้มเหลว') +s:set('output.success_zero', 'สำเร็จ') + +s:set('output.pending_single', 'อยู่ระหว่างดำเนินการ') +s:set('output.failure_single', 'ล้มเหลว') +s:set('output.success_single', 'สำเร็จ') + +s:set('output.seconds', 'วินาที') + +-- definitions following are not used within the 'say' namespace +return { + failure_messages = { + 'คุณมี %d บัสเต็ดสเปค', + 'สเปคของคุณคือ บัสเต็ด', + 'โค้ดของคุณไม่ดีเลย คุณควรรู้สึกแย่น่ะ', + 'โค้ดของคุณอยู่ในเขตอันตราย!', + 'มันแปลกๆน่ะ วิธีที่จะชนะไม่ได้มีแค่เทสอย่างเดียว', + 'ยายผมเขียนสเปคดีกว่านี้อีก บนเครื่อง 386', + 'ทุกๆครั้งที่ล้มเหลว, ดื่มเบียร์แก้วใหม่', + 'แย่จัง นายท่าน' + }, + success_messages = { + 'อุ๊ตะ!!!, สเปคผ่าน!', + 'ไม่สำคัญ, มีสเปค', + 'ฟินเลยดิ นายท่าน', + 'สำเร็จ ยอดเยี่ยม', + 'เทสผ่าน, ดื่มเบียร์ๆๆๆ', + } +} diff --git a/test/lua/busted/languages/ua.lua b/test/lua/busted/languages/ua.lua new file mode 100644 index 000000000..40afcfbab --- /dev/null +++ b/test/lua/busted/languages/ua.lua @@ -0,0 +1,37 @@ +local s = require('say') + +s:set_namespace('ua') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', 'Очікує') +s:set('output.failure', 'Зламався') +s:set('output.success', 'Пройшов') + +s:set('output.pending_plural', 'очікують') +s:set('output.failure_plural', 'зламались') +s:set('output.success_plural', 'пройшли') + +s:set('output.pending_zero', 'очікуючих') +s:set('output.failure_zero', 'зламаних') +s:set('output.success_zero', 'пройдених') + +s:set('output.pending_single', 'очікує') +s:set('output.failure_single', 'зламався') +s:set('output.success_single', 'пройшов') + +s:set('output.seconds', 'секунд') + + +---- definitions following are not used within the 'say' namespace +return { + failure_messages = { + 'Ти зрадив %d тестів!', + 'Ой йо..', + 'Вороги поламали наші тести!' + }, + success_messages = { + 'Слава Україні! Героям Слава!', + 'Тестування успішно пройдено!', + 'Всі баги знищено!' + } +} diff --git a/test/lua/busted/languages/zh.lua b/test/lua/busted/languages/zh.lua new file mode 100644 index 000000000..21f97e5cb --- /dev/null +++ b/test/lua/busted/languages/zh.lua @@ -0,0 +1,43 @@ +local s = require('say') + +s:set_namespace('zh') + +-- 'Pending: test.lua @ 12 \n description +s:set('output.pending', '开发中') +s:set('output.failure', '失败') +s:set('output.success', '成功') + +s:set('output.pending_plural', '开发中') +s:set('output.failure_plural', '失败') +s:set('output.success_plural', '成功') + +s:set('output.pending_zero', '开发中') +s:set('output.failure_zero', '失败') +s:set('output.success_zero', '成功') + +s:set('output.pending_single', '开发中') +s:set('output.failure_single', '失败') +s:set('output.success_single', '成功') + +s:set('output.seconds', '秒') + +-- definitions following are not used within the 'say' namespace +return { + failure_messages = { + '你一共提交了[%d]个测试用例', + '又出错了!', + '到底哪里不对呢?', + '出错了,又要加班了!', + '囧,出Bug了!', + '据说比尔盖兹也写了一堆Bug,别灰心!', + '又出错了,休息一下吧', + 'Bug好多,心情好坏!' + }, + success_messages = { + '牛X,测试通过了!', + '测试通过了,感觉不错吧,兄弟!', + '哥们,干得漂亮!', + '终于通过了!干一杯先!', + '阿弥陀佛~,菩萨显灵了!', + } +} diff --git a/test/lua/busted/modules/configuration_loader.lua b/test/lua/busted/modules/configuration_loader.lua new file mode 100644 index 000000000..f437a04cd --- /dev/null +++ b/test/lua/busted/modules/configuration_loader.lua @@ -0,0 +1,34 @@ +return function() + local tablex = require 'pl.tablex' + + -- Function to load the .busted configuration file if available + local loadBustedConfigurationFile = function(configFile, config, defaults) + if type(configFile) ~= 'table' then + return config, '.busted file does not return a table.' + end + + local defaults = defaults or {} + local run = config.run or defaults.run + + if run and run ~= '' then + local runConfig = configFile[run] + + if type(runConfig) == 'table' then + config = tablex.merge(runConfig, config, true) + else + return config, 'Task `' .. run .. '` not found, or not a table.' + end + end + + if type(configFile.default) == 'table' then + config = tablex.merge(configFile.default, config, true) + end + + config = tablex.merge(defaults, config, true) + + return config + end + + return loadBustedConfigurationFile +end + diff --git a/test/lua/busted/modules/files/lua.lua b/test/lua/busted/modules/files/lua.lua new file mode 100644 index 000000000..a4218f6d4 --- /dev/null +++ b/test/lua/busted/modules/files/lua.lua @@ -0,0 +1,24 @@ +local path = require 'pl.path' + +local ret = {} + +local getTrace = function(filename, info) + local index = info.traceback:find('\n%s*%[C]') + info.traceback = info.traceback:sub(1, index) + return info, false +end + +ret.match = function(busted, filename) + return path.extension(filename) == '.lua' +end + + +ret.load = function(busted, filename) + local file, err = loadfile(filename) + if not file then + busted.publish({ 'error', 'file' }, { descriptor = 'file', name = filename }, nil, err, {}) + end + return file, getTrace +end + +return ret diff --git a/test/lua/busted/modules/files/moonscript.lua b/test/lua/busted/modules/files/moonscript.lua new file mode 100644 index 000000000..145b942f5 --- /dev/null +++ b/test/lua/busted/modules/files/moonscript.lua @@ -0,0 +1,108 @@ +local path = require 'pl.path' + +local ok, moonscript, line_tables, util = pcall(function() + return require 'moonscript', require 'moonscript.line_tables', require 'moonscript.util' +end) + +local _cache = {} + +-- find the line number of `pos` chars into fname +local lookup_line = function(fname, pos) + if not _cache[fname] then + local f = io.open(fname) + _cache[fname] = f:read('*a') + f:close() + end + + return util.pos_to_line(_cache[fname], pos) +end + +local rewrite_linenumber = function(fname, lineno) + local tbl = line_tables['@' .. fname] + if fname and tbl then + for i = lineno, 0 ,-1 do + if tbl[i] then + return lookup_line(fname, tbl[i]) + end + end + end + + return lineno +end + +local rewrite_filename = function(filename) + -- sometimes moonscript gives files like [string "./filename.moon"], so + -- we'll chop it up to only get the filename. + return filename:match('string "(.+)"') or filename +end + +local rewrite_traceback = function(fname, trace) + local rewrite_one = function(line, pattern, sub) + if line == nil then return '' end + + local fname, lineno = line:match(pattern) + + if fname and lineno then + fname = rewrite_filename(fname) + local new_lineno = rewrite_linenumber(fname, tonumber(lineno)) + if new_lineno then + line = line:gsub(sub:format(tonumber(lineno)), sub:format(tonumber(new_lineno))) + end + end + + return line + end + + local lines = {} + local j = 0 + + for line in trace:gmatch('[^\r\n]+') do + j = j + 1 + line = rewrite_one(line, '%s*(.-):(%d+): ', ':%d:') + line = rewrite_one(line, '<(.*):(%d+)>', ':%d>') + lines[j] = line + end + + return '\n' .. table.concat(lines, trace:match('[\r\n]+')) .. '\n' +end + +local ret = {} + +local getTrace = function(filename, info) + local index = info.traceback:find('\n%s*%[C]') + info.traceback = info.traceback:sub(1, index) + + info.short_src = rewrite_filename(info.short_src) + info.traceback = rewrite_traceback(filename, info.traceback) + info.linedefined = rewrite_linenumber(filename, info.linedefined) + info.currentline = rewrite_linenumber(filename, info.currentline) + + return info +end + +local rewriteMessage = function(filename, message) + local fname, line, msg = message:match('^([^\n]-):(%d+): (.*)') + if not fname then + return message + end + + fname = rewrite_filename(fname) + line = rewrite_linenumber(fname, tonumber(line)) + + return fname .. ':' .. tostring(line) .. ': ' .. msg +end + +ret.match = function(busted, filename) + return ok and path.extension(filename) == '.moon' +end + + +ret.load = function(busted, filename) + local file, err = moonscript.loadfile(filename) + if not file then + busted.publish({ 'error', 'file' }, { descriptor = 'file', name = filename }, nil, err, {}) + end + return file, getTrace, rewriteMessage +end + +return ret diff --git a/test/lua/busted/modules/files/terra.lua b/test/lua/busted/modules/files/terra.lua new file mode 100644 index 000000000..02956f76a --- /dev/null +++ b/test/lua/busted/modules/files/terra.lua @@ -0,0 +1,24 @@ +local path = require 'pl.path' + +local ret = {} +local ok, terralib = pcall(function() return require 'terralib' end) + +local getTrace = function(filename, info) + local index = info.traceback:find('\n%s*%[C]') + info.traceback = info.traceback:sub(1, index) + return info +end + +ret.match = function(busted, filename) + return ok and path.extension(filename) == '.t' +end + +ret.load = function(busted, filename) + local file, err = terralib.loadfile(filename) + if not file then + busted.publish({ 'error', 'file' }, { descriptor = 'file', name = filename }, nil, err, {}) + end + return file, getTrace +end + +return ret diff --git a/test/lua/busted/modules/helper_loader.lua b/test/lua/busted/modules/helper_loader.lua new file mode 100644 index 000000000..166f7ccd1 --- /dev/null +++ b/test/lua/busted/modules/helper_loader.lua @@ -0,0 +1,23 @@ +local utils = require 'busted.utils' +local hasMoon, moonscript = pcall(require, 'moonscript') + +return function() + local loadHelper = function(helper, hpath, options, busted) + local success, err = pcall(function() + arg = options.arguments + if helper:match('%.lua$') then + dofile(utils.normpath(hpath)) + elseif hasMoon and helper:match('%.moon$') then + moonscript.dofile(utils.normpath(hpath)) + else + require(helper) + end + end) + + if not success then + busted.publish({ 'error', 'helper' }, { descriptor = 'helper', name = helper }, nil, err, {}) + end + end + + return loadHelper +end diff --git a/test/lua/busted/modules/luacov.lua b/test/lua/busted/modules/luacov.lua new file mode 100644 index 000000000..99cfc8f56 --- /dev/null +++ b/test/lua/busted/modules/luacov.lua @@ -0,0 +1,22 @@ +return function() + -- Function to initialize luacov if available + local loadLuaCov = function() + local result, luacov = pcall(require, 'luacov.runner') + + if not result then + return print('LuaCov not found on the system, try running without --coverage option, or install LuaCov first') + end + + -- call it to start + luacov() + + -- exclude busted files + table.insert(luacov.configuration.exclude, 'busted_bootstrap$') + table.insert(luacov.configuration.exclude, 'busted%.') + table.insert(luacov.configuration.exclude, 'luassert%.') + table.insert(luacov.configuration.exclude, 'say%.') + table.insert(luacov.configuration.exclude, 'pl%.') + end + + return loadLuaCov +end diff --git a/test/lua/busted/modules/output_handler_loader.lua b/test/lua/busted/modules/output_handler_loader.lua new file mode 100644 index 000000000..d52211846 --- /dev/null +++ b/test/lua/busted/modules/output_handler_loader.lua @@ -0,0 +1,31 @@ +local utils = require 'busted.utils' +local hasMoon, moonscript = pcall(require, 'moonscript') + +return function() + local loadOutputHandler = function(output, opath, options, busted, defaultOutput) + local handler + + local success, err = pcall(function() + if output:match('%.lua$') then + handler = dofile(utils.normpath(opath)) + elseif hasMoon and output:match('%.moon$') then + handler = moonscript.dofile(utils.normpath(opath)) + else + handler = require('busted.outputHandlers.' .. output) + end + end) + + if not success and err:match("module '.-' not found:") then + success, err = pcall(function() handler = require(output) end) + end + + if not success then + busted.publish({ 'error', 'output' }, { descriptor = 'output', name = output }, nil, err, {}) + handler = require('busted.outputHandlers.' .. defaultOutput) + end + + return handler(options, busted) + end + + return loadOutputHandler +end diff --git a/test/lua/busted/modules/test_file_loader.lua b/test/lua/busted/modules/test_file_loader.lua new file mode 100644 index 000000000..391cce501 --- /dev/null +++ b/test/lua/busted/modules/test_file_loader.lua @@ -0,0 +1,84 @@ +local s = require 'say' + +return function(busted, loaders, options) + local path = require 'pl.path' + local dir = require 'pl.dir' + local tablex = require 'pl.tablex' + local shuffle = require 'busted.utils'.shuffle + local fileLoaders = {} + + for _, v in pairs(loaders) do + local loader = require('busted.modules.files.'..v) + fileLoaders[#fileLoaders+1] = loader + end + + local getTestFiles = function(rootFile, pattern) + local fileList + + if path.isfile(rootFile) then + fileList = { rootFile } + elseif path.isdir(rootFile) then + local getfiles = options.recursive and dir.getallfiles or dir.getfiles + fileList = getfiles(rootFile) + + fileList = tablex.filter(fileList, function(filename) + return path.basename(filename):find(pattern) + end) + + fileList = tablex.filter(fileList, function(filename) + if path.is_windows then + return not filename:find('%\\%.%w+.%w+') + else + return not filename:find('/%.%w+.%w+') + end + end) + else + fileList = {} + end + + return fileList + end + + -- runs a testfile, loading its tests + local loadTestFile = function(busted, filename) + for _, v in pairs(fileLoaders) do + if v.match(busted, filename) then + return v.load(busted, filename) + end + end + end + + local loadTestFiles = function(rootFile, pattern, loaders) + local fileList = getTestFiles(rootFile, pattern) + + if options.shuffle then + shuffle(fileList, options.seed) + elseif options.sort then + table.sort(fileList) + end + + for i, fileName in ipairs(fileList) do + local testFile, getTrace, rewriteMessage = loadTestFile(busted, fileName, loaders) + + if testFile then + local file = setmetatable({ + getTrace = getTrace, + rewriteMessage = rewriteMessage + }, { + __call = testFile + }) + + busted.executors.file(fileName, file) + end + end + + if #fileList == 0 then + busted.publish({ 'error' }, {}, nil, s('output.no_test_files_match'):format(pattern), {}) + end + + return fileList + end + + return loadTestFiles, loadTestFile, getTestFiles +end + diff --git a/test/lua/busted/outputHandlers/TAP.lua b/test/lua/busted/outputHandlers/TAP.lua new file mode 100644 index 000000000..b46350ec8 --- /dev/null +++ b/test/lua/busted/outputHandlers/TAP.lua @@ -0,0 +1,51 @@ +local pretty = require 'pl.pretty' + +return function(options, busted) + local handler = require 'busted.outputHandlers.base'(busted) + + handler.suiteEnd = function() + local total = handler.successesCount + handler.errorsCount + handler.failuresCount + print('1..' .. total) + + local success = 'ok %u - %s' + local failure = 'not ' .. success + local counter = 0 + + for i,t in pairs(handler.successes) do + counter = counter + 1 + print(success:format(counter, t.name)) + end + + local showFailure = function(t) + counter = counter + 1 + local message = t.message + local trace = t.trace or {} + + if message == nil then + message = 'Nil error' + elseif type(message) ~= 'string' then + message = pretty.write(message) + end + + print(failure:format(counter, t.name)) + print('# ' .. t.element.trace.short_src .. ' @ ' .. t.element.trace.currentline) + print('# Failure message: ' .. message:gsub('\n', '\n# ')) + if options.verbose and trace.traceback then + print('# ' .. trace.traceback:gsub('^\n', '', 1):gsub('\n', '\n# ')) + end + end + + for i,t in pairs(handler.errors) do + showFailure(t) + end + for i,t in pairs(handler.failures) do + showFailure(t) + end + + return nil, true + end + + busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) + + return handler +end diff --git a/test/lua/busted/outputHandlers/base.lua b/test/lua/busted/outputHandlers/base.lua new file mode 100644 index 000000000..5ea9540fc --- /dev/null +++ b/test/lua/busted/outputHandlers/base.lua @@ -0,0 +1,177 @@ +return function(busted) + local handler = { + successes = {}, + successesCount = 0, + pendings = {}, + pendingsCount = 0, + failures = {}, + failuresCount = 0, + errors = {}, + errorsCount = 0, + inProgress = {} + } + + handler.cancelOnPending = function(element, parent, status) + return not ((element.descriptor == 'pending' or status == 'pending') and handler.options.suppressPending) + end + + handler.subscribe = function(handler, options) + require('busted.languages.en') + handler.options = options + + if options.language ~= 'en' then + require('busted.languages.' .. options.language) + end + + busted.subscribe({ 'suite', 'reinitialize' }, handler.baseSuiteRepeat, { priority = 1 }) + busted.subscribe({ 'suite', 'start' }, handler.baseSuiteStart, { priority = 1 }) + busted.subscribe({ 'suite', 'end' }, handler.baseSuiteEnd, { priority = 1 }) + busted.subscribe({ 'test', 'start' }, handler.baseTestStart, { priority = 1, predicate = handler.cancelOnPending }) + busted.subscribe({ 'test', 'end' }, handler.baseTestEnd, { priority = 1, predicate = handler.cancelOnPending }) + busted.subscribe({ 'pending' }, handler.basePending, { priority = 1, predicate = handler.cancelOnPending }) + busted.subscribe({ 'failure', 'it' }, handler.baseTestFailure, { priority = 1 }) + busted.subscribe({ 'error', 'it' }, handler.baseTestError, { priority = 1 }) + busted.subscribe({ 'failure' }, handler.baseError, { priority = 1 }) + busted.subscribe({ 'error' }, handler.baseError, { priority = 1 }) + end + + handler.getFullName = function(context) + local parent = busted.context.parent(context) + local names = { (context.name or context.descriptor) } + + while parent and (parent.name or parent.descriptor) and + parent.descriptor ~= 'file' do + + table.insert(names, 1, parent.name or parent.descriptor) + parent = busted.context.parent(parent) + end + + return table.concat(names, ' ') + end + + handler.format = function(element, parent, message, debug, isError) + local formatted = { + trace = debug or element.trace, + element = element, + name = handler.getFullName(element), + message = message, + isError = isError + } + formatted.element.trace = element.trace or debug + + return formatted + end + + handler.getDuration = function() + if not handler.endTime or not handler.startTime then + return 0 + end + + return handler.endTime - handler.startTime + end + + handler.baseSuiteStart = function() + handler.startTime = os.clock() + return nil, true + end + + handler.baseSuiteRepeat = function() + handler.successes = {} + handler.successesCount = 0 + handler.pendings = {} + handler.pendingsCount = 0 + handler.failures = {} + handler.failuresCount = 0 + handler.errors = {} + handler.errorsCount = 0 + handler.inProgress = {} + + return nil, true + end + + handler.baseSuiteEnd = function() + handler.endTime = os.clock() + return nil, true + end + + handler.baseTestStart = function(element, parent) + handler.inProgress[tostring(element)] = {} + return nil, true + end + + handler.baseTestEnd = function(element, parent, status, debug) + local isError + local insertTable + + if status == 'success' then + insertTable = handler.successes + handler.successesCount = handler.successesCount + 1 + elseif status == 'pending' then + insertTable = handler.pendings + handler.pendingsCount = handler.pendingsCount + 1 + elseif status == 'failure' then + insertTable = handler.failures + -- failure count already incremented in error handler + elseif status == 'error' then + -- error count already incremented in error handler + insertTable = handler.errors + isError = true + end + + local formatted = handler.format(element, parent, element.message, debug, isError) + + local id = tostring(element) + if handler.inProgress[id] then + for k, v in pairs(handler.inProgress[id]) do + formatted[k] = v + end + + handler.inProgress[id] = nil + end + + table.insert(insertTable, formatted) + + return nil, true + end + + local function saveInProgress(element, message, debug) + local id = tostring(element) + handler.inProgress[id].message = message + handler.inProgress[id].trace = debug + end + + local function saveError(element, parent, message, debug) + if parent.randomseed then + message = 'Random Seed: ' .. parent.randomseed .. '\n' .. message + end + saveInProgress(element, message, debug) + end + + handler.basePending = function(element, parent, message, debug) + saveInProgress(element, message, debug) + return nil, true + end + + handler.baseTestFailure = function(element, parent, message, debug) + handler.failuresCount = handler.failuresCount + 1 + saveError(element, parent, message, debug) + return nil, true + end + + handler.baseTestError = function(element, parent, message, debug) + handler.errorsCount = handler.errorsCount + 1 + saveError(element, parent, message, debug) + return nil, true + end + + handler.baseError = function(element, parent, message, debug) + if element.descriptor ~= 'it' then + handler.errorsCount = handler.errorsCount + 1 + table.insert(handler.errors, handler.format(element, parent, message, debug, true)) + end + + return nil, true + end + + return handler +end diff --git a/test/lua/busted/outputHandlers/json.lua b/test/lua/busted/outputHandlers/json.lua new file mode 100644 index 000000000..f19a336aa --- /dev/null +++ b/test/lua/busted/outputHandlers/json.lua @@ -0,0 +1,21 @@ +local json = require 'dkjson' + +return function(options, busted) + local handler = require 'busted.outputHandlers.base'(busted) + + handler.suiteEnd = function() + print(json.encode({ + pendings = handler.pendings, + successes = handler.successes, + failures = handler.failures, + errors = handler.errors, + duration = handler.getDuration() + })) + + return nil, true + end + + busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) + + return handler +end diff --git a/test/lua/busted/outputHandlers/junit.lua b/test/lua/busted/outputHandlers/junit.lua new file mode 100644 index 000000000..36e78936e --- /dev/null +++ b/test/lua/busted/outputHandlers/junit.lua @@ -0,0 +1,136 @@ +local xml = require 'pl.xml' +local socket = require("socket") +local string = require("string") + +return function(options, busted) + local handler = require 'busted.outputHandlers.base'(busted) + local top = { + start_time = socket.gettime(), + xml_doc = xml.new('testsuites', { + tests = 0, + errors = 0, + failures = 0, + skip = 0, + }) + } + local stack = {} + local testStartTime + + handler.suiteStart = function(count, total) + local suite = { + start_time = socket.gettime(), + xml_doc = xml.new('testsuite', { + name = 'Run ' .. count .. ' of ' .. total, + tests = 0, + errors = 0, + failures = 0, + skip = 0, + timestamp = os.date('!%Y-%m-%dT%T'), + }) + } + top.xml_doc:add_direct_child(suite.xml_doc) + table.insert(stack, top) + top = suite + + return nil, true + end + + local function elapsed(start_time) + return string.format("%.2f", (socket.gettime() - start_time)) + end + + handler.suiteEnd = function(count, total) + local suite = top + suite.xml_doc.attr.time = elapsed(suite.start_time) + + top = table.remove(stack) + top.xml_doc.attr.tests = top.xml_doc.attr.tests + suite.xml_doc.attr.tests + top.xml_doc.attr.errors = top.xml_doc.attr.errors + suite.xml_doc.attr.errors + top.xml_doc.attr.failures = top.xml_doc.attr.failures + suite.xml_doc.attr.failures + top.xml_doc.attr.skip = top.xml_doc.attr.skip + suite.xml_doc.attr.skip + + return nil, true + end + + handler.exit = function() + top.xml_doc.attr.time = elapsed(top.start_time) + print(xml.tostring(top.xml_doc, '', '\t', nil, false)) + + return nil, true + end + + local function testStatus(element, parent, message, status, trace) + local testcase_node = xml.new('testcase', { + classname = element.trace.short_src .. ':' .. element.trace.currentline, + name = handler.getFullName(element), + time = elapsed(testStartTime) + }) + top.xml_doc:add_direct_child(testcase_node) + + if status ~= 'success' then + testcase_node:addtag(status) + if message then testcase_node:text(message) end + if trace and trace.traceback then testcase_node:text(trace.traceback) end + testcase_node:up() + end + end + + handler.testStart = function(element, parent) + testStartTime = socket.gettime() + + return nil, true + end + + handler.testEnd = function(element, parent, status) + top.xml_doc.attr.tests = top.xml_doc.attr.tests + 1 + + if status == 'success' then + testStatus(element, parent, nil, 'success') + elseif status == 'pending' then + top.xml_doc.attr.skip = top.xml_doc.attr.skip + 1 + local formatted = handler.pendings[#handler.pendings] + local trace = element.trace ~= formatted.trace and formatted.trace + testStatus(element, parent, formatted.message, 'skipped', trace) + end + + return nil, true + end + + handler.failureTest = function(element, parent, message, trace) + top.xml_doc.attr.failures = top.xml_doc.attr.failures + 1 + testStatus(element, parent, message, 'failure', trace) + return nil, true + end + + handler.errorTest = function(element, parent, message, trace) + top.xml_doc.attr.errors = top.xml_doc.attr.errors + 1 + testStatus(element, parent, message, 'error', trace) + return nil, true + end + + handler.error = function(element, parent, message, trace) + if element.descriptor ~= 'it' then + top.xml_doc.attr.errors = top.xml_doc.attr.errors + 1 + top.xml_doc:addtag('error') + top.xml_doc:text(message) + if trace and trace.traceback then + top.xml_doc:text(trace.traceback) + end + top.xml_doc:up() + end + + return nil, true + end + + busted.subscribe({ 'exit' }, handler.exit) + busted.subscribe({ 'suite', 'start' }, handler.suiteStart) + busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) + busted.subscribe({ 'test', 'start' }, handler.testStart, { predicate = handler.cancelOnPending }) + busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending }) + busted.subscribe({ 'error', 'it' }, handler.errorTest) + busted.subscribe({ 'failure', 'it' }, handler.failureTest) + busted.subscribe({ 'error' }, handler.error) + busted.subscribe({ 'failure' }, handler.error) + + return handler +end diff --git a/test/lua/busted/outputHandlers/plainTerminal.lua b/test/lua/busted/outputHandlers/plainTerminal.lua new file mode 100644 index 000000000..fc4b092f2 --- /dev/null +++ b/test/lua/busted/outputHandlers/plainTerminal.lua @@ -0,0 +1,175 @@ +local s = require 'say' +local pretty = require 'pl.pretty' + +return function(options, busted) + local handler = require 'busted.outputHandlers.base'(busted) + + local successDot = '+' + local failureDot = '-' + local errorDot = '*' + local pendingDot = '.' + + local pendingDescription = function(pending) + local name = pending.name + + local string = s('output.pending') .. ' → ' .. + pending.trace.short_src .. ' @ ' .. + pending.trace.currentline .. + '\n' .. name + + if type(pending.message) == 'string' then + string = string .. '\n' .. pending.message + elseif pending.message ~= nil then + string = string .. '\n' .. pretty.write(pending.message) + end + + return string + end + + local failureMessage = function(failure) + local string + if type(failure.message) == 'string' then + string = failure.message + elseif failure.message == nil then + string = 'Nil error' + else + string = pretty.write(failure.message) + end + + return string + end + + local failureDescription = function(failure, isError) + local string = s('output.failure') .. ' → ' + if isError then + string = s('output.error') .. ' → ' + end + + if not failure.element.trace or not failure.element.trace.short_src then + string = string .. + failureMessage(failure) .. '\n' .. + failure.name + else + string = string .. + failure.element.trace.short_src .. ' @ ' .. + failure.element.trace.currentline .. '\n' .. + failure.name .. '\n' .. + failureMessage(failure) + end + + if options.verbose and failure.trace and failure.trace.traceback then + string = string .. '\n' .. failure.trace.traceback + end + + return string + end + + local statusString = function() + local successString = s('output.success_plural') + local failureString = s('output.failure_plural') + local pendingString = s('output.pending_plural') + local errorString = s('output.error_plural') + + local ms = handler.getDuration() + local successes = handler.successesCount + local pendings = handler.pendingsCount + local failures = handler.failuresCount + local errors = handler.errorsCount + + if successes == 0 then + successString = s('output.success_zero') + elseif successes == 1 then + successString = s('output.success_single') + end + + if failures == 0 then + failureString = s('output.failure_zero') + elseif failures == 1 then + failureString = s('output.failure_single') + end + + if pendings == 0 then + pendingString = s('output.pending_zero') + elseif pendings == 1 then + pendingString = s('output.pending_single') + end + + if errors == 0 then + errorString = s('output.error_zero') + elseif errors == 1 then + errorString = s('output.error_single') + end + + local formattedTime = ('%.6f'):format(ms):gsub('([0-9])0+$', '%1') + + return successes .. ' ' .. successString .. ' / ' .. + failures .. ' ' .. failureString .. ' / ' .. + errors .. ' ' .. errorString .. ' / ' .. + pendings .. ' ' .. pendingString .. ' : ' .. + formattedTime .. ' ' .. s('output.seconds') + end + + handler.testEnd = function(element, parent, status, debug) + if not options.deferPrint then + local string = successDot + + if status == 'pending' then + string = pendingDot + elseif status == 'failure' then + string = failureDot + elseif status == 'error' then + string = errorDot + end + + io.write(string) + io.flush() + end + + return nil, true + end + + handler.suiteStart = function(count, total) + local runString = (total > 1 and '\nRepeating all tests (run %d of %d) . . .\n\n' or '') + io.write(runString:format(count, total)) + io.flush() + end + + handler.suiteEnd = function() + print('') + print(statusString()) + + for i, pending in pairs(handler.pendings) do + print('') + print(pendingDescription(pending)) + end + + for i, err in pairs(handler.failures) do + print('') + print(failureDescription(err)) + end + + for i, err in pairs(handler.errors) do + print('') + print(failureDescription(err, true)) + end + + return nil, true + end + + handler.error = function(element, parent, message, debug) + io.write(errorDot) + io.flush() + + return nil, true + end + + busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending }) + busted.subscribe({ 'suite', 'start' }, handler.suiteStart) + busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) + busted.subscribe({ 'error', 'file' }, handler.error) + busted.subscribe({ 'failure', 'file' }, handler.error) + busted.subscribe({ 'error', 'describe' }, handler.error) + busted.subscribe({ 'failure', 'describe' }, handler.error) + + return handler +end diff --git a/test/lua/busted/outputHandlers/sound.lua b/test/lua/busted/outputHandlers/sound.lua new file mode 100644 index 000000000..8ac1a46ce --- /dev/null +++ b/test/lua/busted/outputHandlers/sound.lua @@ -0,0 +1,36 @@ +local app = require 'pl.app' +return function(options, busted) + local handler = require 'busted.outputHandlers.base'(busted) + local language = require('busted.languages.' .. options.language) + + handler.suiteEnd = function() + local system = app.platform() + local sayer_pre, sayer_post + local messages + + if system == 'Linux' then + sayer_pre = 'espeak -s 160 ' + sayer_post = ' > /dev/null 2>&1' + elseif system and system:match('^Windows') then + sayer_pre = 'echo ' + sayer_post = ' | ptts' + else + sayer_pre = 'say ' + sayer_post = '' + end + + if handler.failuresCount > 0 then + messages = language.failure_messages + else + messages = language.success_messages + end + + io.popen(sayer_pre .. '"' .. messages[math.random(1, #messages)] .. '"' .. sayer_post) + + return nil, true + end + + busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) + + return handler +end diff --git a/test/lua/busted/outputHandlers/utfTerminal.lua b/test/lua/busted/outputHandlers/utfTerminal.lua new file mode 100644 index 000000000..f34015cc4 --- /dev/null +++ b/test/lua/busted/outputHandlers/utfTerminal.lua @@ -0,0 +1,176 @@ +local ansicolors = require 'ansicolors' +local s = require 'say' +local pretty = require 'pl.pretty' + +return function(options, busted) + local handler = require 'busted.outputHandlers.base'(busted) + + local successDot = ansicolors('%{green}●') + local failureDot = ansicolors('%{red}◼') + local errorDot = ansicolors('%{magenta}✱') + local pendingDot = ansicolors('%{yellow}◌') + + local pendingDescription = function(pending) + local name = pending.name + + local string = ansicolors('%{yellow}' .. s('output.pending')) .. ' → ' .. + ansicolors('%{cyan}' .. pending.trace.short_src) .. ' @ ' .. + ansicolors('%{cyan}' .. pending.trace.currentline) .. + '\n' .. ansicolors('%{bright}' .. name) + + if type(pending.message) == 'string' then + string = string .. '\n' .. pending.message + elseif pending.message ~= nil then + string = string .. '\n' .. pretty.write(pending.message) + end + + return string + end + + local failureMessage = function(failure) + local string + if type(failure.message) == 'string' then + string = failure.message + elseif failure.message == nil then + string = 'Nil error' + else + string = pretty.write(failure.message) + end + + return string + end + + local failureDescription = function(failure, isError) + local string = ansicolors('%{red}' .. s('output.failure')) .. ' → ' + if isError then + string = ansicolors('%{magenta}' .. s('output.error')) .. ' → ' + end + + if not failure.element.trace or not failure.element.trace.short_src then + string = string .. + ansicolors('%{cyan}' .. failureMessage(failure)) .. '\n' .. + ansicolors('%{bright}' .. failure.name) + else + string = string .. + ansicolors('%{cyan}' .. failure.element.trace.short_src) .. ' @ ' .. + ansicolors('%{cyan}' .. failure.element.trace.currentline) .. '\n' .. + ansicolors('%{bright}' .. failure.name) .. '\n' .. + failureMessage(failure) + end + + if options.verbose and failure.trace and failure.trace.traceback then + string = string .. '\n' .. failure.trace.traceback + end + + return string + end + + local statusString = function() + local successString = s('output.success_plural') + local failureString = s('output.failure_plural') + local pendingString = s('output.pending_plural') + local errorString = s('output.error_plural') + + local ms = handler.getDuration() + local successes = handler.successesCount + local pendings = handler.pendingsCount + local failures = handler.failuresCount + local errors = handler.errorsCount + + if successes == 0 then + successString = s('output.success_zero') + elseif successes == 1 then + successString = s('output.success_single') + end + + if failures == 0 then + failureString = s('output.failure_zero') + elseif failures == 1 then + failureString = s('output.failure_single') + end + + if pendings == 0 then + pendingString = s('output.pending_zero') + elseif pendings == 1 then + pendingString = s('output.pending_single') + end + + if errors == 0 then + errorString = s('output.error_zero') + elseif errors == 1 then + errorString = s('output.error_single') + end + + local formattedTime = ('%.6f'):format(ms):gsub('([0-9])0+$', '%1') + + return ansicolors('%{green}' .. successes) .. ' ' .. successString .. ' / ' .. + ansicolors('%{red}' .. failures) .. ' ' .. failureString .. ' / ' .. + ansicolors('%{magenta}' .. errors) .. ' ' .. errorString .. ' / ' .. + ansicolors('%{yellow}' .. pendings) .. ' ' .. pendingString .. ' : ' .. + ansicolors('%{bright}' .. formattedTime) .. ' ' .. s('output.seconds') + end + + handler.testEnd = function(element, parent, status, debug) + if not options.deferPrint then + local string = successDot + + if status == 'pending' then + string = pendingDot + elseif status == 'failure' then + string = failureDot + elseif status == 'error' then + string = errorDot + end + + io.write(string) + io.flush() + end + + return nil, true + end + + handler.suiteStart = function(count, total) + local runString = (total > 1 and '\nRepeating all tests (run %d of %d) . . .\n\n' or '') + io.write(runString:format(count, total)) + io.flush() + end + + handler.suiteEnd = function(count, total) + print('') + print(statusString()) + + for i, pending in pairs(handler.pendings) do + print('') + print(pendingDescription(pending)) + end + + for i, err in pairs(handler.failures) do + print('') + print(failureDescription(err)) + end + + for i, err in pairs(handler.errors) do + print('') + print(failureDescription(err, true)) + end + + return nil, true + end + + handler.error = function(element, parent, message, debug) + io.write(errorDot) + io.flush() + + return nil, true + end + + busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending }) + busted.subscribe({ 'suite', 'start' }, handler.suiteStart) + busted.subscribe({ 'suite', 'end' }, handler.suiteEnd) + busted.subscribe({ 'error', 'file' }, handler.error) + busted.subscribe({ 'failure', 'file' }, handler.error) + busted.subscribe({ 'error', 'describe' }, handler.error) + busted.subscribe({ 'failure', 'describe' }, handler.error) + + return handler +end diff --git a/test/lua/busted/runner.lua b/test/lua/busted/runner.lua new file mode 100644 index 000000000..91ce94e50 --- /dev/null +++ b/test/lua/busted/runner.lua @@ -0,0 +1,400 @@ +-- Busted command-line runner + +local path = require 'pl.path' +local term = require 'term' +local utils = require 'busted.utils' +local loaded = false + +return function(options) + if loaded then return else loaded = true end + + local opt = options or {} + local isBatch = opt.batch + local cli = require 'cliargs' + local busted = require 'busted.core'() + + local configLoader = require 'busted.modules.configuration_loader'() + local helperLoader = require 'busted.modules.helper_loader'() + local outputHandlerLoader = require 'busted.modules.output_handler_loader'() + + local luacov = require 'busted.modules.luacov'() + + local osexit = require 'busted.compatibility'.osexit + + require 'busted'(busted) + + -- Default cli arg values + local defaultOutput = term.isatty(io.stdout) and 'utfTerminal' or 'plainTerminal' + local defaultLoaders = 'lua,moonscript' + local defaultPattern = '_spec' + local defaultSeed = 'os.time()' + local lpathprefix = './src/?.lua;./src/?/?.lua;./src/?/init.lua' + local cpathprefix = path.is_windows and './csrc/?.dll;./csrc/?/?.dll;' or './csrc/?.so;./csrc/?/?.so;' + + local level = 2 + local info = debug.getinfo(level, 'Sf') + local source = info.source + local fileName = source:sub(1,1) == '@' and source:sub(2) or source + + local cliArgsParsed = {} + + local function processOption(key, value, altkey, opt) + if altkey then cliArgsParsed[altkey] = value end + cliArgsParsed[key] = value + return true + end + + local function processNumber(key, value, altkey, opt) + local number = tonumber(value) + if not number then + return nil, 'argument to ' .. opt:gsub('=.*', '') .. ' must be a number' + end + if altkey then cliArgsParsed[altkey] = number end + cliArgsParsed[key] = number + return true + end + + local function processVersion() + -- Return early if asked for the version + print(busted.version) + osexit(0, true) + end + + -- Load up the command-line interface options + cli:set_name(path.basename(fileName)) + cli:add_flag('--version', 'prints the program version and exits', processVersion) + + if isBatch then + cli:optarg('ROOT', 'test script file/folder. Folders will be traversed for any file that matches the --pattern option.', 'spec', 1) + + cli:add_option('-p, --pattern=PATTERN', 'only run test files matching the Lua pattern', defaultPattern, processOption) + end + + cli:add_option('-o, --output=LIBRARY', 'output library to load', defaultOutput, processOption) + cli:add_option('-d, --cwd=cwd', 'path to current working directory', './', processOption) + cli:add_option('-t, --tags=TAGS', 'only run tests with these #tags', nil, processOption) + cli:add_option('--exclude-tags=TAGS', 'do not run tests with these #tags, takes precedence over --tags', nil, processOption) + cli:add_option('--filter=PATTERN', 'only run test names matching the Lua pattern', nil, processOption) + cli:add_option('--filter-out=PATTERN', 'do not run test names matching the Lua pattern, takes precedence over --filter', nil, processOption) + cli:add_option('-m, --lpath=PATH', 'optional path to be prefixed to the Lua module search path', lpathprefix, processOption) + cli:add_option('--cpath=PATH', 'optional path to be prefixed to the Lua C module search path', cpathprefix, processOption) + cli:add_option('-r, --run=RUN', 'config to run from .busted file', nil, processOption) + cli:add_option('--repeat=COUNT', 'run the tests repeatedly', '1', processNumber) + cli:add_option('--seed=SEED', 'random seed value to use for shuffling test order', defaultSeed, processNumber) + cli:add_option('--lang=LANG', 'language for error messages', 'en', processOption) + cli:add_option('--loaders=NAME', 'test file loaders', defaultLoaders, processOption) + cli:add_option('--helper=PATH', 'A helper script that is run before tests', nil, processOption) + + cli:add_option('-Xoutput OPTION', 'pass `OPTION` as an option to the output handler. If `OPTION` contains commas, it is split into multiple options at the commas.', nil, processOption) + cli:add_option('-Xhelper OPTION', 'pass `OPTION` as an option to the helper script. If `OPTION` contains commas, it is split into multiple options at the commas.', nil, processOption) + + cli:add_flag('-c, --coverage', 'do code coverage analysis (requires `LuaCov` to be installed)', processOption) + cli:add_flag('-v, --verbose', 'verbose output of errors', processOption) + cli:add_flag('-s, --enable-sound', 'executes `say` command if available', processOption) + cli:add_flag('-l, --list', 'list the names of all tests instead of running them', processOption) + cli:add_flag('--no-keep-going', 'quit after first error or failure', processOption) + cli:add_flag('--no-recursive', 'do not recurse into subdirectories', processOption) + cli:add_flag('--shuffle', 'randomize file and test order, takes precedence over --sort (--shuffle-test and --shuffle-files)', processOption) + cli:add_flag('--shuffle-files', 'randomize file execution order, takes precedence over --sort-files', processOption) + cli:add_flag('--shuffle-tests', 'randomize test order within a file, takes precedence over --sort-tests', processOption) + cli:add_flag('--sort', 'sort file and test order (--sort-tests and --sort-files)', processOption) + cli:add_flag('--sort-files', 'sort file execution order', processOption) + cli:add_flag('--sort-tests', 'sort test order within a file', processOption) + cli:add_flag('--suppress-pending', 'suppress `pending` test output', processOption) + cli:add_flag('--defer-print', 'defer print to when test suite is complete', processOption) + + -- Parse the cli arguments + local cliArgs = cli:parse(arg) + if not cliArgs then + osexit(1, true) + end + + -- Load current working directory + local fpath = utils.normpath(cliArgs.cwd) + + -- Load busted config file if available + local configFile = { } + local bustedConfigFilePath = utils.normpath(path.join(fpath, '.busted')) + local bustedConfigFile = pcall(function() configFile = loadfile(bustedConfigFilePath)() end) + if bustedConfigFile then + local config, err = configLoader(configFile, cliArgsParsed, cliArgs) + if err then + print('Error: ' .. err) + osexit(1, true) + else + cliArgs = config + end + end + + local tags = {} + local excludeTags = {} + + if cliArgs.tags and cliArgs.tags ~= '' then + tags = utils.split(cliArgs.tags, ',') + end + + if cliArgs['exclude-tags'] and cliArgs['exclude-tags'] ~= '' then + excludeTags = utils.split(cliArgs['exclude-tags'], ',') + end + + -- If coverage arg is passed in, load LuaCovsupport + if cliArgs.coverage then + luacov() + end + + -- Add additional package paths based on lpath and cpath cliArgs + if #cliArgs.lpath > 0 then + lpathprefix = cliArgs.lpath + lpathprefix = lpathprefix:gsub('^%.([/%\\])', fpath .. '%1') + lpathprefix = lpathprefix:gsub(';%.([/%\\])', ';' .. fpath .. '%1') + package.path = (lpathprefix .. ';' .. package.path):gsub(';;',';') + end + + if #cliArgs.cpath > 0 then + cpathprefix = cliArgs.cpath + cpathprefix = cpathprefix:gsub('^%.([/%\\])', fpath .. '%1') + cpathprefix = cpathprefix:gsub(';%.([/%\\])', ';' .. fpath .. '%1') + package.cpath = (cpathprefix .. ';' .. package.cpath):gsub(';;',';') + end + + local loaders = {} + if #cliArgs.loaders > 0 then + string.gsub(cliArgs.loaders, '([^,]+)', function(c) loaders[#loaders+1] = c end) + end + + -- We report an error if the same tag appears in both `options.tags` + -- and `options.excluded_tags` because it does not make sense for the + -- user to tell Busted to include and exclude the same tests at the + -- same time. + for _, excluded in pairs(excludeTags) do + for _, included in pairs(tags) do + if excluded == included then + print('Error: Cannot use --tags and --exclude-tags for the same tags') + osexit(1, true) + end + end + end + + -- watch for test errors + local failures = 0 + local errors = 0 + local quitOnError = cliArgs['no-keep-going'] + + busted.subscribe({ 'error', 'output' }, function(element, parent, message) + print('Error: Cannot load output library: ' .. element.name .. '\n' .. message) + return nil, true + end) + + busted.subscribe({ 'error', 'helper' }, function(element, parent, message) + print('Error: Cannot load helper script: ' .. element.name .. '\n' .. message) + return nil, true + end) + + busted.subscribe({ 'error' }, function(element, parent, message) + errors = errors + 1 + busted.skipAll = quitOnError + return nil, true + end) + + busted.subscribe({ 'failure' }, function(element, parent, message) + if element.descriptor == 'it' then + failures = failures + 1 + else + errors = errors + 1 + end + busted.skipAll = quitOnError + return nil, true + end) + + -- Set up output handler to listen to events + local outputHandlerOptions = { + verbose = cliArgs.verbose, + suppressPending = cliArgs['suppress-pending'], + language = cliArgs.lang, + deferPrint = cliArgs['defer-print'], + arguments = utils.split(cliArgs.Xoutput or '', ',') or {} + } + + local opath = utils.normpath(path.join(fpath, cliArgs.output)) + local outputHandler = outputHandlerLoader(cliArgs.output, opath, outputHandlerOptions, busted, defaultOutput) + outputHandler:subscribe(outputHandlerOptions) + + if cliArgs['enable-sound'] then + require 'busted.outputHandlers.sound'(outputHandlerOptions, busted) + end + + -- Set up randomization options + busted.sort = cliArgs['sort-tests'] or cliArgs.sort + busted.randomize = cliArgs['shuffle-tests'] or cliArgs.shuffle + busted.randomseed = tonumber(cliArgs.seed) or os.time() + + local getFullName = function(name) + local parent = busted.context.get() + local names = { name } + + while parent and (parent.name or parent.descriptor) and + parent.descriptor ~= 'file' do + table.insert(names, 1, parent.name or parent.descriptor) + parent = busted.context.parent(parent) + end + + return table.concat(names, ' ') + end + + local hasTag = function(name, tag) + local found = name:find('#' .. tag) + return (found ~= nil) + end + + local filterExcludeTags = function(name) + for i, tag in pairs(excludeTags) do + if hasTag(name, tag) then + return nil, false + end + end + return nil, true + end + + local filterTags = function(name) + local fullname = getFullName(name) + for i, tag in pairs(tags) do + if hasTag(fullname, tag) then + return nil, true + end + end + return nil, (#tags == 0) + end + + local filterOutNames = function(name) + local found = (getFullName(name):find(cliArgs['filter-out']) ~= nil) + return nil, not found + end + + local filterNames = function(name) + local found = (getFullName(name):find(cliArgs.filter) ~= nil) + return nil, found + end + + local printNameOnly = function(name, fn, trace) + local fullname = getFullName(name) + if trace and trace.what == 'Lua' then + print(trace.short_src .. ':' .. trace.currentline .. ': ' .. fullname) + else + print(fullname) + end + return nil, false + end + + local ignoreAll = function() + return nil, false + end + + local skipOnError = function() + return nil, (failures == 0 and errors == 0) + end + + local applyFilter = function(descriptors, name, fn) + if cliArgs[name] and cliArgs[name] ~= '' then + for _, descriptor in ipairs(descriptors) do + busted.subscribe({ 'register', descriptor }, fn, { priority = 1 }) + end + end + end + + if cliArgs.list then + busted.subscribe({ 'suite', 'start' }, ignoreAll, { priority = 1 }) + busted.subscribe({ 'suite', 'end' }, ignoreAll, { priority = 1 }) + applyFilter({ 'setup', 'teardown', 'before_each', 'after_each' }, 'list', ignoreAll) + applyFilter({ 'it', 'pending' }, 'list', printNameOnly) + end + + applyFilter({ 'setup', 'teardown', 'before_each', 'after_each' }, 'no-keep-going', skipOnError) + applyFilter({ 'file', 'describe', 'it', 'pending' }, 'no-keep-going', skipOnError) + + -- The following filters are applied in reverse order + applyFilter({ 'it', 'pending' } , 'filter' , filterNames ) + applyFilter({ 'describe', 'it', 'pending' }, 'filter-out' , filterOutNames ) + applyFilter({ 'it', 'pending' } , 'tags' , filterTags ) + applyFilter({ 'describe', 'it', 'pending' }, 'exclude-tags', filterExcludeTags) + + -- Set up helper script + if cliArgs.helper and cliArgs.helper ~= '' then + local helperOptions = { + verbose = cliArgs.verbose, + language = cliArgs.lang, + arguments = utils.split(cliArgs.Xhelper or '', ',') or {} + } + + local hpath = utils.normpath(path.join(fpath, cliArgs.helper)) + helperLoader(cliArgs.helper, hpath, helperOptions, busted) + end + + -- Set up test loader options + local testFileLoaderOptions = { + verbose = cliArgs.verbose, + sort = cliArgs['sort-files'] or cliArgs.sort, + shuffle = cliArgs['shuffle-files'] or cliArgs.shuffle, + recursive = not cliArgs['no-recursive'], + seed = busted.randomseed + } + + -- Load test directory + local rootFile = cliArgs.ROOT and utils.normpath(path.join(fpath, cliArgs.ROOT)) or fileName + local pattern = cliArgs.pattern + local testFileLoader = require 'busted.modules.test_file_loader'(busted, loaders, testFileLoaderOptions) + local fileList = testFileLoader(rootFile, pattern) + + if not cliArgs.ROOT then + local ctx = busted.context.get() + local file = busted.context.children(ctx)[1] + getmetatable(file.run).__call = info.func + end + + busted.subscribe({'suite', 'reinitialize'}, function() + local oldctx = busted.context.get() + local children = busted.context.children(oldctx) + + busted.context.clear() + local ctx = busted.context.get() + for k, v in pairs(oldctx) do + ctx[k] = v + end + + for _, child in pairs(children) do + for descriptor, _ in pairs(busted.executors) do + child[descriptor] = nil + end + busted.context.attach(child) + end + + busted.randomseed = tonumber(cliArgs.seed) or os.time() + + return nil, true + end) + + local runs = tonumber(cliArgs['repeat']) or 1 + for i = 1, runs do + if i > 1 then + busted.publish({ 'suite', 'reinitialize' }) + end + + busted.publish({ 'suite', 'start' }, i, runs) + busted.execute() + busted.publish({ 'suite', 'end' }, i, runs) + + if quitOnError and (failures > 0 or errors > 0) then + break + end + end + + busted.publish({ 'exit' }) + + local exit = 0 + if failures > 0 or errors > 0 then + exit = failures + errors + if exit > 255 then + exit = 255 + end + end + osexit(exit, true) +end diff --git a/test/lua/busted/status.lua b/test/lua/busted/status.lua new file mode 100644 index 000000000..c68ce7fc7 --- /dev/null +++ b/test/lua/busted/status.lua @@ -0,0 +1,43 @@ +local function get_status(status) + local smap = { + ['success'] = 'success', + ['pending'] = 'pending', + ['failure'] = 'failure', + ['error'] = 'error', + ['true'] = 'success', + ['false'] = 'failure', + ['nil'] = 'error', + } + return smap[tostring(status)] or 'error' +end + +return function(inital_status) + local objstat = get_status(inital_status) + local obj = { + success = function(self) return (objstat == 'success') end, + pending = function(self) return (objstat == 'pending') end, + failure = function(self) return (objstat == 'failure') end, + error = function(self) return (objstat == 'error') end, + + get = function(self) + return objstat + end, + + set = function(self, status) + objstat = get_status(status) + end, + + update = function(self, status) + -- prefer current failure/error status over new status + status = get_status(status) + if objstat == 'success' or (objstat == 'pending' and status ~= 'success') then + objstat = status + end + end + } + + return setmetatable(obj, { + __index = {}, + __tostring = function(self) return objstat end + }) +end diff --git a/test/lua/busted/utils.lua b/test/lua/busted/utils.lua new file mode 100644 index 000000000..4e02bc036 --- /dev/null +++ b/test/lua/busted/utils.lua @@ -0,0 +1,53 @@ +local path = require 'pl.path' + +math.randomseed(os.time()) + +-- Do not use pl.path.normpath +-- It is broken for paths with leading '../../' +local function normpath(fpath) + if type(fpath) ~= 'string' then + error(fpath .. ' is not a string') + end + local sep = '/' + if path.is_windows then + sep = '\\' + if fpath:match '^\\\\' then -- UNC + return '\\\\' .. normpath(fpath:sub(3)) + end + fpath = fpath:gsub('/','\\') + end + local np_gen1, np_gen2 = '([^SEP]+)SEP(%.%.SEP?)', 'SEP+%.?SEP' + local np_pat1 = np_gen1:gsub('SEP', sep) + local np_pat2 = np_gen2:gsub('SEP', sep) + local k + repeat -- /./ -> / + fpath, k = fpath:gsub(np_pat2, sep) + until k == 0 + repeat -- A/../ -> (empty) + local oldpath = fpath + fpath, k = fpath:gsub(np_pat1, function(d, up) + if d == '..' then return nil end + if d == '.' then return up end + return '' + end) + until k == 0 or oldpath == fpath + if fpath == '' then fpath = '.' end + return fpath +end + +return { + split = require 'pl.utils'.split, + + normpath = normpath, + + shuffle = function(t, seed) + if seed then math.randomseed(seed) end + local n = #t + while n >= 2 do + local k = math.random(n) + t[n], t[k] = t[k], t[n] + n = n - 1 + end + return t + end +} diff --git a/test/lua/rsa.lua b/test/lua/rsa.lua index 83a6a9e45..073062fd1 100644 --- a/test/lua/rsa.lua +++ b/test/lua/rsa.lua @@ -1,39 +1,47 @@ -- Test rsa signing -local pubkey = 'testkey.pub' -local privkey = 'testkey' -local data = 'test.data' -local signature = 'test.sig' +require "busted" () --- Signing test -local rsa_key = rsa_privkey.load(string.format('%s/%s', test_dir, privkey)) +describe("rsa signarture test", function() + local rsa_privkey = require "rspamd_rsa_privkey" + local rsa_pubkey = require "rspamd_rsa_pubkey" + local rsa_signature = require "rspamd_rsa_signature" + local rsa = require "rspamd_rsa" + local pubkey = 'testkey.pub' + local privkey = 'testkey' + local data = 'test.data' + local signature = 'test.sig' -if not rsa_key then - return -1 -end + -- Signing test + local rsa_key = rsa_privkey.load(string.format('%s/%s', test_dir, privkey)) -local rsa_sig = rsa.sign_file(rsa_key, string.format('%s/%s', test_dir, data)) + if not rsa_key then + return -1 + end -if not rsa_sig then - return -1 -end + local rsa_sig = rsa.sign_file(rsa_key, string.format('%s/%s', test_dir, data)) -rsa_sig:save(string.format('%s/%s', test_dir, signature), true) + if not rsa_sig then + return -1 + end --- Verifying test -rsa_key = rsa_pubkey.load(string.format('%s/%s', test_dir, pubkey)) + rsa_sig:save(string.format('%s/%s', test_dir, signature), true) -if not rsa_key then - return -1 -end + -- Verifying test + rsa_key = rsa_pubkey.load(string.format('%s/%s', test_dir, pubkey)) -rsa_sig = rsa_signature.load(string.format('%s/%s', test_dir, signature)) + if not rsa_key then + return -1 + end -if not rsa_sig then - return -1 -end + rsa_sig = rsa_signature.load(string.format('%s/%s', test_dir, signature)) -if not rsa.verify_file(rsa_key, rsa_sig, string.format('%s/%s', test_dir, data)) then - return -1 -end + if not rsa_sig then + return -1 + end + if not rsa.verify_file(rsa_key, rsa_sig, string.format('%s/%s', test_dir, data)) then + return -1 + end + +end) diff --git a/test/rspamd_lua_test.c b/test/rspamd_lua_test.c index 46303b356..da9405fa5 100644 --- a/test/rspamd_lua_test.c +++ b/test/rspamd_lua_test.c @@ -37,6 +37,7 @@ rspamd_lua_test_func (void) glob_t globbuf; gchar *pattern; guint i, len; + struct stat st; msg_info ("Starting lua tests"); @@ -54,6 +55,15 @@ rspamd_lua_test_func (void) for (i = 0; i < globbuf.gl_pathc; i++) { lua_file = globbuf.gl_pathv[i]; + if (stat (lua_file, &st) == -1 || !S_ISREG (st.st_mode)) { + continue; + } + + if (strstr (lua_file, "busted") != NULL) { + /* Skip busted code itself */ + continue; + } + if (luaL_loadfile (L, lua_file) != 0) { msg_err ("load test from %s failed", lua_file); g_assert (0);