Skip to content

Commit

Permalink
Tests: Add assertSuccessBefore, assertThrowsMessage and assertThrows. (
Browse files Browse the repository at this point in the history
…#3975)

Add the following new assertions for tests:
* assertSuccessBefore: Test the given method succeeds before a certain amount of time.
* assertThrowsMessage: Test the given method throws an exception with the provided error message.
* assertThrows: Test the given method throws any exception.

Add test_assertions test for internal tests.
  • Loading branch information
saurtron authored Nov 28, 2024
1 parent 5d4a63e commit 20943cb
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 0 deletions.
67 changes: 67 additions & 0 deletions common/testing/assertions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,75 @@ local function assertTablesEqual(table1, table2, margin, visited, path)
end
end

local depth = 0

-- Assert the given fn returns trueish before seconds.
--
-- fn will be called every 'frames' game frames.
-- errorMsg can be set to customize the error message preface.
local function assertSuccessBefore(seconds, frames, fn, errorMsg, depthOffset)
local iters = math.ceil((seconds*30)/frames)
for i=1, iters do
-- dangerous to set depth here since fn() can fail.
-- no pcall since SyncedProxy and SyncedRun wouldn't work.
local res = fn()
if res then
return
end
Test.waitFrames(frames)
end
depthOffset = (depthOffset or 0) + 2 + depth
-- Error instead of assert to get a proper error line position
error(errorMsg or "assertSuccessBefore: didn't succeed before " .. tostring(seconds) .. " seconds", depthOffset)
end


-- Assert the given function throws an exception
--
-- Note it's better to use assertThrowsMessage since otherwise you might be catching an
-- unexpected error. Still, this is provided for convenience.
-- Can't be used with SyncedProxy or SyncedRun for now as they don't work inside pcall.
local function assertThrows(fn, errorMsg, depthOffset)
depth = depth + 1
local isOk = pcall(fn)
depth = depth - 1
if isOk then
depthOffset = (depthOffset or 0) + 2 + depth
error(errorMsg or "assertThrows", depthOffset)
end
end


-- Assert the given function throws an exception with a specific error message
--
-- Can't be used with SyncedProxy or SyncedRun for now as they don't work inside pcall.
local function assertThrowsMessage(fn, testMsg, errorMsg, depthOffset)
depthOffset = depthOffset or 0
depth = depth + 1
local isOk, result = pcall(fn)
depth = depth - 1
depthOffset = (depthOffset or 0) + 2 + depth
if isOk then
error(errorMsg or "assertThrowsMessage: didn't throw", depthOffset)
end
if not isOk and not type(result) == "string" then
error(errorMsg or "assertThrowsMessage: error was not a string", depthOffset)
end
-- split "standard" error format
-- it's in the form: [string "LuaUI/Widgets/tests/selftests/test_assertions.lua"]:17: error2
local match = result
local errorIndex = result:match'^%[string "[%p%a%s]*%"]:[%d]+:().*'
if errorIndex and errorIndex > 0 then
match = result:sub(errorIndex + 1)
end
if match ~= testMsg then
error(errorMsg or "assertThrowsMessage: error was not '" .. tostring(testMsg) .. "': '" .. tostring(match) .. "'", depthOffset)
end
end

return {
assertTablesEqual = assertTablesEqual,
assertSuccessBefore = assertSuccessBefore,
assertThrows = assertThrows,
assertThrowsMessage = assertThrowsMessage,
}
159 changes: 159 additions & 0 deletions luaui/Widgets/Tests/selftests/test_assertions.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@

function sanityChecks()
-- Just to make sure some standard methods used here work as expected.
Spring.GiveOrderToUnit(2, CMD.FIRE_STATE, {0}, {})
SyncedProxy.Spring.ValidUnitID(20)
local res, err = pcall(function()
Spring.GiveOrderToUnit(2, CMD.FIRE_STATE, {0}, {})
end)
assert(err ~= "attempt to yield across metamethod/C-call boundary")
end

function failingTests()
-- All of these fail due to error "attempt to yield across metamethod/C-call boundary"
-- This is something to do with how the test system is structured.
local res, err = pcall(function()
SyncedRun(function()
Spring.ValidUnitID(20)
end)
end)
assert(err ~= "attempt to yield across metamethod/C-call boundary")

local res, err = pcall(function()
SyncedProxy.Spring.ValidUnitID(20)
end)
assert(err ~= "attempt to yield across metamethod/C-call boundary")

local res, bla = pcall(function()
Test.waitUntil(function()
return true
end)
end)
assert(err ~= "attempt to yield across metamethod/C-call boundary")

assertThrowsMessage(function()
SyncedProxy.Spring.ValidUnitID(20)
error("error")
end, "error")

assertThrowsMessage(function()
SyncedProxy.Spring.GiveOrderToUnit(20, CMD.FIRE_STATE, {0}, {})
end, "[GiveOrderToUnit] invalid unitID")

assertThrowsMessage(function()
assertSuccessBefore(1, 10, function() return false end, "error")
end, "error")

assertThrowsMessage(function()
Test.waitUntil(function()
error("error")
end, 1)
end, "error")

-- this ones can get stuck forever:
--Test.waitUntil(function()
-- SyncedRun(function()
-- Spring.ValidUnitID(20)
-- end)
--end, 1)
--Test.waitUntil(function()
-- SyncedProxy.Spring.ValidUnitID(20)
--end, 1)
--
end

function failingWhileSucceedingTests()
-- these ones are actually failing even when they don't throw exceptions,
-- it's because assertThrows is catching the exception, it's just not
-- the one we want.

assertThrows(function()
-- this test should fail since ValidUnitID doesn't throw exceptions.
SyncedProxy.Spring.ValidUnitID(20)
end)
assertThrows(function()
-- this actually throws an exception, but due to something else.
SyncedProxy.Spring.GiveOrderToUnit(2, CMD.FIRE_STATE, {0}, {})
end)
end

function testWaitUntil()
Test.waitUntil(function()
return true
end)
end

function testAssertSuccessBefore()
-- test the method succeeding
assertSuccessBefore(1, 10, function() return true end)
assertSuccessBefore(1, 10, function()
-- SyncedProxy works here
SyncedProxy.Spring.ValidUnitID(20)
return true
end)
-- test the method never succeeding in the alloted time
assertThrowsMessage(function()
assertSuccessBefore(1, 10, function() error("error") end)
end, "error")
end

function testAssertThrows()
-- test detecting an exception
assertThrows(function()
error("error")
end)
-- test detecting an assertion
assertThrows(function()
assert(false)
end)
-- test assert throws an error when the function doesn't
assertThrows(function()
assertThrows(function() return true end)
end)
assertThrows(function()
Spring.ValidUnitID(20)
error("error")
end)
end

function testAssertThrowsMessage()
-- test throwing a specific message
assertThrowsMessage(function()
error("error")
end, "error")
-- test assertion throwing a specific message
assertThrowsMessage(function()
assert(false, "error")
end, "error")
-- test if works when the error has brackets
assertThrowsMessage(function()
error("[error]")
end, "[error]")
-- test it works when error ends the same as the error
assertThrows(function()
assertThrowsMessage(function() error("another error") end, "error")
end)
-- test our splitting method works when the error has the same pattern
assertThrowsMessage(function()
error('[string "LuaUI/Widgets/tests/selftests/test_assertions.lua"]:17: error2')
end, '[string "LuaUI/Widgets/tests/selftests/test_assertions.lua"]:17: error2')

-- test other methods setting the error message
assertThrowsMessage(function()
assert(false, "error")
end, "error")
assertThrowsMessage(function()
Spring.ValidUnitID(20)
error("error")
end, "error")
end

function test()
sanityChecks()
testWaitUntil()
testAssertThrows()
testAssertSuccessBefore()
testAssertThrowsMessage()
--failingTests()
failingWhileSucceedingTests()
end

0 comments on commit 20943cb

Please sign in to comment.