add gs
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
--[[
|
||||
Contains the logic to run a test plan and gather test results from it.
|
||||
|
||||
TestRunner accepts a TestPlan object, executes the planned tests, and
|
||||
produces a TestResults object. While the tests are running, the system's
|
||||
state is contained inside a TestSession object.
|
||||
]]
|
||||
|
||||
local Expectation = require(script.Parent.Expectation)
|
||||
local TestEnum = require(script.Parent.TestEnum)
|
||||
local TestSession = require(script.Parent.TestSession)
|
||||
local Stack = require(script.Parent.Stack)
|
||||
|
||||
local RUNNING_GLOBAL = "__TESTEZ_RUNNING_TEST__"
|
||||
|
||||
local TestRunner = {
|
||||
environment = {}
|
||||
}
|
||||
|
||||
function TestRunner.environment.expect(...)
|
||||
return Expectation.new(...)
|
||||
end
|
||||
|
||||
--[[
|
||||
Runs the given TestPlan and returns a TestResults object representing the
|
||||
results of the run.
|
||||
]]
|
||||
function TestRunner.runPlan(plan)
|
||||
local session = TestSession.new(plan)
|
||||
local tryStack = Stack.new()
|
||||
|
||||
local exclusiveNodes = plan:findNodes(function(node)
|
||||
return node.modifier == TestEnum.NodeModifier.Focus
|
||||
end)
|
||||
|
||||
session.hasFocusNodes = #exclusiveNodes > 0
|
||||
|
||||
TestRunner.runPlanNode(session, plan, tryStack)
|
||||
|
||||
return session:finalize()
|
||||
end
|
||||
|
||||
--[[
|
||||
Run the given test plan node and its descendants, using the given test
|
||||
session to store all of the results.
|
||||
]]
|
||||
function TestRunner.runPlanNode(session, planNode, tryStack, noXpcall)
|
||||
for _, childPlanNode in ipairs(planNode.children) do
|
||||
local childResultNode = session:pushNode(childPlanNode)
|
||||
|
||||
if childPlanNode.type == TestEnum.NodeType.It then
|
||||
if session:shouldSkip() then
|
||||
childResultNode.status = TestEnum.TestStatus.Skipped
|
||||
else
|
||||
if tryStack:size() > 0 and tryStack:getBack().isOk == false then
|
||||
childResultNode.status = TestEnum.TestStatus.Failure
|
||||
table.insert(childResultNode.errors,
|
||||
string.format("%q failed without trying, because test case %q failed",
|
||||
childPlanNode.phrase, tryStack:getBack().failedNode.phrase))
|
||||
else
|
||||
-- Errors can be set either via `error` propagating upwards or
|
||||
-- by a test calling fail([message]).
|
||||
local success = true
|
||||
local errorMessage
|
||||
|
||||
local testEnvironment = getfenv(childPlanNode.callback)
|
||||
|
||||
for key, value in pairs(TestRunner.environment) do
|
||||
testEnvironment[key] = value
|
||||
end
|
||||
|
||||
testEnvironment.fail = function(message)
|
||||
if message == nil then
|
||||
message = "fail() was called."
|
||||
end
|
||||
|
||||
success = false
|
||||
errorMessage = message .. "\n" .. debug.traceback()
|
||||
end
|
||||
|
||||
-- We prefer xpcall, but yielding doesn't work from xpcall.
|
||||
-- As a workaround, you can mark nodes as "not xpcallable"
|
||||
local call = noXpcall and pcall or xpcall
|
||||
|
||||
-- Any code can check RUNNING_GLOBAL to fork behavior based on
|
||||
-- whether a test is running. We use this to avoid accessing
|
||||
-- protected APIs; it's a workaround that will go away someday.
|
||||
_G[RUNNING_GLOBAL] = true
|
||||
|
||||
local nodeSuccess, nodeResult = call(childPlanNode.callback, function(message)
|
||||
return message .. "\n" .. debug.traceback()
|
||||
end)
|
||||
|
||||
_G[RUNNING_GLOBAL] = nil
|
||||
|
||||
-- If a node threw an error, we prefer to use that message over
|
||||
-- one created by fail() if it was set.
|
||||
if not nodeSuccess then
|
||||
success = false
|
||||
errorMessage = nodeResult
|
||||
end
|
||||
|
||||
if success then
|
||||
childResultNode.status = TestEnum.TestStatus.Success
|
||||
else
|
||||
childResultNode.status = TestEnum.TestStatus.Failure
|
||||
table.insert(childResultNode.errors, errorMessage)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif childPlanNode.type == TestEnum.NodeType.Describe or childPlanNode.type == TestEnum.NodeType.Try then
|
||||
if childPlanNode.type == TestEnum.NodeType.Try then tryStack:push({isOk = true, failedNode = nil}) end
|
||||
TestRunner.runPlanNode(session, childPlanNode, tryStack, childPlanNode.HACK_NO_XPCALL)
|
||||
if childPlanNode.type == TestEnum.NodeType.Try then tryStack:pop() end
|
||||
|
||||
local status = TestEnum.TestStatus.Success
|
||||
|
||||
-- Did we have an error trying build a test plan?
|
||||
if childPlanNode.loadError then
|
||||
status = TestEnum.TestStatus.Failure
|
||||
|
||||
local message = "Error during planning: " .. childPlanNode.loadError
|
||||
|
||||
table.insert(childResultNode.errors, message)
|
||||
else
|
||||
local skipped = true
|
||||
|
||||
-- If all children were skipped, then we were skipped
|
||||
-- If any child failed, then we failed!
|
||||
for _, child in ipairs(childResultNode.children) do
|
||||
if child.status ~= TestEnum.TestStatus.Skipped then
|
||||
skipped = false
|
||||
|
||||
if child.status == TestEnum.TestStatus.Failure then
|
||||
status = TestEnum.TestStatus.Failure
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if skipped then
|
||||
status = TestEnum.TestStatus.Skipped
|
||||
end
|
||||
end
|
||||
|
||||
childResultNode.status = status
|
||||
end
|
||||
|
||||
session:popNode()
|
||||
end
|
||||
end
|
||||
|
||||
return TestRunner
|
||||
Reference in New Issue
Block a user