129 lines
3.4 KiB
Lua
129 lines
3.4 KiB
Lua
local test, testAssert, testError
|
|
|
|
-- Create a node representing a test section
|
|
local function createNode (parent, description, process)
|
|
return setmetatable({
|
|
parent = parent,
|
|
description = description,
|
|
process = process,
|
|
nodes = {},
|
|
activeNodeIndex = 1,
|
|
currentNodeIndex = 0,
|
|
assert = testAssert,
|
|
error = testError,
|
|
}, { __call = test })
|
|
end
|
|
|
|
-- Run a node
|
|
local function runNode (node)
|
|
node.currentNodeIndex = 0
|
|
return node:process()
|
|
end
|
|
|
|
-- Get the root node for a given node
|
|
local function getRootNode (node)
|
|
local parent = node.parent
|
|
return parent and getRootNode(parent) or node
|
|
end
|
|
|
|
-- Update the active child node of the given node
|
|
local function updateActiveNode (node, description, process)
|
|
local activeNodeIndex = node.activeNodeIndex
|
|
local nodes = node.nodes
|
|
local activeNode = nodes[activeNodeIndex]
|
|
|
|
if not activeNode then
|
|
activeNode = createNode(node, description, process)
|
|
nodes[activeNodeIndex] = activeNode
|
|
else
|
|
activeNode.process = process
|
|
end
|
|
|
|
getRootNode(node).lastActiveLeaf = activeNode
|
|
|
|
return activeNode
|
|
end
|
|
|
|
-- Run the active child node of the given node
|
|
local function runActiveNode (node, description, process)
|
|
local activeNode = updateActiveNode(node, description, process)
|
|
return runNode(activeNode)
|
|
end
|
|
|
|
-- Get ancestors of a node, including the node
|
|
local function getAncestors (node)
|
|
local ancestors = { node }
|
|
for ancestor in function () return node.parent end do
|
|
ancestors[#ancestors + 1] = ancestor
|
|
node = ancestor
|
|
end
|
|
return ancestors
|
|
end
|
|
|
|
-- Print a message describing one execution path in the test scenario
|
|
local function printScenario (node)
|
|
local ancestors = getAncestors(node)
|
|
for i = #ancestors, 1, -1 do
|
|
io.stderr:write(ancestors[i].description or '')
|
|
io.stderr:write('\n')
|
|
end
|
|
end
|
|
|
|
-- Print a message and stop the test scenario when an assertion fails
|
|
local function failAssert (node, description, message)
|
|
io.stderr:write(message or '')
|
|
io.stderr:write('\n\n')
|
|
printScenario(node)
|
|
io.stderr:write(description or '')
|
|
io.stderr:write('\n\n')
|
|
error(message or '', 2)
|
|
end
|
|
|
|
-- Create a branch node for a test scenario
|
|
test = function (node, description, process)
|
|
node.currentNodeIndex = node.currentNodeIndex + 1
|
|
if node.currentNodeIndex == node.activeNodeIndex then
|
|
return runActiveNode(node, description, process)
|
|
end
|
|
end
|
|
|
|
-- Test an assertion
|
|
testAssert = function (self, value, description)
|
|
if not value then
|
|
return failAssert(self, description, 'Test failed: assertion failed')
|
|
end
|
|
return value
|
|
end
|
|
|
|
-- Expect function f to fail
|
|
testError = function (self, f, description)
|
|
if pcall(f) then
|
|
return failAssert(self, description, 'Test failed: expected error')
|
|
end
|
|
end
|
|
|
|
-- Create the root node for a test scenario
|
|
local function T (description, process)
|
|
local root = createNode(nil, description, process)
|
|
|
|
runNode(root)
|
|
while root.activeNodeIndex <= #root.nodes do
|
|
local lastActiveBranch = root.lastActiveLeaf.parent
|
|
lastActiveBranch.activeNodeIndex = lastActiveBranch.activeNodeIndex + 1
|
|
runNode(root)
|
|
end
|
|
|
|
return root
|
|
end
|
|
|
|
-- Run any other files passed from CLI.
|
|
if arg and arg[0] and arg[0]:gmatch('test.lua') then
|
|
_G.T = T
|
|
for i = 1, #arg do
|
|
dofile(arg[i])
|
|
end
|
|
_G.T = nil
|
|
end
|
|
|
|
return T
|