Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

basic API test suite #1974

Merged
merged 57 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
19df804
0.1
ellraiser Oct 4, 2023
08467cb
Update readme.md
ellraiser Oct 4, 2023
e03b2db
0.2
ellraiser Oct 5, 2023
1692f7f
Test MacOS Testing workflow
ellraiser Oct 5, 2023
db600b7
output to src
ellraiser Oct 5, 2023
fae3b6f
fix xml format
ellraiser Oct 5, 2023
6c29497
skipped format
ellraiser Oct 5, 2023
a513320
prevent empty <failure/> tag
ellraiser Oct 6, 2023
ac5f97e
Update main.yml
ellraiser Oct 6, 2023
04293d3
use love-test-report action
ellraiser Oct 6, 2023
6a0ff06
fixed test.filesystem.openFile
ellraiser Oct 6, 2023
5ac5438
try lovec instead
ellraiser Oct 6, 2023
a620910
add renderer alts.
ellraiser Oct 6, 2023
8f10ba9
check flag change
ellraiser Oct 6, 2023
e1948f6
added all graphics 'state' tests
ellraiser Oct 8, 2023
c7c8f25
added objects module placeholders
ellraiser Oct 8, 2023
add23a4
fix mesa install
ellraiser Oct 8, 2023
f477b80
finish graphics module + HTML image comparison
ellraiser Oct 9, 2023
09d0ace
finished a bunch of modules
ellraiser Oct 14, 2023
658c75b
latest from love-test
ellraiser Nov 14, 2023
1371266
update ci run
ellraiser Nov 14, 2023
a9a5507
add missing linux lib req
ellraiser Nov 14, 2023
75c2667
Update main.yml
ellraiser Nov 14, 2023
ef39fb3
Merge branch '12.0-development' into 12.0-testsuite
ellraiser Nov 14, 2023
c682ba1
use 12.0dev workflow
ellraiser Nov 14, 2023
d23c396
split workflow
ellraiser Nov 14, 2023
58fa1c8
test the test workflow
ellraiser Nov 14, 2023
0d338b0
workflow changes #1
ellraiser Nov 14, 2023
5155a96
fix upload paths
ellraiser Nov 14, 2023
38ef55e
add arch+compat to win test output
ellraiser Nov 14, 2023
46d4567
invalid lib linux
ellraiser Nov 14, 2023
b1fe623
turn off vulkan tests for now
ellraiser Nov 14, 2023
333169e
fix alsoft path
ellraiser Nov 14, 2023
c89e793
alsoft relative to checkout
ellraiser Nov 14, 2023
357b005
fix test.audio.Source
ellraiser Nov 14, 2023
af4c50a
skip test.audio.RecordingDevice on CI
ellraiser Nov 15, 2023
ad44eb4
more graphics class tests
ellraiser Nov 15, 2023
038b36a
add renderer to report md
ellraiser Nov 15, 2023
0ef417f
fix test.graphics.Canvas
ellraiser Nov 15, 2023
1323bb1
ignore setMipmapFilter on opengl ES
ellraiser Nov 15, 2023
8124804
more graphics + some physics tests
ellraiser Nov 16, 2023
adc6fbe
added test.physics.Joint
ellraiser Nov 16, 2023
37961f9
floor joint pos
ellraiser Nov 16, 2023
d130758
add graphics pixel comparison tests
ellraiser Nov 16, 2023
a8c714b
change to exact rgba comparison for visual tests
ellraiser Nov 17, 2023
89119c5
add runner exceptions to readme
ellraiser Nov 17, 2023
51c2aa8
specify write for action permission
ellraiser Nov 17, 2023
a977ad2
revert
ellraiser Nov 17, 2023
eba8ccb
Update main.yml
ellraiser Nov 17, 2023
1ef183c
Update main.yml
ellraiser Nov 17, 2023
825e466
finished obj tests
ellraiser Nov 20, 2023
63d303a
make workflow fail if tests fail
ellraiser Nov 22, 2023
f846d4a
review changes
ellraiser Nov 23, 2023
c5fb1a7
Merge remote-tracking branch 'upstream/12.0-development' into 12.0-te…
ellraiser Nov 23, 2023
d6c7796
fix some ranges for runners
ellraiser Nov 23, 2023
cc4b618
Update physics.lua
ellraiser Nov 23, 2023
13c5f7a
Merge remote-tracking branch 'upstream/12.0-development' into 12.0-te…
ellraiser Nov 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
359 changes: 359 additions & 0 deletions testing/classes/TestMethod.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,359 @@
-- @class - TestMethod
-- @desc - used to run a specific method from a module's /test/ suite
-- each assertion is tracked and then printed to output
TestMethod = {


-- @method - TestMethod:new()
-- @desc - create a new TestMethod object
-- @param {string} method - string of method name to run
-- @param {TestMethod} testmethod - parent testmethod this test belongs to
-- @return {table} - returns the new Test object
new = function(self, method, testmodule)
local test = {
testmodule = testmodule,
method = method,
asserts = {},
start = love.timer.getTime(),
finish = 0,
count = 0,
passed = false,
skipped = false,
skipreason = '',
fatal = '',
message = nil,
result = {},
colors = {
red = {1, 0, 0, 1},
green = {0, 1, 0, 1},
blue = {0, 0, 1, 1},
black = {0, 0, 0, 1},
white = {1, 1, 1, 1}
}
}
setmetatable(test, self)
self.__index = self
return test
end,


-- @method - TestMethod:assertEquals()
-- @desc - used to assert two values are equals
-- @param {any} expected - expected value of the test
-- @param {any} actual - actual value of the test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertEquals = function(self, expected, actual, label)
self.count = self.count + 1
table.insert(self.asserts, {
key = 'assert #' .. tostring(self.count),
passed = expected == actual,
message = 'expected \'' .. tostring(expected) .. '\' got \'' ..
tostring(actual) .. '\'',
test = label
})
end,


-- @method - TestMethod:assertPixels()
-- @desc - checks a list of coloured pixels agaisnt given imgdata
-- @param {ImageData} imgdata - image data to check
-- @param {table} pixels - map of colors to list of pixel coords, i.e.
-- { blue = { {1, 1}, {2, 2}, {3, 4} } }
-- @return {nil}
assertPixels = function(self, imgdata, pixels, label)
for i, v in pairs(pixels) do
local col = self.colors[i]
local pixels = v
for p=1,#pixels do
local coord = pixels[p]
local tr, tg, tb, ta = imgdata:getPixel(coord[1], coord[2])
local compare_id = tostring(coord[1]) .. ',' .. tostring(coord[2])
-- @TODO add some sort pixel tolerance to the coords
self:assertEquals(col[1], tr, 'check pixel r for ' .. i .. ' at ' .. compare_id .. '(' .. label .. ')')
self:assertEquals(col[2], tg, 'check pixel g for ' .. i .. ' at ' .. compare_id .. '(' .. label .. ')')
self:assertEquals(col[3], tb, 'check pixel b for ' .. i .. ' at ' .. compare_id .. '(' .. label .. ')')
self:assertEquals(col[4], ta, 'check pixel a for ' .. i .. ' at ' .. compare_id .. '(' .. label .. ')')
end
end
end,


-- @method - TestMethod:assertNotEquals()
-- @desc - used to assert two values are not equal
-- @param {any} expected - expected value of the test
-- @param {any} actual - actual value of the test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertNotEquals = function(self, expected, actual, label)
self.count = self.count + 1
table.insert(self.asserts, {
key = 'assert #' .. tostring(self.count),
passed = expected ~= actual,
message = 'avoiding \'' .. tostring(expected) .. '\' got \'' ..
tostring(actual) .. '\'',
test = label
})
end,


-- @method - TestMethod:assertRange()
-- @desc - used to check a value is within an expected range
-- @param {number} actual - actual value of the test
-- @param {number} min - minimum value the actual should be >= to
-- @param {number} max - maximum value the actual should be <= to
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertRange = function(self, actual, min, max, label)
self.count = self.count + 1
table.insert(self.asserts, {
key = 'assert #' .. tostring(self.count),
passed = actual >= min and actual <= max,
message = 'value \'' .. tostring(actual) .. '\' out of range \'' ..
tostring(min) .. '-' .. tostring(max) .. '\'',
test = label
})
end,


-- @method - TestMethod:assertMatch()
-- @desc - used to check a value is within a list of values
-- @param {number} list - list of valid values for the test
-- @param {number} actual - actual value of the test to check is in the list
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertMatch = function(self, list, actual, label)
self.count = self.count + 1
local found = false
for l=1,#list do
if list[l] == actual then found = true end;
end
table.insert(self.asserts, {
key = 'assert #' .. tostring(self.count),
passed = found == true,
message = 'value \'' .. tostring(actual) .. '\' not found in \'' ..
table.concat(list, ',') .. '\'',
test = label
})
end,


-- @method - TestMethod:assertGreaterEqual()
-- @desc - used to check a value is >= than a certain target value
-- @param {any} target - value to check the test agaisnt
-- @param {any} actual - actual value of the test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertGreaterEqual = function(self, target, actual, label)
self.count = self.count + 1
local passing = false
if target ~= nil and actual ~= nil then
passing = actual >= target
end
table.insert(self.asserts, {
key = 'assert #' .. tostring(self.count),
passed = passing,
message = 'value \'' .. tostring(actual) .. '\' not >= \'' ..
tostring(target) .. '\'',
test = label
})
end,


-- @method - TestMethod:assertLessEqual()
-- @desc - used to check a value is <= than a certain target value
-- @param {any} target - value to check the test agaisnt
-- @param {any} actual - actual value of the test
-- @param {string} label - label for this test to use in exports
-- @return {nil}
assertLessEqual = function(self, target, actual, label)
self.count = self.count + 1
local passing = false
if target ~= nil and actual ~= nil then
passing = actual <= target
end
table.insert(self.asserts, {
key = 'assert #' .. tostring(self.count),
passed = passing,
message = 'value \'' .. tostring(actual) .. '\' not <= \'' ..
tostring(target) .. '\'',
test = label
})
end,


-- @method - TestMethod:assertObject()
-- @desc - used to check a table is a love object, this runs 3 seperate
-- tests to check table has the basic properties of an object
-- @note - actual object functionality tests are done in the objects module
-- @param {table} obj - table to check is a valid love object
-- @return {nil}
assertObject = function(self, obj)
self:assertNotEquals(nil, obj, 'check not nill')
self:assertEquals('userdata', type(obj), 'check is userdata')
if obj ~= nil then
self:assertNotEquals(nil, obj:type(), 'check has :type()')
end
end,



-- @method - TestMethod:skipTest()
-- @desc - used to mark this test as skipped for a specific reason
-- @param {string} reason - reason why method is being skipped
-- @return {nil}
skipTest = function(self, reason)
self.skipped = true
self.skipreason = reason
end,


-- @method - TestMethod:evaluateTest()
-- @desc - evaluates the results of all assertions for a final restult
-- @return {nil}
evaluateTest = function(self)
local failure = ''
local failures = 0
for a=1,#self.asserts do
-- @TODO just return first failed assertion msg? or all?
-- currently just shows the first assert that failed
if self.asserts[a].passed == false and self.skipped == false then
if failure == '' then failure = self.asserts[a] end
failures = failures + 1
end
end
if self.fatal ~= '' then failure = self.fatal end
local passed = tostring(#self.asserts - failures)
local total = '(' .. passed .. '/' .. tostring(#self.asserts) .. ')'
if self.skipped == true then
self.testmodule.skipped = self.testmodule.skipped + 1
love.test.totals[3] = love.test.totals[3] + 1
self.result = {
total = '',
result = "SKIP",
passed = false,
message = '(0/0) - method skipped [' .. self.skipreason .. ']'
}
else
if failure == '' and #self.asserts > 0 then
self.passed = true
self.testmodule.passed = self.testmodule.passed + 1
love.test.totals[1] = love.test.totals[1] + 1
self.result = {
total = total,
result = 'PASS',
passed = true,
message = nil
}
else
self.passed = false
self.testmodule.failed = self.testmodule.failed + 1
love.test.totals[2] = love.test.totals[2] + 1
if #self.asserts == 0 then
local msg = 'no asserts defined'
if self.fatal ~= '' then msg = self.fatal end
self.result = {
total = total,
result = 'FAIL',
passed = false,
key = 'test',
message = msg
}
else
local key = failure['key']
if failure['test'] ~= nil then
key = key .. ' [' .. failure['test'] .. ']'
end
self.result = {
total = total,
result = 'FAIL',
passed = false,
key = key,
message = failure['message']
}
end
end
end
self:printResult()
end,


-- @method - TestMethod:printResult()
-- @desc - prints the result of the test to the console as well as appends
-- the XML + HTML for the test to the testsuite output
-- @return {nil}
printResult = function(self)

-- get total timestamp
-- @TODO make nicer, just need a 3DP ms value
self.finish = love.timer.getTime() - self.start
love.test.time = love.test.time + self.finish
self.testmodule.time = self.testmodule.time + self.finish
local endtime = tostring(math.floor((love.timer.getTime() - self.start)*1000))
if string.len(endtime) == 1 then endtime = ' ' .. endtime end
if string.len(endtime) == 2 then endtime = ' ' .. endtime end
if string.len(endtime) == 3 then endtime = ' ' .. endtime end

-- get failure/skip message for output (if any)
local failure = ''
local output = ''
if self.passed == false and self.skipped == false then
failure = '\t\t\t<failure message="' .. self.result.key .. ' ' ..
self.result.message .. '"></failure>\n'
output = self.result.key .. ' ' .. self.result.message
end
if output == '' and self.skipped == true then
output = self.skipreason
end

-- append XML for the test class result
self.testmodule.xml = self.testmodule.xml .. '\t\t<testclass classname="' ..
self.method .. '" name="' .. self.method ..
'" time="' .. tostring(self.finish*1000) .. '">\n' ..
failure .. '\t\t</testclass>\n'

-- unused currently, adds a preview image for certain graphics methods to the output
local preview = ''
-- if self.testmodule.module == 'graphics' then
-- local filename = 'love_test_graphics_rectangle'
-- preview = '<div class="preview">' .. '<img src="' .. filename .. '_expected.png"/><p>Expected</p></div>' ..
-- '<div class="preview"><img src="' .. filename .. '_actual.png"/><p>Actual</p></div>'
-- end

-- append HTML for the test class result
local status = '🔴'
local cls = 'red'
if self.passed == true then status = '🟢'; cls = '' end
if self.skipped == true then status = '🟡'; cls = '' end
self.testmodule.html = self.testmodule.html ..
'<tr class=" ' .. cls .. '">' ..
'<td>' .. status .. '</td>' ..
'<td>' .. self.method .. '</td>' ..
'<td>' .. tostring(self.finish*1000) .. 'ms</td>' ..
'<td>' .. output .. preview .. '</td>' ..
'</tr>'

-- add message if assert failed
local msg = ''
if self.result.message ~= nil and self.skipped == false then
msg = ' - ' .. self.result.key ..
' failed - (' .. self.result.message .. ')'
end
if self.skipped == true then
msg = self.result.message
end

-- log final test result to console
-- i know its hacky but its neat soz
local tested = 'love.' .. self.testmodule.module .. '.' .. self.method .. '()'
local matching = string.sub(self.testmodule.spacer, string.len(tested), 40)
self.testmodule:log(
self.testmodule.colors[self.result.result],
' ' .. tested .. matching,
' ==> ' .. self.result.result .. ' - ' .. endtime .. 'ms ' ..
self.result.total .. msg
)
end


}
slime73 marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading