diff --git a/testing/classes/TestMethod.lua b/testing/classes/TestMethod.lua
new file mode 100644
index 000000000..b0c763082
--- /dev/null
+++ b/testing/classes/TestMethod.lua
@@ -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\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\n' ..
+ failure .. '\t\t\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 = '
' .. '
Expected
' ..
+ -- 'Actual
'
+ -- 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 ..
+ '' ..
+ '' .. status .. ' | ' ..
+ '' .. self.method .. ' | ' ..
+ '' .. tostring(self.finish*1000) .. 'ms | ' ..
+ '' .. output .. preview .. ' | ' ..
+ '
'
+
+ -- 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
+
+
+}
\ No newline at end of file
diff --git a/testing/classes/TestModule.lua b/testing/classes/TestModule.lua
new file mode 100644
index 000000000..379aac360
--- /dev/null
+++ b/testing/classes/TestModule.lua
@@ -0,0 +1,114 @@
+-- @class - TestModule
+-- @desc - used to run tests for a given module, each test method will spawn
+-- a love.test.Test object
+TestModule = {
+
+
+ -- @method - TestModule:new()
+ -- @desc - create a new Suite object
+ -- @param {string} module - string of love module the suite is for
+ -- @return {table} - returns the new Suite object
+ new = function(self, module, method)
+ local testmodule = {
+ timer = 0,
+ time = 0,
+ delay = 0.1,
+ spacer = ' ',
+ colors = {
+ PASS = 'green', FAIL = 'red', SKIP = 'grey'
+ },
+ colormap = {
+ grey = '\27[37m',
+ green = '\27[32m',
+ red = '\27[31m',
+ yellow = '\27[33m'
+ },
+ xml = '',
+ html = '',
+ tests = {},
+ running = {},
+ called = {},
+ passed = 0,
+ failed = 0,
+ skipped = 0,
+ module = module,
+ method = method,
+ index = 1,
+ start = false,
+ }
+ setmetatable(testmodule, self)
+ self.__index = self
+ return testmodule
+ end,
+
+
+ -- @method - TestModule:log()
+ -- @desc - log to console with specific colors, split out to make it easier
+ -- to adjust all console output across the tests
+ -- @param {string} color - color key to use for the log
+ -- @param {string} line - main message to write (LHS)
+ -- @param {string} result - result message to write (RHS)
+ -- @return {nil}
+ log = function(self, color, line, result)
+ if result == nil then result = '' end
+ print(self.colormap[color] .. line .. result)
+ end,
+
+
+ -- @method - TestModule:runTests()
+ -- @desc - starts the running of tests and sets up the list of methods to test
+ -- @param {string} module - module to set for the test suite
+ -- @param {string} method - specific method to test, if nil all methods tested
+ -- @return {nil}
+ runTests = function(self)
+ self.running = {}
+ self.passed = 0
+ self.failed = 0
+ if self.method ~= nil then
+ table.insert(self.running, self.method)
+ else
+ for i,_ in pairs(love.test[self.module]) do
+ table.insert(self.running, i)
+ end
+ table.sort(self.running)
+ end
+ self.index = 1
+ self.start = true
+ self:log('yellow', '\nlove.' .. self.module .. '.testmodule.start')
+ end,
+
+
+ -- @method - TestModule:printResult()
+ -- @desc - prints the result of the module to the console as well as appends
+ -- the XML + HTML for the test to the testsuite output
+ -- @return {nil}
+ printResult = function(self)
+ -- add xml to main output
+ love.test.xml = love.test.xml .. '\t\n' .. self.xml .. '\t\n'
+ -- add html to main output
+ local status = '🔴'
+ if self.failed == 0 then status = '🟢' end
+ love.test.html = love.test.html .. '' .. status .. ' love.' .. self.module .. '
' ..
+ '- 🟢 ' .. tostring(self.passed) .. ' Tests
' ..
+ '- 🔴 ' .. tostring(self.failed) .. ' Failures
' ..
+ '- 🟡 ' .. tostring(self.skipped) .. ' Skipped
' ..
+ '- ' .. tostring(self.time*1000) .. 'ms
' .. '
' ..
+ ' | Method | Time | Details |
' ..
+ self.html .. '
'
+ -- print module results to console
+ self:log('yellow', 'love.' .. self.module .. '.testmodule.end')
+ local failedcol = '\27[31m'
+ if self.failed == 0 then failedcol = '\27[37m' end
+ self:log('green', tostring(self.passed) .. ' PASSED' .. ' || ' ..
+ failedcol .. tostring(self.failed) .. ' FAILED || \27[37m' ..
+ tostring(self.skipped) .. ' SKIPPED')
+ self.start = false
+ self.fakequit = false
+ end
+
+
+}
\ No newline at end of file
diff --git a/testing/classes/TestSuite.lua b/testing/classes/TestSuite.lua
new file mode 100644
index 000000000..234019c8f
--- /dev/null
+++ b/testing/classes/TestSuite.lua
@@ -0,0 +1,159 @@
+TestSuite = {
+
+
+ -- @method - TestSuite:new()
+ -- @desc - creates a new TestSuite object that handles all the tests
+ -- @return {table} - returns the new TestSuite object
+ new = function(self)
+ local test = {
+
+ -- testsuite internals
+ modules = {},
+ module = nil,
+ testcanvas = love.graphics.newCanvas(16, 16),
+ current = 1,
+ output = '',
+ totals = {0, 0, 0},
+ time = 0,
+ xml = '',
+ html = '',
+ fakequit = false,
+ windowmode = true,
+
+ -- love modules to test
+ audio = {},
+ data = {},
+ event = {},
+ filesystem = {},
+ font = {},
+ graphics = {},
+ image = {},
+ joystick = {},
+ math = {},
+ mouse = {},
+ objects = {}, -- special for all object class contructor tests
+ physics = {},
+ sound = {},
+ system = {},
+ thread = {},
+ timer = {},
+ touch = {},
+ video = {},
+ window = {}
+
+ }
+ setmetatable(test, self)
+ self.__index = self
+ return test
+ end,
+
+
+ -- @method - TestSuite:runSuite()
+ -- @desc - called in love.update, runs through every method or every module
+ -- @param {number} delta - delta from love.update to track time elapsed
+ -- @return {nil}
+ runSuite = function(self, delta)
+
+ -- stagger 0.1s between tests
+ if self.module ~= nil then
+ self.module.timer = self.module.timer + delta
+ if self.module.timer >= self.module.delay then
+ self.module.timer = self.module.timer - self.module.delay
+ if self.module.start == true then
+
+ -- work through each test method 1 by 1
+ if self.module.index <= #self.module.running then
+
+ -- run method once
+ if self.module.called[self.module.index] == nil then
+ self.module.called[self.module.index] = true
+ local method = self.module.running[self.module.index]
+ local test = TestMethod:new(method, self.module)
+
+ -- check method exists in love first
+ if self.module.module ~= 'objects' and (love[self.module.module] == nil or love[self.module.module][method] == nil) then
+ local tested = 'love.' .. self.module.module .. '.' .. method .. '()'
+ local matching = string.sub(self.module.spacer, string.len(tested), 40)
+ self.module:log(self.module.colors['FAIL'],
+ tested .. matching,
+ ' ==> FAIL (0/0) - call failed - method does not exist'
+ )
+ -- otherwise run the test method then eval the asserts
+ else
+ local ok, chunk, err = pcall(self[self.module.module][method], test)
+ if ok == false then
+ print("FATAL", chunk, err)
+ test.fatal = tostring(chunk) .. tostring(err)
+ end
+ local ok, chunk, err = pcall(test.evaluateTest, test)
+ if ok == false then
+ print("FATAL", chunk, err)
+ test.fatal = tostring(chunk) .. tostring(err)
+ end
+ end
+ -- move onto the next test
+ self.module.index = self.module.index + 1
+ end
+
+ else
+
+ -- print module results and add to output
+ self.module:printResult()
+
+ -- if we have more modules to go run the next one
+ self.current = self.current + 1
+ if #self.modules >= self.current then
+ self.module = self.modules[self.current]
+ self.module:runTests()
+
+ -- otherwise print the final results and export output
+ else
+ self:printResult()
+ love.event.quit(0)
+ end
+
+ end
+ end
+ end
+ end
+ end,
+
+
+ -- @method - TestSuite:printResult()
+ -- @desc - prints the result of the whole test suite as well as writes
+ -- the XML + HTML of the testsuite output
+ -- @return {nil}
+ printResult = function(self)
+ local finaltime = tostring(math.floor(self.time*1000))
+ if string.len(finaltime) == 1 then finaltime = ' ' .. finaltime end
+ if string.len(finaltime) == 2 then finaltime = ' ' .. finaltime end
+ if string.len(finaltime) == 3 then finaltime = ' ' .. finaltime end
+
+ local xml = '\n'
+
+ local status = '🔴'
+ if self.totals[2] == 0 then status = '🟢' end
+ local html = '' .. status .. ' love.test
'
+ html = html ..
+ '- 🟢 ' .. tostring(self.totals[1]) .. ' Tests
' ..
+ '- 🔴 ' .. tostring(self.totals[2]) .. ' Failures
' ..
+ '- 🟡 ' .. tostring(self.totals[3]) .. ' Skipped
' ..
+ '- ' .. tostring(self.time*1000) .. 'ms
'
+
+ -- @TODO use mountFullPath to write output to src?
+ love.filesystem.createDirectory('output')
+ love.filesystem.write('output/' .. self.output .. '.xml', xml .. self.xml .. '')
+ love.filesystem.write('output/' .. self.output .. '.html', html .. self.html .. '
')
+
+ self.module:log('grey', '\nFINISHED - ' .. finaltime .. 'ms\n')
+ local failedcol = '\27[31m'
+ if self.totals[2] == 0 then failedcol = '\27[37m' end
+ self.module:log('green', tostring(self.totals[1]) .. ' PASSED' .. ' || ' .. failedcol .. tostring(self.totals[2]) .. ' FAILED || \27[37m' .. tostring(self.totals[3]) .. ' SKIPPED')
+
+ end
+
+
+}
\ No newline at end of file
diff --git a/testing/conf.lua b/testing/conf.lua
new file mode 100644
index 000000000..9f1ba5789
--- /dev/null
+++ b/testing/conf.lua
@@ -0,0 +1,24 @@
+function love.conf(t)
+ t.console = true
+ t.window.name = 'love.test'
+ t.window.width = 256
+ t.window.height = 256
+ t.window.resizable = true
+ t.renderers = {"opengl"}
+ t.modules.audio = true
+ t.modules.data = true
+ t.modules.event = true
+ t.modules.filesystem = true
+ t.modules.font = true
+ t.modules.graphics = true
+ t.modules.image = true
+ t.modules.math = true
+ t.modules.objects = true
+ t.modules.physics = true
+ t.modules.sound = true
+ t.modules.system = true
+ t.modules.thread = true
+ t.modules.timer = true
+ t.modules.video = true
+ t.modules.window = true
+end
\ No newline at end of file
diff --git a/testing/main.lua b/testing/main.lua
new file mode 100644
index 000000000..205f0b786
--- /dev/null
+++ b/testing/main.lua
@@ -0,0 +1,172 @@
+-- & 'c:\Program Files\LOVE\love.exe' ./ --console
+-- /Applications/love.app/Contents/MacOS/love ./
+
+-- load test objs
+require('classes.TestSuite')
+require('classes.TestModule')
+require('classes.TestMethod')
+
+-- create testsuite obj
+love.test = TestSuite:new()
+
+-- load test scripts if module is active
+if love.audio ~= nil then require('tests.audio') end
+if love.data ~= nil then require('tests.data') end
+if love.event ~= nil then require('tests.event') end
+if love.filesystem ~= nil then require('tests.filesystem') end
+if love.font ~= nil then require('tests.font') end
+if love.graphics ~= nil then require('tests.graphics') end
+if love.image ~= nil then require('tests.image') end
+if love.math ~= nil then require('tests.math') end
+if love.physics ~= nil then require('tests.physics') end
+if love.sound ~= nil then require('tests.sound') end
+if love.system ~= nil then require('tests.system') end
+if love.thread ~= nil then require('tests.thread') end
+if love.timer ~= nil then require('tests.timer') end
+if love.video ~= nil then require('tests.video') end
+if love.window ~= nil then require('tests.window') end
+require('tests.objects')
+
+-- love.load
+-- load given arguments and run the test suite
+love.load = function(args)
+
+ -- setup basic img to display
+ if love.window ~= nil then
+ love.window.setMode(256, 256, {
+ fullscreen = false,
+ resizable = true,
+ centered = true
+ })
+ if love.graphics ~= nil then
+ love.graphics.setDefaultFilter("nearest", "nearest")
+ love.graphics.setLineStyle('rough')
+ love.graphics.setLineWidth(1)
+ Logo = {
+ texture = love.graphics.newImage('resources/love.png'),
+ img = nil
+ }
+ Logo.img = love.graphics.newQuad(0, 0, 64, 64, Logo.texture)
+ end
+ end
+
+ -- get all args with any comma lists split out as seperate
+ local arglist = {}
+ for a=1,#args do
+ local splits = UtilStringSplit(args[a], '([^,]+)')
+ for s=1,#splits do
+ table.insert(arglist, splits[s])
+ end
+ end
+
+ -- convert args to the cmd to run, modules, method (if any) and disabled
+ local testcmd = '--runAllTests'
+ local module = ''
+ local method = ''
+ local modules = {
+ 'audio', 'data', 'event', 'filesystem', 'font', 'graphics',
+ 'image', 'math', 'objects', 'physics', 'sound', 'system',
+ 'thread', 'timer', 'video', 'window'
+ }
+ for a=1,#arglist do
+ if testcmd == '--runSpecificMethod' then
+ if module == '' and love[ arglist[a] ] ~= nil then
+ module = arglist[a]
+ table.insert(modules, module)
+ end
+ if module ~= '' and love[module][ arglist[a] ] ~= nil and method == '' then
+ method = arglist[a]
+ end
+ end
+ if testcmd == '--runSpecificModules' then
+ if love[ arglist[a] ] ~= nil or arglist[a] == 'objects' then
+ table.insert(modules, arglist[a])
+ end
+ end
+ if arglist[a] == '--runSpecificMethod' then
+ testcmd = arglist[a]
+ modules = {}
+ end
+ if arglist[a] == '--runSpecificModules' then
+ testcmd = arglist[a]
+ modules = {}
+ end
+ end
+
+ -- runSpecificMethod uses the module + method given
+ if testcmd == '--runSpecificMethod' then
+ local testmodule = TestModule:new(module, method)
+ table.insert(love.test.modules, testmodule)
+ love.test.module = testmodule
+ love.test.module:log('grey', '--runSpecificMethod "' .. module .. '" "' .. method .. '"')
+ love.test.output = 'lovetest_runSpecificMethod_' .. module .. '_' .. method
+ end
+
+ -- runSpecificModules runs all methods for all the modules given
+ if testcmd == '--runSpecificModules' then
+ local modulelist = {}
+ for m=1,#modules do
+ local testmodule = TestModule:new(modules[m])
+ table.insert(love.test.modules, testmodule)
+ table.insert(modulelist, modules[m])
+ end
+
+ love.test.module = love.test.modules[1]
+ love.test.module:log('grey', '--runSpecificModules "' .. table.concat(modulelist, '" "') .. '"')
+ love.test.output = 'lovetest_runSpecificModules_' .. table.concat(modulelist, '_')
+ end
+
+ -- otherwise default runs all methods for all modules
+ if arglist[1] == nil or arglist[1] == '' or arglist[1] == '--runAllTests' then
+ for m=1,#modules do
+ local testmodule = TestModule:new(modules[m])
+ table.insert(love.test.modules, testmodule)
+ end
+ love.test.module = love.test.modules[1]
+ love.test.module:log('grey', '--runAllTests')
+ love.test.output = 'lovetest_runAllTests'
+ end
+
+ -- invalid command
+ if love.test.module == nil then
+ print("Wrong flags used")
+ end
+
+ -- start first module
+ love.test.module:runTests()
+
+end
+
+-- love.update
+-- run test suite logic
+love.update = function(delta)
+ love.test:runSuite(delta)
+end
+
+
+-- love.draw
+-- draw a little logo to the screen
+love.draw = function()
+ love.graphics.draw(Logo.texture, Logo.img, 64, 64, 0, 2, 2)
+end
+
+
+-- love.quit
+-- add a hook to allow test modules to fake quit
+love.quit = function()
+ if love.test.module ~= nil and love.test.module.fakequit == true then
+ return true
+ else
+ return false
+ end
+end
+
+
+-- string split helper
+function UtilStringSplit(str, splitter)
+ local splits = {}
+ for word in string.gmatch(str, splitter) do
+ table.insert(splits, word)
+ end
+ return splits
+end
\ No newline at end of file
diff --git a/testing/output/lovetest_runAllTests.html b/testing/output/lovetest_runAllTests.html
new file mode 100644
index 000000000..164ade985
--- /dev/null
+++ b/testing/output/lovetest_runAllTests.html
@@ -0,0 +1 @@
+🔴 love.test
- 🟢 157 Tests
- 🔴 5 Failures
- 🟡 11 Skipped
- 7341.71ms
🟢 love.audio
- 🟢 26 Tests
- 🔴 0 Failures
- 🟡 0 Skipped
- 0.40991666666712ms
| Method | Time | Details |
🟢 | getActiveEffects | 0.045083333333418ms | |
🟢 | getActiveSourceCount | 1.1385ms | |
🟢 | getDistanceModel | 0.018125000000202ms | |
🟢 | getDopplerScale | 0.012208333333374ms | |
🟢 | getEffect | 0.035250000000042ms | |
🟢 | getMaxSceneEffects | 0.0089583333331422ms | |
🟢 | getMaxSourceEffects | 0.022874999999978ms | |
🟢 | getOrientation | 0.037541666666696ms | |
🟢 | getPosition | 0.024500000000316ms | |
🟢 | getRecordingDevices | 0.039250000000157ms | |
🟢 | getVelocity | 0.021333333333207ms | |
🟢 | getVolume | 0.046083333333335ms | |
🟢 | isEffectsSupported | 0.016749999999899ms | |
🟢 | newQueueableSource | 0.041958333333314ms | |
🟢 | newSource | 2.8378333333332ms | |
🟢 | pause | 2.6265416666664ms | |
🟢 | play | 1.7924166666665ms | |
🟢 | setDistanceModel | 0.023249999999919ms | |
🟢 | setDopplerScale | 0.094375000000646ms | |
🟢 | setEffect | 0.024166666666936ms | |
🟢 | setMixWithSystem | 0.0052083333330621ms | |
🟢 | setOrientation | 0.017000000000156ms | |
🟢 | setPosition | 0.0091666666666157ms | |
🟢 | setVelocity | 0.0070416666657636ms | |
🟢 | setVolume | 0.0075833333332831ms | |
🟢 | stop | 1.787666666667ms | |
🟢 love.data
- 🟢 7 Tests
- 🔴 0 Failures
- 🟡 3 Skipped
- 0.34008333333449ms
| Method | Time | Details |
🟢 | compress | 0.39495833333403ms | |
🟢 | decode | 0.027791666666666ms | |
🟢 | decompress | 0.28366666666635ms | |
🟢 | encode | 0.044000000000377ms | |
🟡 | getPackedSize | 0.0069999999996462ms | dont understand lua packing types |
🟢 | hash | 0.12045833333341ms | |
🟢 | newByteData | 0.021916666666844ms | |
🟢 | newDataView | 0.025416666666889ms | |
🟡 | pack | 0.0070833333332132ms | dont understand lua packing types |
🟡 | unpack | 0.0091250000000542ms | dont understand lua packing types |
🟢 love.event
- 🟢 4 Tests
- 🔴 0 Failures
- 🟡 2 Skipped
- 0.47999999999884ms
| Method | Time | Details |
🟢 | clear | 0.028666666666233ms | |
🟢 | poll | 0.022374999999464ms | |
🟡 | pump | 0.0079583333327804ms | not sure we can test when its internal? |
🟢 | push | 0.022500000000036ms | |
🟢 | quit | 0.014125000000753ms | |
🟡 | wait | 0.0069166666669673ms | not sure on best way to test this |
🔴 love.filesystem
- 🟢 26 Tests
- 🔴 1 Failures
- 🟡 2 Skipped
- 1.0150000000023ms
| Method | Time | Details |
🟢 | append | 1.3135833333333ms | |
🟢 | areSymlinksEnabled | 0.01433333333356ms | |
🟢 | createDirectory | 0.39929166666663ms | |
🟢 | getAppdataDirectory | 0.014166666668203ms | |
🟢 | getCRequirePath | 0.015749999999315ms | |
🟢 | getDirectoryItems | 1.0962083333332ms | |
🟢 | getIdentity | 0.10579166666691ms | |
🟢 | getInfo | 0.9892916666665ms | |
🟢 | getRealDirectory | 0.98024999999957ms | |
🟢 | getRequirePath | 0.097583333333873ms | |
🟢 | getSaveDirectory | 0.01891666666598ms | |
🟡 | getSource | 0.020666666666003ms | not sure we can test when its internal? |
🟢 | getSourceBaseDirectory | 0.015083333334331ms | |
🟢 | getUserDirectory | 0.043666666666553ms | |
🟢 | getWorkingDirectory | 0.018791666667184ms | |
🟢 | isFused | 0.017999999998963ms | |
🟢 | lines | 13.630166666666ms | |
🟢 | load | 7.9870833333331ms | |
🟢 | mount | 0.48216666666789ms | |
🔴 | newFile | 0.37395833333331ms | assert #2 [check file made] avoiding 'nil' got 'nil' |
🟢 | newFileData | 0.030666666666512ms | |
🟢 | read | 0.19545833333368ms | |
🟢 | remove | 0.81487500000055ms | |
🟢 | setCRequirePath | 0.017416666667103ms | |
🟢 | setIdentity | 0.086666666666346ms | |
🟢 | setRequirePath | 0.014500000001583ms | |
🟡 | setSource | 0.0082916666652721ms | not sure we can test when its internal? |
🟢 | unmount | 1.1363333333341ms | |
🟢 | write | 1.0965416666666ms | |
🟢 love.font
- 🟢 4 Tests
- 🔴 0 Failures
- 🟡 1 Skipped
- 0.11233333333444ms
| Method | Time | Details |
🟡 | newBMFontRasterizer | 0.007625000002065ms | wiki and source dont match, not sure expected usage |
🟢 | newGlyphData | 0.28787499999972ms | |
🟢 | newImageRasterizer | 0.22408333333424ms | |
🟢 | newRasterizer | 0.18954166666596ms | |
🟢 | newTrueTypeRasterizer | 0.18991666666501ms | |
🔴 love.graphics
- 🟢 0 Tests
- 🔴 1 Failures
- 🟡 0 Skipped
- 0.94387500000009ms
| Method | Time | Details |
🔴 | rectangle | 3.7313333333344ms | assert #2 [check 0x,0y G] expected '1' got '0' |
🟢 love.image
- 🟢 3 Tests
- 🔴 0 Failures
- 🟡 0 Skipped
- 1.2476249999999ms
| Method | Time | Details |
🟢 | isCompressed | 0.21679166666644ms | |
🟢 | newCompressedData | 0.19920833333309ms | |
🟢 | newImageData | 0.40049999999958ms | |
🟢 love.math
- 🟢 16 Tests
- 🔴 0 Failures
- 🟡 0 Skipped
- 0.062999999998231ms
| Method | Time | Details |
🟢 | colorFromBytes | 0.29325000000036ms | |
🟢 | colorToBytes | 0.27899999999903ms | |
🟢 | gammaToLinear | 0.021624999998693ms | |
🟢 | getRandomSeed | 0.014708333333502ms | |
🟢 | getRandomState | 0.071791666666599ms | |
🟢 | isConvex | 0.056666666665706ms | |
🟢 | linearToGamma | 0.01791666666584ms | |
🟢 | newBezierCurve | 0.053125000000875ms | |
🟢 | newRandomGenerator | 0.019874999999558ms | |
🟢 | newTransform | 0.02287499999909ms | |
🟢 | noise | 0.076916666666094ms | |
🟢 | random | 0.17783333333377ms | |
🟢 | randomNormal | 0.020458333334972ms | |
🟢 | setRandomSeed | 0.019541666667067ms | |
🟢 | setRandomState | 0.053750000001074ms | |
🟢 | triangulate | 0.023874999998341ms | |
🟢 love.objects
- 🟢 0 Tests
- 🔴 0 Failures
- 🟡 0 Skipped
- 1.6546249999983ms
🔴 love.physics
- 🟢 21 Tests
- 🔴 1 Failures
- 🟡 0 Skipped
- 0.014583333332624ms
| Method | Time | Details |
🟢 | getDistance | 0.075333333334981ms | |
🟢 | getMeter | 0.015125000000893ms | |
🟢 | newBody | 0.03362500000037ms | |
🟢 | newChainShape | 0.028624999998783ms | |
🟢 | newCircleShape | 0.020624999999441ms | |
🟢 | newDistanceJoint | 0.037833333333737ms | |
🟢 | newEdgeShape | 0.020624999999441ms | |
🟢 | newFixture | 0.077750000000876ms | |
🟢 | newFrictionJoint | 0.031708333333214ms | |
🔴 | newGearJoint | 0.12670833333139ms | test tests/physics.lua:134: Box2D assertion failed: m_bodyA->m_type == b2_dynamicBody |
🟢 | newMotorJoint | 0.092416666667816ms | |
🟢 | newMouseJoint | 0.050208333334467ms | |
🟢 | newPolygonShape | 0.025333333333322ms | |
🟢 | newPrismaticJoint | 0.034124999999108ms | |
🟢 | newPulleyJoint | 0.035041666665236ms | |
🟢 | newRectangleShape | 0.077833333332222ms | |
🟢 | newRevoluteJoint | 0.040416666667653ms | |
🟢 | newRopeJoint | 0.03037500000147ms | |
🟢 | newWeldJoint | 0.074916666664038ms | |
🟢 | newWheelJoint | 0.1102083333322ms | |
🟢 | newWorld | 0.075416666666328ms | |
🟢 | setMeter | 0.031208333336252ms | |
🟢 love.sound
- 🟢 2 Tests
- 🔴 0 Failures
- 🟡 0 Skipped
- 0.93524999999842ms
| Method | Time | Details |
🟢 | newDecoder | 0.31383333333501ms | |
🟢 | newSoundData | 1.1787083333292ms | |
🟢 love.system
- 🟢 6 Tests
- 🔴 0 Failures
- 🟡 2 Skipped
- 1.3057500000059ms
| Method | Time | Details |
🟢 | getClipboardText | 1.6217916666665ms | |
🟢 | getOS | 0.034041666669538ms | |
🟢 | getPowerInfo | 0.080041666665309ms | |
🟢 | getProcessorCount | 0.017791666669709ms | |
🟢 | hasBackgroundMusic | 0.086708333334684ms | |
🟡 | openURL | 0.016333333334728ms | gets annoying to test everytime |
🟢 | setClipboardText | 0.59291666666716ms | |
🟡 | vibrate | 0.0090416666651549ms | cant really test this |
🟢 love.thread
- 🟢 3 Tests
- 🔴 0 Failures
- 🟡 0 Skipped
- 0.96195833333323ms
| Method | Time | Details |
🟢 | getChannel | 0.47320833333231ms | |
🟢 | newChannel | 0.028374999999414ms | |
🟢 | newThread | 0.2556250000012ms | |
🟢 love.timer
- 🟢 6 Tests
- 🔴 0 Failures
- 🟡 0 Skipped
- 3713.5796666667ms
| Method | Time | Details |
🟢 | getAverageDelta | 0.023208333331581ms | |
🟢 | getDelta | 0.015708333332753ms | |
🟢 | getFPS | 0.011125000000334ms | |
🟢 | getTime | 1001.1531666667ms | |
🟢 | sleep | 1000.4330833333ms | |
🟢 | step | 0.032958333331834ms | |
🟢 love.video
- 🟢 1 Tests
- 🔴 0 Failures
- 🟡 0 Skipped
- 0.67754166666772ms
| Method | Time | Details |
🟢 | newVideoStream | 3.4201249999981ms | |
🔴 love.window
- 🟢 32 Tests
- 🔴 2 Failures
- 🟡 1 Skipped
- 7985.3964583333ms
| Method | Time | Details |
🟢 | close | 11.641583333336ms | |
🟢 | fromPixels | 0.015333333330148ms | |
🟢 | getDPIScale | 0.014499999998918ms | |
🟢 | getDesktopDimensions | 0.015083333330779ms | |
🟢 | getDisplayCount | 0.010291666665552ms | |
🟢 | getDisplayName | 0.014875000001524ms | |
🟢 | getDisplayOrientation | 0.016250000001605ms | |
🟢 | getFullscreen | 1305.7451666667ms | |
🟢 | getFullscreenModes | 0.48033333332853ms | |
🟢 | getIcon | 2.0577083333322ms | |
🟢 | getMode | 0.10416666667012ms | |
🟢 | getPosition | 4.9660833333327ms | |
🟢 | getSafeArea | 0.067875000002715ms | |
🟢 | getTitle | 0.55745833333276ms | |
🟢 | getVSync | 0.095124999997864ms | |
🟢 | hasFocus | 0.055624999998116ms | |
🟢 | hasMouseFocus | 0.022541666666598ms | |
🟢 | isDisplaySleepEnabled | 0.14775000000355ms | |
🔴 | isMaximized | 642.02083333333ms | assert #2 [check window not maximized] expected 'true' got 'false' |
🟢 | isMinimized | 641.41475ms | |
🟢 | isOpen | 25.519625000001ms | |
🟢 | isVisible | 18.191791666666ms | |
🔴 | maximize | 0.23570833333508ms | assert #1 [check window maximized] expected 'true' got 'false' |
🟢 | minimize | 640.26066666666ms | |
🟢 | restore | 643.01916666667ms | |
🟢 | setDisplaySleepEnabled | 0.59508333333014ms | |
🟢 | setFullscreen | 1329.778375ms | |
🟢 | setIcon | 2.0223750000028ms | |
🟢 | setMode | 4.5707499999992ms | |
🟢 | setPosition | 0.16512499999521ms | |
🟢 | setTitle | 0.47262500000045ms | |
🟢 | setVSync | 0.022583333333159ms | |
🟡 | showMessageBox | 0.068958333333313ms | skipping cos annoying to test with |
🟢 | toPixels | 0.067666666666355ms | |
🟢 | updateMode | 6.8227083333348ms | |
\ No newline at end of file
diff --git a/testing/output/lovetest_runAllTests.xml b/testing/output/lovetest_runAllTests.xml
new file mode 100644
index 000000000..a30afbbc2
--- /dev/null
+++ b/testing/output/lovetest_runAllTests.xml
@@ -0,0 +1,385 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/testing/readme.md b/testing/readme.md
new file mode 100644
index 000000000..4533e8c45
--- /dev/null
+++ b/testing/readme.md
@@ -0,0 +1,139 @@
+# löve.test
+Basic testing suite for the löve APIs, based off of [this issue](https://github.com/love2d/love/issues/1745)
+
+Currently written for löve 12
+
+---
+
+## Primary Goals
+- [x] Simple pass/fail tests in Lua with minimal setup
+- [x] Ability to run all tests with a simple command.
+- [x] Ability to see how many tests are passing/failing
+- [x] No platform-specific dependencies / scripts
+- [x] Ability to run a subset of tests
+- [x] Ability to easily run an individual test.
+
+---
+
+## Running Tests
+The initial pass is to keep things as simple as possible, and just run all the tests inside Löve to match how they'd be used by developers in-engine.
+To run the tests, download the repo and then run the main.lua as you would a löve game, i.e:
+
+WINDOWS: `& 'c:\Program Files\LOVE\love.exe' PATH_TO_TESTING_FOLDER --console`
+MACOS: `/Applications/love.app/Contents/MacOS/love PATH_TO_TESTING_FOLDER`
+
+By default all tests will be run for all modules.
+
+If you want to specify a module you can add:
+`--runSpecificModules filesystem`
+For multiple modules, provide a comma seperate list:
+`--runSpecificModules filesystem,audio,data"`
+
+If you want to specify only 1 specific method only you can use:
+`--runSpecificMethod filesystem write`
+
+All results will be printed in the console per method as PASS, FAIL, or SKIP with total assertions met on a module level and overall level.
+
+An `XML` file in the style of [JUnit XML](https://www.ibm.com/docs/en/developer-for-zos/14.1?topic=formats-junit-xml-format) will be generated in your save directory, along with a `HTML` file with a summary of all tests (including visuals for love.graphics tests).
+> Note that this can only be viewed properly locally as the generated images are written to the save directory.
+> An example of both types of output can be found in the `/output` folder
+
+---
+
+## Architecture
+Each method has it's own test method written in `/tests` under the matching module name.
+
+When you run the tests, a single TestSuite object is created which handles the progress + totals for all the tests.
+Each module has a TestModule object created, and each test method has a TestMethod object created which keeps track of assertions for that method. You can currently do the following assertions:
+- **assertEquals**(expected, actual)
+- **assertNotEquals**(expected, actual)
+- **assertRange**(actual, min, max)
+- **assertMatch**({option1, option2, option3 ...}, actual)
+- **assertGreaterEqual**(expected, actual)
+- **assertLessEqual**(expected, actual)
+- **assertObject**(table)
+
+Example test method:
+```lua
+-- love.filesystem.read test method
+-- all methods should be put under love.test.MODULE.METHOD, matching the API
+love.test.filesystem.read = function(test)
+ -- setup any data needed then run any asserts using the passed test object
+ local content, size = love.filesystem.read('resources/test.txt')
+ test:assertNotEquals(nil, content, 'check not nil')
+ test:assertEquals('helloworld', content, 'check content match')
+ test:assertEquals(10, size, 'check size match')
+ content, size = love.filesystem.read('resources/test.txt', 5)
+ test:assertNotEquals(nil, content, 'check not nil')
+ test:assertEquals('hello', content, 'check content match')
+ test:assertEquals(5, size, 'check size match')
+ -- no need to return anything just cleanup any objs if needed
+end
+```
+
+After each test method is ran, the assertions are totalled up, printed, and we move onto the next method! Once all methods in the suite are run a total pass/fail/skip is given for that module and we move onto the next module (if any)
+
+For sanity-checking, if it's currently not covered or we're not sure how to test yet we can set the test to be skipped with `test:skipTest(reason)` - this way we still see the method listed in the tests without it affected the pass/fail totals
+
+---
+
+## Coverage
+This is the status of all module tests currently.
+"objects" is a special module to cover any object specific tests, i.e. testing a File object functions as expected
+```lua
+-- [x] audio 26 PASSED | 0 FAILED | 0 SKIPPED
+-- [x] data 7 PASSED | 0 FAILED | 3 SKIPPED [SEE BELOW]
+-- [x] event 4 PASSED | 0 FAILED | 2 SKIPPED [SEE BELOW]
+-- [x] filesystem 26 PASSED | 1 FAILED | 2 SKIPPED [SEE BELOW]
+-- [x] font 4 PASSED | 0 FAILED | 1 SKIPPED [SEE BELOW]
+-- [ ] graphics STILL TO BE DONE
+-- [x] image 3 PASSED | 0 FAILED | 0 SKIPPED
+-- [x] math 16 PASSED | 0 FAILED | 0 SKIPPED [SEE BELOW]
+-- [x] physics 21 PASSED | 1 FAILED | 0 SKIPPED [SEE BELOW]
+-- [x] sound 2 PASSED | 0 FAILED | 0 SKIPPED
+-- [x] system 7 PASSED | 0 FAILED | 1 SKIPPED
+-- [ ] thread 3 PASSED | 0 FAILED | 0 SKIPPED
+-- [x] timer 6 PASSED | 0 FAILED | 0 SKIPPED [SEE BELOW]
+-- [x] video 1 PASSED | 0 FAILED | 0 SKIPPED
+-- [x] window 32 PASSED | 2 FAILED | 1 SKIPPED [SEE BELOW]
+-- [ ] objects STILL TO BE DONE
+```
+
+The following modules are not covered as we can't really emulate input nicely:
+`joystick`, `keyboard`, `mouse`, and `touch`
+
+---
+
+## Todo / Skipped
+Modules with some small bits needed or needing sense checking:
+- **love.data** - packing methods need writing cos i dont really get what they are
+- **love.event** - love.event.wait or love.event.pump need writing if possible I dunno how to check
+- **love.filesystem** - getSource() / setSource() dont think we can test
+- **love.font** - newBMFontRasterizer() wiki entry is wrong so not sure whats expected
+- **love.timer** - couple methods I don't know if you could reliably test specific values
+- **love.image** - ideally isCompressed should have an example of all compressed files love can take
+- **love.math** - linearToGamma + gammaToLinear using direct formulas don't get same value back
+- **love.window** - couple stuff just nil checked as I think it's hardware dependent, needs checking
+
+Modules still to be completed or barely started
+- **love.graphics** - done 1 as an example of how we can test the drawing but not really started
+- **love.objects** - done 1 as an example of how we can test objs with mini scenarios
+
+---
+
+## Failures
+- **love.window.isMaximized()** - returns false after calling love.window.maximize?
+- **love.window.maximize()** - same as above
+- **love.filesystem.newFile()** - something changed in 12
+- **love.physics.newGearJoint()** - something changed in 12
+
+---
+
+## Stretch Goals
+- [ ] Tests can compare visual results to a reference image
+- [ ] Ability to see all visual results at a glance
+- [ ] Automatic testing that happens after every commit
+- [ ] Ability to test loading different combinations of modules
+- [ ] Performance tests
+
+There is some unused code in the Test.lua class to add preview vs actual images to the HTML output
\ No newline at end of file
diff --git a/testing/resources/click.ogg b/testing/resources/click.ogg
new file mode 100644
index 000000000..49707b3e8
Binary files /dev/null and b/testing/resources/click.ogg differ
diff --git a/testing/resources/font.ttf b/testing/resources/font.ttf
new file mode 100644
index 000000000..0ff4bf7d3
Binary files /dev/null and b/testing/resources/font.ttf differ
diff --git a/testing/resources/love.dxt1 b/testing/resources/love.dxt1
new file mode 100644
index 000000000..88cb9e689
Binary files /dev/null and b/testing/resources/love.dxt1 differ
diff --git a/testing/resources/love.png b/testing/resources/love.png
new file mode 100644
index 000000000..e2612a857
Binary files /dev/null and b/testing/resources/love.png differ
diff --git a/testing/resources/love_test_graphics_rectangle_expected.png b/testing/resources/love_test_graphics_rectangle_expected.png
new file mode 100644
index 000000000..bbbaf6edc
Binary files /dev/null and b/testing/resources/love_test_graphics_rectangle_expected.png differ
diff --git a/testing/resources/sample.ogv b/testing/resources/sample.ogv
new file mode 100644
index 000000000..f145489c2
Binary files /dev/null and b/testing/resources/sample.ogv differ
diff --git a/testing/resources/test.txt b/testing/resources/test.txt
new file mode 100644
index 000000000..620ffd0fd
--- /dev/null
+++ b/testing/resources/test.txt
@@ -0,0 +1 @@
+helloworld
\ No newline at end of file
diff --git a/testing/resources/test.zip b/testing/resources/test.zip
new file mode 100644
index 000000000..46cfaba97
Binary files /dev/null and b/testing/resources/test.zip differ
diff --git a/testing/tests/audio.lua b/testing/tests/audio.lua
new file mode 100644
index 000000000..0e33a7640
--- /dev/null
+++ b/testing/tests/audio.lua
@@ -0,0 +1,296 @@
+-- love.audio
+
+
+-- love.audio.getActiveEffects
+love.test.audio.getActiveEffects = function(test)
+ -- tests
+ test:assertNotEquals(nil, love.audio.getActiveEffects(), 'check not nil')
+ test:assertEquals(0, #love.audio.getActiveEffects(), 'check no effects running')
+ love.audio.setEffect('testeffect', {
+ type = 'chorus',
+ volume = 10
+ })
+ test:assertEquals(1, #love.audio.getActiveEffects(), 'check 1 effect running')
+ test:assertEquals('testeffect', love.audio.getActiveEffects()[1], 'check effect details')
+end
+
+
+-- love.audio.getActiveSourceCount
+love.test.audio.getActiveSourceCount = function(test)
+ -- tests
+ test:assertNotEquals(nil, love.audio.getActiveSourceCount(), 'check not nil')
+ test:assertEquals(0, love.audio.getActiveSourceCount(), 'check 0 by default')
+ local testsource = love.audio.newSource('resources/click.ogg', 'static')
+ test:assertEquals(0, love.audio.getActiveSourceCount(), 'check not active')
+ love.audio.play(testsource)
+ test:assertEquals(1, love.audio.getActiveSourceCount(), 'check now active')
+ love.audio.pause()
+ testsource:release()
+end
+
+
+-- love.audio.getDistanceModel
+love.test.audio.getDistanceModel = function(test)
+ -- tests
+ test:assertNotEquals(nil, love.audio.getDistanceModel(), 'check not nil')
+ test:assertEquals('inverseclamped', love.audio.getDistanceModel(), 'check default value')
+ love.audio.setDistanceModel('inverse')
+ test:assertEquals('inverse', love.audio.getDistanceModel(), 'check setting model')
+end
+
+
+-- love.audio.getDopplerScale
+love.test.audio.getDopplerScale = function(test)
+ test:assertEquals(1, love.audio.getDopplerScale(), 'check default 1')
+ love.audio.setDopplerScale(0)
+ test:assertEquals(0, love.audio.getDopplerScale(), 'check setting to 0')
+ love.audio.setDopplerScale(1)
+end
+
+
+-- love.audio.getEffect
+love.test.audio.getEffect = function(test)
+ -- setup
+ love.audio.setEffect('testeffect', {
+ type = 'chorus',
+ volume = 10
+ })
+ -- tests
+ test:assertEquals(nil, love.audio.getEffect('madeupname'), 'check wrong name')
+ test:assertNotEquals(nil, love.audio.getEffect('testeffect'), 'check not nil')
+ test:assertEquals('chorus', love.audio.getEffect('testeffect').type, 'check effect type')
+ test:assertEquals(10, love.audio.getEffect('testeffect').volume, 'check effect volume')
+end
+
+
+-- love.audio.getMaxSceneEffects
+-- @NOTE feel like this is platform specific number so best we can do is a nil?
+love.test.audio.getMaxSceneEffects = function(test)
+ test:assertNotEquals(nil, love.audio.getMaxSceneEffects(), 'check not nil')
+end
+
+
+-- love.audio.getMaxSourceEffects
+-- @NOTE feel like this is platform specific number so best we can do is a nil?
+love.test.audio.getMaxSourceEffects = function(test)
+ test:assertNotEquals(nil, love.audio.getMaxSourceEffects(), 'check not nil')
+end
+
+
+-- love.audio.getOrientation
+-- @NOTE is there an expected default listener pos?
+love.test.audio.getOrientation = function(test)
+ -- setup
+ love.audio.setOrientation(1, 2, 3, 4, 5, 6)
+ -- tests
+ local fx, fy, fz, ux, uy, uz = love.audio.getOrientation()
+ test:assertEquals(1, fx, 'check fx orientation')
+ test:assertEquals(2, fy, 'check fy orientation')
+ test:assertEquals(3, fz, 'check fz orientation')
+ test:assertEquals(4, ux, 'check ux orientation')
+ test:assertEquals(5, uy, 'check uy orientation')
+ test:assertEquals(6, uz, 'check uz orientation')
+end
+
+
+-- love.audio.getPosition
+-- @NOTE is there an expected default listener pos?
+love.test.audio.getPosition = function(test)
+ -- setup
+ love.audio.setPosition(1, 2, 3)
+ -- tests
+ local x, y, z = love.audio.getPosition()
+ test:assertEquals(1, x, 'check x position')
+ test:assertEquals(2, y, 'check y position')
+ test:assertEquals(3, z, 'check z position')
+end
+
+
+-- love.audio.getRecordingDevices
+love.test.audio.getRecordingDevices = function(test)
+ test:assertNotEquals(nil, love.audio.getRecordingDevices(), 'check not nil')
+end
+
+
+-- love.audio.getVelocity
+love.test.audio.getVelocity = function(test)
+ -- setup
+ love.audio.setVelocity(1, 2, 3)
+ -- tests
+ local x, y, z = love.audio.getVelocity()
+ test:assertEquals(1, x, 'check x velocity')
+ test:assertEquals(2, y, 'check y velocity')
+ test:assertEquals(3, z, 'check z velocity')
+end
+
+
+-- love.audio.getVolume
+love.test.audio.getVolume = function(test)
+ -- setup
+ love.audio.setVolume(0.5)
+ -- tests
+ test:assertNotEquals(nil, love.audio.getVolume(), 'check not nil')
+ test:assertEquals(0.5, love.audio.getVolume(), 'check matches set')
+end
+
+
+-- love.audio.isEffectsSupported
+love.test.audio.isEffectsSupported = function(test)
+ test:assertNotEquals(nil, love.audio.isEffectsSupported(), 'check not nil')
+end
+
+
+-- love.audio.newQueueableSource
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.audio.newQueueableSource = function(test)
+ local source = love.audio.newQueueableSource(32, 8, 1, 8)
+ test:assertObject(source)
+ source:release()
+end
+
+
+-- love.audio.newSource
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.audio.newSource = function(test)
+ -- setup
+ local source1 = love.audio.newSource('resources/click.ogg', 'static')
+ local source2 = love.audio.newSource('resources/click.ogg', 'stream')
+ -- tests
+ test:assertObject(source1)
+ test:assertObject(source2)
+ -- cleanup
+ source1:release()
+ source2:release()
+end
+
+
+-- love.audio.pause
+love.test.audio.pause = function(test)
+ -- tests
+ local nopauses = love.audio.pause()
+ test:assertNotEquals(nil, nopauses, 'check not nil')
+ test:assertEquals(0, #nopauses, 'check nothing paused')
+ local source = love.audio.newSource('resources/click.ogg', 'static')
+ love.audio.play(source)
+ local onepause = love.audio.pause()
+ test:assertEquals(1, #onepause, 'check 1 paused')
+ source:release()
+end
+
+
+-- love.audio.play
+love.test.audio.play = function(test)
+ -- setup
+ local source = love.audio.newSource('resources/click.ogg', 'static')
+ -- tests
+ love.audio.play(source)
+ test:assertEquals(true, source:isPlaying(), 'check something playing')
+ love.audio.pause()
+ source:release()
+end
+
+
+-- love.audio.setDistanceModel
+love.test.audio.setDistanceModel = function(test)
+ -- tests
+ local distancemodel = {
+ 'none', 'inverse', 'inverseclamped', 'linear', 'linearclamped',
+ 'exponent', 'exponentclamped'
+ }
+ for d=1,#distancemodel do
+ love.audio.setDistanceModel(distancemodel[d])
+ test:assertEquals(distancemodel[d], love.audio.getDistanceModel(), 'check model set to ' .. distancemodel[d])
+ end
+end
+
+
+-- love.audio.setDopplerScale
+love.test.audio.setDopplerScale = function(test)
+ -- tests
+ love.audio.setDopplerScale(0)
+ test:assertEquals(0, love.audio.getDopplerScale(), 'check set to 0')
+ love.audio.setDopplerScale(1)
+ test:assertEquals(1, love.audio.getDopplerScale(), 'check set to 1')
+end
+
+
+-- love.audio.setEffect
+love.test.audio.setEffect = function(test)
+ -- tests
+ local effect = love.audio.setEffect('testeffect', {
+ type = 'chorus',
+ volume = 10
+ })
+ test:assertEquals(true, effect, 'check effect created')
+ local settings = love.audio.getEffect('testeffect')
+ test:assertEquals('chorus', settings.type, 'check effect type')
+ test:assertEquals(10, settings.volume, 'check effect volume')
+end
+
+
+-- love.audio.setMixWithSystem
+love.test.audio.setMixWithSystem = function(test)
+ test:assertNotEquals(nil, love.audio.setMixWithSystem(true), 'check not nil')
+end
+
+
+-- love.audio.setOrientation
+love.test.audio.setOrientation = function(test)
+ -- setup
+ love.audio.setOrientation(1, 2, 3, 4, 5, 6)
+ -- tests
+ local fx, fy, fz, ux, uy, uz = love.audio.getOrientation()
+ test:assertEquals(1, fx, 'check fx orientation')
+ test:assertEquals(2, fy, 'check fy orientation')
+ test:assertEquals(3, fz, 'check fz orientation')
+ test:assertEquals(4, ux, 'check ux orientation')
+ test:assertEquals(5, uy, 'check uy orientation')
+ test:assertEquals(6, uz, 'check uz orientation')
+end
+
+
+-- love.audio.setPosition
+love.test.audio.setPosition = function(test)
+ -- setup
+ love.audio.setPosition(1, 2, 3)
+ -- tests
+ local x, y, z = love.audio.getPosition()
+ test:assertEquals(1, x, 'check x position')
+ test:assertEquals(2, y, 'check y position')
+ test:assertEquals(3, z, 'check z position')
+end
+
+
+-- love.audio.setVelocity
+love.test.audio.setVelocity = function(test)
+ -- setup
+ love.audio.setVelocity(1, 2, 3)
+ -- tests
+ local x, y, z = love.audio.getVelocity()
+ test:assertEquals(1, x, 'check x velocity')
+ test:assertEquals(2, y, 'check y velocity')
+ test:assertEquals(3, z, 'check z velocity')
+end
+
+
+-- love.audio.setVolume
+love.test.audio.setVolume = function(test)
+ -- setup
+ love.audio.setVolume(0.5)
+ -- tests
+ test:assertNotEquals(nil, love.audio.getVolume(), 'check not nil')
+ test:assertEquals(0.5, love.audio.getVolume(), 'check set to 0.5')
+end
+
+
+-- love.audio.stop
+love.test.audio.stop = function(test)
+ -- setup
+ local source = love.audio.newSource('resources/click.ogg', 'static')
+ love.audio.play(source)
+ -- tests
+ test:assertEquals(true, source:isPlaying(), 'check is playing')
+ love.audio.stop()
+ test:assertEquals(false, source:isPlaying(), 'check stopped playing')
+ source:release()
+end
\ No newline at end of file
diff --git a/testing/tests/data.lua b/testing/tests/data.lua
new file mode 100644
index 000000000..d59d192ba
--- /dev/null
+++ b/testing/tests/data.lua
@@ -0,0 +1,182 @@
+-- love.data
+
+
+-- love.data.compress
+love.test.data.compress = function(test)
+ -- here just testing each combo 'works', in decompress's test method
+ -- we actually check the compress + decompress give the right value back
+ -- setup
+ local compressions = {
+ { love.data.compress('string', 'lz4', 'helloworld', -1), 'string'},
+ { love.data.compress('string', 'lz4', 'helloworld', 0), 'string'},
+ { love.data.compress('string', 'lz4', 'helloworld', 9), 'string'},
+ { love.data.compress('string', 'zlib', 'helloworld', -1), 'string'},
+ { love.data.compress('string', 'zlib', 'helloworld', 0), 'string'},
+ { love.data.compress('string', 'zlib', 'helloworld', 9), 'string'},
+ { love.data.compress('string', 'gzip', 'helloworld', -1), 'string'},
+ { love.data.compress('string', 'gzip', 'helloworld', 0), 'string'},
+ { love.data.compress('string', 'gzip', 'helloworld', 9), 'string'},
+ { love.data.compress('data', 'lz4', 'helloworld', -1), 'userdata'},
+ { love.data.compress('data', 'lz4', 'helloworld', 0), 'userdata'},
+ { love.data.compress('data', 'lz4', 'helloworld', 9), 'userdata'},
+ { love.data.compress('data', 'zlib', 'helloworld', -1), 'userdata'},
+ { love.data.compress('data', 'zlib', 'helloworld', 0), 'userdata'},
+ { love.data.compress('data', 'zlib', 'helloworld', 9), 'userdata'},
+ { love.data.compress('data', 'gzip', 'helloworld', -1), 'userdata'},
+ { love.data.compress('data', 'gzip', 'helloworld', 0), 'userdata'},
+ { love.data.compress('data', 'gzip', 'helloworld', 9), 'userdata'}
+ }
+ -- tests
+ for c=1,#compressions do
+ test:assertNotEquals(nil, compressions[c][1], 'check not nil')
+ -- sense check return type and make sure bytedata returns are an object
+ test:assertEquals(compressions[c][2], type(compressions[c][1]), 'check is userdata')
+ if compressions[c][2] == 'userdata' then
+ test:assertNotEquals(nil, compressions[c][1]:type(), 'check has :type()')
+ end
+ end
+end
+
+
+-- love.data.decode
+love.test.data.decode = function(test)
+ -- setup
+ local str1 = love.data.encode('string', 'base64', 'helloworld', 0)
+ local str2 = love.data.encode('string', 'hex', 'helloworld')
+ local str3 = love.data.encode('data', 'base64', 'helloworld', 0)
+ local str4 = love.data.encode('data', 'hex', 'helloworld')
+ -- tests
+ test:assertEquals('helloworld', love.data.decode('string', 'base64', str1), 'check string base64 decode')
+ test:assertEquals('helloworld', love.data.decode('string', 'hex', str2), 'check string hex decode')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decode('data', 'base64', str3):getString(), 'check data base64 decode')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decode('data', 'hex', str4):getString(), 'check data hex decode')
+end
+
+
+-- love.data.decompress
+love.test.data.decompress = function(test)
+ -- setup
+ local str1 = love.data.compress('string', 'lz4', 'helloworld', -1)
+ local str2 = love.data.compress('string', 'lz4', 'helloworld', 0)
+ local str3 = love.data.compress('string', 'lz4', 'helloworld', 9)
+ local str4 = love.data.compress('string', 'zlib', 'helloworld', -1)
+ local str5 = love.data.compress('string', 'zlib', 'helloworld', 0)
+ local str6 = love.data.compress('string', 'zlib', 'helloworld', 9)
+ local str7 = love.data.compress('string', 'gzip', 'helloworld', -1)
+ local str8 = love.data.compress('string', 'gzip', 'helloworld', 0)
+ local str9 = love.data.compress('string', 'gzip', 'helloworld', 9)
+ local str10 = love.data.compress('data', 'lz4', 'helloworld', -1)
+ local str11 = love.data.compress('data', 'lz4', 'helloworld', 0)
+ local str12 = love.data.compress('data', 'lz4', 'helloworld', 9)
+ local str13 = love.data.compress('data', 'zlib', 'helloworld', -1)
+ local str14 = love.data.compress('data', 'zlib', 'helloworld', 0)
+ local str15 = love.data.compress('data', 'zlib', 'helloworld', 9)
+ local str16 = love.data.compress('data', 'gzip', 'helloworld', -1)
+ local str17 = love.data.compress('data', 'gzip', 'helloworld', 0)
+ local str18 = love.data.compress('data', 'gzip', 'helloworld', 9)
+ -- tests
+ test:assertEquals('helloworld', love.data.decompress('string', 'lz4', str1), 'check string lz4 decompress')
+ test:assertEquals('helloworld', love.data.decompress('string', 'lz4', str2), 'check string lz4 decompress')
+ test:assertEquals('helloworld', love.data.decompress('string', 'lz4', str3), 'check string lz4 decompress')
+ test:assertEquals('helloworld', love.data.decompress('string', 'zlib', str4), 'check string zlib decompress')
+ test:assertEquals('helloworld', love.data.decompress('string', 'zlib', str5), 'check string zlib decompress')
+ test:assertEquals('helloworld', love.data.decompress('string', 'zlib', str6), 'check string zlib decompress')
+ test:assertEquals('helloworld', love.data.decompress('string', 'gzip', str7), 'check string glib decompress')
+ test:assertEquals('helloworld', love.data.decompress('string', 'gzip', str8), 'check string glib decompress')
+ test:assertEquals('helloworld', love.data.decompress('string', 'gzip', str9), 'check string glib decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'lz4', str10):getString(), 'check data lz4 decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'lz4', str11):getString(), 'check data lz4 decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'lz4', str12):getString(), 'check data lz4 decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'zlib', str13):getString(), 'check data zlib decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'zlib', str14):getString(), 'check data zlib decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'zlib', str15):getString(), 'check data zlib decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'gzip', str16):getString(), 'check data glib decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'gzip', str17):getString(), 'check data glib decompress')
+ test:assertEquals(love.data.newByteData('helloworld'):getString(), love.data.decompress('data', 'gzip', str18):getString(), 'check data glib decompress')
+end
+
+
+-- love.data.encode
+love.test.data.encode = function(test)
+ -- here just testing each combo 'works', in decode's test method
+ -- we actually check the encode + decode give the right value back
+ -- setup
+ local encodes = {
+ { love.data.encode('string', 'base64', 'helloworld', 0), 'string'},
+ { love.data.encode('string', 'base64', 'helloworld', 2), 'string'},
+ { love.data.encode('string', 'hex', 'helloworld'), 'string'},
+ { love.data.encode('data', 'base64', 'helloworld', 0), 'userdata'},
+ { love.data.encode('data', 'base64', 'helloworld', 2), 'userdata'},
+ { love.data.encode('data', 'hex', 'helloworld'), 'userdata'}
+ }
+ -- tests
+ for e=1,#encodes do
+ test:assertNotEquals(nil, encodes[e][1], 'check not nil')
+ -- sense check return type and make sure bytedata returns are an object
+ test:assertEquals(encodes[e][2], type(encodes[e][1]), 'check is usedata')
+ if encodes[e][2] == 'userdata' then
+ test:assertNotEquals(nil, encodes[e][1]:type(), 'check has :type()')
+ end
+ end
+
+end
+
+
+-- love.data.getPackedSize
+-- @NOTE I dont really get the packing types of lua so best someone else writes this one
+love.test.data.getPackedSize = function(test)
+ test:skipTest('dont understand lua packing types')
+end
+
+
+-- love.data.hash
+love.test.data.hash = function(test)
+ -- setup
+ local str1 = love.data.hash('md5', 'helloworld')
+ local str2 = love.data.hash('sha1', 'helloworld')
+ local str3 = love.data.hash('sha224', 'helloworld')
+ local str4 = love.data.hash('sha256', 'helloworld')
+ local str5 = love.data.hash('sha384', 'helloworld')
+ local str6 = love.data.hash('sha512', 'helloworld')
+ -- tests
+ test:assertEquals('fc5e038d38a57032085441e7fe7010b0', love.data.encode("string", "hex", str1), 'check md5 encode')
+ test:assertEquals('6adfb183a4a2c94a2f92dab5ade762a47889a5a1', love.data.encode("string", "hex", str2), 'check sha1 encode')
+ test:assertEquals('b033d770602994efa135c5248af300d81567ad5b59cec4bccbf15bcc', love.data.encode("string", "hex", str3), 'check sha224 encode')
+ test:assertEquals('936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af', love.data.encode("string", "hex", str4), 'check sha256 encode')
+ test:assertEquals('97982a5b1414b9078103a1c008c4e3526c27b41cdbcf80790560a40f2a9bf2ed4427ab1428789915ed4b3dc07c454bd9', love.data.encode("string", "hex", str5), 'check sha384 encode')
+ test:assertEquals('1594244d52f2d8c12b142bb61f47bc2eaf503d6d9ca8480cae9fcf112f66e4967dc5e8fa98285e36db8af1b8ffa8b84cb15e0fbcf836c3deb803c13f37659a60', love.data.encode("string", "hex", str6), 'check sha512 encode')
+end
+
+
+-- love.data.newByteData
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.data.newByteData = function(test)
+ -- setup
+ local bytedata = love.data.newByteData('helloworld')
+ -- tests
+ test:assertObject(bytedata)
+end
+
+
+-- love.data.newDataView
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.data.newDataView = function(test)
+ -- setup
+ local dataview = love.data.newDataView(love.data.newByteData('helloworld'), 0, 10)
+ -- tests
+ test:assertObject(dataview)
+end
+
+
+-- love.data.pack
+-- @NOTE i dont really get the packing types of lua so best someone else writes this one
+love.test.data.pack = function(test)
+ test:skipTest('dont understand lua packing types')
+end
+
+
+-- love.data.unpack
+-- @NOTE i dont really get the packing types of lua so best someone else writes this one
+love.test.data.unpack = function(test)
+ test:skipTest('dont understand lua packing types')
+end
\ No newline at end of file
diff --git a/testing/tests/event.lua b/testing/tests/event.lua
new file mode 100644
index 000000000..8c552c03e
--- /dev/null
+++ b/testing/tests/event.lua
@@ -0,0 +1,73 @@
+-- love.event
+
+
+-- love.event.clear
+love.test.event.clear = function(test)
+ -- setup
+ love.event.push('test', 1, 2, 3)
+ love.event.push('test', 1, 2, 3)
+ love.event.push('test', 1, 2, 3)
+ -- tests
+ love.event.clear()
+ local count = 0
+ for n, a, b, c, d, e, f in love.event.poll() do
+ count = count + 1
+ end
+ test:assertEquals(0, count, 'check no events')
+end
+
+
+-- love.event.poll
+love.test.event.poll = function(test)
+ -- setup
+ love.event.push('test', 1, 2, 3)
+ love.event.push('test', 1, 2, 3)
+ love.event.push('test', 1, 2, 3)
+ -- tests
+ local count = 0
+ for n, a, b, c, d, e, f in love.event.poll() do
+ count = count + 1
+ end
+ test:assertEquals(3, count, 'check 3 events')
+end
+
+
+-- love.event.pump
+-- @NOTE dont think can really test as internal?
+love.test.event.pump = function(test)
+ test:skipTest('not sure we can test when its internal?')
+end
+
+
+-- love.event.push
+love.test.event.push = function(test)
+ -- test
+ love.event.push('add', 1, 2, 3)
+ love.event.push('ignore', 1, 2, 3)
+ love.event.push('add', 1, 2, 3)
+ love.event.push('ignore', 1, 2, 3)
+ local count = 0
+ for n, a, b, c, d, e, f in love.event.poll() do
+ if n == 'add' then
+ count = count + a + b + c
+ end
+ end
+ test:assertEquals(12, count, 'check total events')
+end
+
+
+-- love.event.quit
+love.test.event.quit = function(test)
+ love.test.module.fakequit = true
+ -- this should call love.quit, which will prevent the quit
+ -- if this fails then the test would just abort here
+ love.event.quit(0)
+ test:assertEquals(true, true, 'check quit hook called')
+end
+
+
+-- love.event.wait
+-- @NOTE not sure best way to test this one
+love.test.event.wait = function(test)
+ test:skipTest('not sure on best way to test this')
+end
\ No newline at end of file
diff --git a/testing/tests/filesystem.lua b/testing/tests/filesystem.lua
new file mode 100644
index 000000000..5779660b3
--- /dev/null
+++ b/testing/tests/filesystem.lua
@@ -0,0 +1,354 @@
+-- love.filesystem
+
+
+-- love.filesystem.append
+love.test.filesystem.append = function(test)
+ -- setup
+ love.filesystem.write('filesystem.append.txt', 'foo')
+ -- test
+ local success, message = love.filesystem.append('filesystem.append.txt', 'bar')
+ test:assertNotEquals(false, success, 'check success')
+ test:assertEquals(nil, message, 'check no error msg')
+ local contents, size = love.filesystem.read('filesystem.append.txt')
+ test:assertEquals(contents, 'foobar', 'check file contents')
+ test:assertEquals(size, 6, 'check file size')
+ love.filesystem.append('filesystem.append.txt', 'foobarfoobarfoo', 6)
+ contents, size = love.filesystem.read('filesystem.append.txt')
+ test:assertEquals(contents, 'foobarfoobar', 'check appended contents')
+ test:assertEquals(size, 12, 'check appended size')
+ -- cleanup
+ love.filesystem.remove('filesystem.append.txt')
+end
+
+
+-- love.filesystem.areSymlinksEnabled
+-- @NOTE this one, need to see if default val is consistent on platforms?
+love.test.filesystem.areSymlinksEnabled = function(test)
+ test:assertNotEquals(nil, love.filesystem.areSymlinksEnabled())
+end
+
+
+-- love.filesystem.createDirectory
+love.test.filesystem.createDirectory = function(test)
+ local success = love.filesystem.createDirectory('foo/bar')
+ test:assertNotEquals(false, success, 'check success')
+ test:assertNotEquals(nil, love.filesystem.getInfo('foo', 'directory'), 'check directory created')
+ test:assertNotEquals(nil, love.filesystem.getInfo('foo/bar', 'directory'), 'check subdirectory created')
+ -- cleanup
+ love.filesystem.remove('foo/bar')
+ love.filesystem.remove('foo')
+end
+
+
+-- love.filesystem.getAppdataDirectory
+-- @NOTE i think this is too platform dependent to be tested nicely
+love.test.filesystem.getAppdataDirectory = function(test)
+ test:assertNotEquals(nil, love.filesystem.getAppdataDirectory(), 'check not nill')
+end
+
+
+-- love.filesystem.getCRequirePath
+love.test.filesystem.getCRequirePath = function(test)
+ test:assertEquals('??', love.filesystem.getCRequirePath(), 'check default value')
+end
+
+
+-- love.filesystem.getDirectoryItems
+love.test.filesystem.getDirectoryItems = function(test)
+ -- setup
+ love.filesystem.createDirectory('foo/bar')
+ love.filesystem.write('foo/file1.txt', 'file1')
+ love.filesystem.write('foo/bar/file2.txt', 'file2')
+ -- tests
+ local files = love.filesystem.getDirectoryItems('foo')
+ local hasfile = false
+ local hasdir = false
+ for _,v in ipairs(files) do
+ local info = love.filesystem.getInfo('foo/'..v)
+ if v == 'bar' and info.type == 'directory' then hasdir = true end
+ if v == 'file1.txt' and info.type == 'file' then hasfile = true end
+ end
+ test:assertEquals(true, hasfile, 'check file exists')
+ test:assertEquals(true, hasdir, 'check directory exists')
+ -- cleanup
+ love.filesystem.remove('foo/file1.txt')
+ love.filesystem.remove('foo/bar/file2.txt')
+ love.filesystem.remove('foo/bar')
+ love.filesystem.remove('foo')
+end
+
+
+-- love.filesystem.getIdentity
+love.test.filesystem.getIdentity = function(test)
+ -- setup
+ local original = love.filesystem.getIdentity()
+ love.filesystem.setIdentity('lover')
+ -- test
+ test:assertEquals('lover', love.filesystem.getIdentity(), 'check identity matches')
+ -- cleanup
+ love.filesystem.setIdentity(original)
+end
+
+
+-- love.filesystem.getRealDirectory
+love.test.filesystem.getRealDirectory = function(test)
+ -- setup
+ love.filesystem.createDirectory('foo')
+ love.filesystem.write('foo/test.txt', 'test')
+ -- test
+ test:assertEquals(love.filesystem.getSaveDirectory(), love.filesystem.getRealDirectory('foo/test.txt'), 'check directory matches')
+ -- cleanup
+ love.filesystem.remove('foo/test.txt')
+ love.filesystem.remove('foo')
+end
+
+
+-- love.filesystem.getRequirePath
+love.test.filesystem.getRequirePath = function(test)
+ test:assertEquals('?.lua;?/init.lua', love.filesystem.getRequirePath(), 'check default value')
+end
+
+
+-- love.filesystem.getSource
+-- @NOTE i dont think we can test this cos love calls it first
+love.test.filesystem.getSource = function(test)
+ test:skipTest('not sure we can test when its internal?')
+end
+
+
+-- love.filesystem.getSourceBaseDirectory
+-- @NOTE i think this is too platform dependent to be tested nicely
+love.test.filesystem.getSourceBaseDirectory = function(test)
+ test:assertNotEquals(nil, love.filesystem.getSourceBaseDirectory(), 'check not nil')
+end
+
+
+-- love.filesystem.getUserDirectory
+-- @NOTE i think this is too platform dependent to be tested nicely
+love.test.filesystem.getUserDirectory = function(test)
+ test:assertNotEquals(nil, love.filesystem.getUserDirectory(), 'check not nil')
+end
+
+
+-- love.filesystem.getWorkingDirectory
+-- @NOTE i think this is too platform dependent to be tested nicely
+love.test.filesystem.getWorkingDirectory = function(test)
+ test:assertNotEquals(nil, love.filesystem.getWorkingDirectory(), 'check not nil')
+end
+
+
+-- love.filesystem.getSaveDirectory
+-- @NOTE i think this is too platform dependent to be tested nicely
+love.test.filesystem.getSaveDirectory = function(test)
+ test:assertNotEquals(nil, love.filesystem.getSaveDirectory(), 'check not nil')
+end
+
+
+-- love.filesystem.getInfo
+love.test.filesystem.getInfo = function(test)
+ -- setup
+ love.filesystem.createDirectory('foo/bar')
+ love.filesystem.write('foo/bar/file2.txt', 'file2')
+ -- tests
+ test:assertEquals(nil, love.filesystem.getInfo('foo/bar/file2.txt', 'directory'), 'check not directory')
+ test:assertNotEquals(nil, love.filesystem.getInfo('foo/bar/file2.txt'), 'check info not nil')
+ test:assertEquals(love.filesystem.getInfo('foo/bar/file2.txt').size, 5, 'check info size match')
+ -- @TODO test modified timestamp from info.modtime?
+ -- cleanup
+ love.filesystem.remove('foo/bar/file2.txt')
+ love.filesystem.remove('foo/bar')
+ love.filesystem.remove('foo')
+end
+
+
+-- love.filesystem.isFused
+love.test.filesystem.isFused = function(test)
+ -- kinda assuming you'd run the testsuite in a non-fused game
+ test:assertEquals(love.filesystem.isFused(), false, 'check default value')
+end
+
+
+-- love.filesystem.lines
+love.test.filesystem.lines = function(test)
+ -- setup
+ love.filesystem.write('file.txt', 'line1\nline2\nline3')
+ -- tests
+ local linenum = 1
+ for line in love.filesystem.lines('file.txt') do
+ test:assertEquals('line' .. tostring(linenum), line, 'check line matches')
+ test:assertEquals(nil, string.find(line, '\n'), 'check newline removed')
+ linenum = linenum + 1
+ end
+ -- cleanup
+ love.filesystem.remove('file.txt')
+end
+
+
+-- love.filesystem.load
+love.test.filesystem.load = function(test)
+ -- setup
+ love.filesystem.write('test1.lua', 'function test()\nreturn 1\nend\nreturn test()')
+ love.filesystem.write('test2.lua', 'function test()\nreturn 1')
+ -- tests
+ local chunk, errormsg = love.filesystem.load('faker.lua')
+ test:assertEquals(nil, chunk, 'check file doesnt exist')
+ chunk, errormsg = love.filesystem.load('test1.lua')
+ test:assertEquals(nil, errormsg, 'check no error message')
+ test:assertEquals(1, chunk(), 'check lua file runs')
+ local ok, chunk, err = pcall(love.filesystem.load, 'test2.lua')
+ test:assertEquals(false, ok, 'check invalid lua file')
+ -- cleanup
+ love.filesystem.remove('test1.lua')
+ love.filesystem.remove('test2.lua')
+end
+
+
+-- love.filesystem.mount
+love.test.filesystem.mount = function(test)
+ -- setup
+ local contents, size = love.filesystem.read('resources/test.zip') -- contains test.txt
+ love.filesystem.write('test.zip', contents, size)
+ -- tests
+ local success = love.filesystem.mount('test.zip', 'test')
+ test:assertEquals(true, success, 'check success')
+ test:assertNotEquals(nil, love.filesystem.getInfo('test'), 'check mount not nil')
+ test:assertEquals('directory', love.filesystem.getInfo('test').type, 'check directory made')
+ test:assertNotEquals(nil, love.filesystem.getInfo('test/test.txt'), 'check file not nil')
+ test:assertEquals('file', love.filesystem.getInfo('test/test.txt').type, 'check file type')
+ -- cleanup
+ love.filesystem.remove('test/test.txt')
+ love.filesystem.remove('test')
+ love.filesystem.remove('test.zip')
+end
+
+
+-- love.filesystem.newFile
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.filesystem.newFile = function(test)
+ -- setup
+ local file = love.filesystem.newFile('file1')
+ local file_r, err_r = love.filesystem.newFile('file2', 'r')
+ local file_w, err_w = love.filesystem.newFile('file2', 'w')
+ local file_a, err_a = love.filesystem.newFile('file2', 'a')
+ local file_c, err_c = love.filesystem.newFile('file2', 'c')
+ -- tests
+ test:assertNotEquals(nil, file, 'check file made')
+ test:assertNotEquals(nil, file_r, 'check file made')
+ test:assertNotEquals(nil, file_w, 'check file made')
+ test:assertNotEquals(nil, file_a, 'check file made')
+ test:assertNotEquals(nil, file_c, 'check file made')
+ -- cleanup
+ if file ~= nil then file:release() end
+ if file_r ~= nil then file_r:release() end
+ if file_w ~= nil then file_w:release() end
+ if file_a ~= nil then file_a:release() end
+ if file_c ~= nil then file_c:release() end
+end
+
+
+-- love.filesystem.newFileData
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.filesystem.newFileData = function(test)
+ local data = love.filesystem.newFileData('helloworld', 'file1')
+ test:assertNotEquals(nil, data, 'check not nil')
+ data:release()
+end
+
+
+-- love.filesystem.read
+love.test.filesystem.read = function(test)
+ local content, size = love.filesystem.read('resources/test.txt')
+ test:assertNotEquals(nil, content, 'check not nil')
+ test:assertEquals('helloworld', content, 'check content match')
+ test:assertEquals(10, size, 'check size match')
+ content, size = love.filesystem.read('resources/test.txt', 5)
+ test:assertNotEquals(nil, content, 'check not nil')
+ test:assertEquals('hello', content, 'check content match')
+ test:assertEquals(5, size, 'check size match')
+end
+
+
+-- love.filesystem.remove
+love.test.filesystem.remove = function(test)
+ -- setup
+ love.filesystem.createDirectory('foo/bar')
+ love.filesystem.write('foo/bar/file2.txt', 'helloworld')
+ -- tests
+ test:assertEquals(false, love.filesystem.remove('foo'), 'check fail when file inside')
+ test:assertEquals(false, love.filesystem.remove('foo/bar'), 'check fail when file inside')
+ test:assertEquals(true, love.filesystem.remove('foo/bar/file2.txt'), 'check file removed')
+ test:assertEquals(true, love.filesystem.remove('foo/bar'), 'check subdirectory removed')
+ test:assertEquals(true, love.filesystem.remove('foo'), 'check directory removed')
+ -- cleanup not needed here
+end
+
+
+-- love.filesystem.setCRequirePath
+love.test.filesystem.setCRequirePath = function(test)
+ love.filesystem.setCRequirePath('/??')
+ test:assertEquals('/??', love.filesystem.getCRequirePath(), 'check crequirepath value')
+ love.filesystem.setCRequirePath('??')
+end
+
+
+-- love.filesystem.setIdentity
+love.test.filesystem.setIdentity = function(test)
+ -- setup
+ local original = love.filesystem.getIdentity()
+ -- test
+ love.filesystem.setIdentity('lover')
+ test:assertEquals('lover', love.filesystem.getIdentity(), 'check indentity value')
+ -- cleanup
+ love.filesystem.setIdentity(original)
+end
+
+
+-- love.filesystem.setRequirePath
+love.test.filesystem.setRequirePath = function(test)
+ -- test
+ love.filesystem.setRequirePath('?.lua;?/start.lua')
+ test:assertEquals('?.lua;?/start.lua', love.filesystem.getRequirePath(), 'check require path')
+ -- cleanup
+ love.filesystem.setRequirePath('?.lua;?/init.lua')
+end
+
+
+-- love.filesystem.setSource
+-- @NOTE dont think can test this cos used internally?
+love.test.filesystem.setSource = function(test)
+ test:skipTest('not sure we can test when its internal?')
+end
+
+
+-- love.filesystem.unmount
+love.test.filesystem.unmount = function(test)
+ --setup
+ local contents, size = love.filesystem.read('resources/test.zip') -- contains test.txt
+ love.filesystem.write('test.zip', contents, size)
+ love.filesystem.mount('test.zip', 'test')
+ -- tests
+ test:assertNotEquals(nil, love.filesystem.getInfo('test/test.txt'), 'check mount exists')
+ love.filesystem.unmount('test.zip')
+ test:assertEquals(nil, love.filesystem.getInfo('test/test.txt'), 'check unmounted')
+ -- cleanup
+ love.filesystem.remove('test/test.txt')
+ love.filesystem.remove('test')
+ love.filesystem.remove('test.zip')
+end
+
+
+-- love.filesystem.write
+love.test.filesystem.write = function(test)
+ -- setup
+ love.filesystem.write('test1.txt', 'helloworld')
+ love.filesystem.write('test2.txt', 'helloworld', 10)
+ love.filesystem.write('test3.txt', 'helloworld', 5)
+ -- test
+ test:assertEquals('helloworld', love.filesystem.read('test1.txt'), 'check read file')
+ test:assertEquals('helloworld', love.filesystem.read('test2.txt'), 'check read all')
+ test:assertEquals('hello', love.filesystem.read('test3.txt'), 'check read partial')
+ -- cleanup
+ love.filesystem.remove('test1.txt')
+ love.filesystem.remove('test2.txt')
+ love.filesystem.remove('test3.txt')
+end
\ No newline at end of file
diff --git a/testing/tests/font.lua b/testing/tests/font.lua
new file mode 100644
index 000000000..6c5d440fb
--- /dev/null
+++ b/testing/tests/font.lua
@@ -0,0 +1,54 @@
+-- love.font
+
+
+-- love.font.newBMFontRasterizer
+-- @NOTE the wiki specifies diff. params to source code and trying to do
+-- what source code wants gives some errors still
+love.test.font.newBMFontRasterizer = function(test)
+ test:skipTest('wiki and source dont match, not sure expected usage')
+end
+
+
+-- love.font.newGlyphData
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.font.newGlyphData = function(test)
+ local img = love.image.newImageData('resources/love.png')
+ local rasterizer = love.font.newImageRasterizer(img, 'ABC', 0, 1);
+ local glyphdata = love.font.newGlyphData(rasterizer, 65)
+ test:assertObject(glyphdata)
+ img:release()
+ rasterizer:release()
+ glyphdata:release()
+end
+
+
+-- love.font.newImageRasterizer
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.font.newImageRasterizer = function(test)
+ local img = love.image.newImageData('resources/love.png')
+ local rasterizer = love.font.newImageRasterizer(img, 'ABC', 0, 1);
+ test:assertObject(rasterizer)
+ img:release()
+ rasterizer:release()
+end
+
+
+-- love.font.newRasterizer
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.font.newRasterizer = function(test)
+ local rasterizer = love.font.newRasterizer('resources/font.ttf');
+ test:assertObject(rasterizer)
+ rasterizer:release()
+end
+
+
+-- love.font.newTrueTypeRasterizer
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.font.newTrueTypeRasterizer = function(test)
+ local defaltraster = love.font.newTrueTypeRasterizer(12, "normal", 1)
+ local customraster = love.font.newTrueTypeRasterizer('resources/font.ttf', 8, "normal", 1)
+ test:assertObject(defaltraster)
+ test:assertObject(customraster)
+ defaltraster:release()
+ customraster:release()
+end
\ No newline at end of file
diff --git a/testing/tests/graphics.lua b/testing/tests/graphics.lua
new file mode 100644
index 000000000..877e6b7d6
--- /dev/null
+++ b/testing/tests/graphics.lua
@@ -0,0 +1,158 @@
+-- love.graphics
+
+
+-- DRAWING
+
+
+
+-- love.graphics.rectangle
+love.test.graphics.rectangle = function(test)
+ -- setup, draw a 16x16 red rectangle with a blue central square
+ local canvas = love.graphics.newCanvas(16, 16)
+ love.graphics.setCanvas(canvas)
+ love.graphics.clear(0, 0, 0, 1)
+ love.graphics.setColor(1, 0, 0, 1)
+ love.graphics.rectangle('fill', 0, 0, 16, 16)
+ love.graphics.setColor(0, 0, 1, 1)
+ love.graphics.rectangle('fill', 6, 6, 4, 4)
+ love.graphics.setColor(1, 1, 1, 1)
+ love.graphics.setCanvas()
+ local imgdata1 = love.graphics.readbackTexture(canvas, {16, 0, 0, 0, 16, 16})
+ -- test, check red bg and blue central square
+ local comparepixels = {
+ red = {{0,0},{15,0},{15,15},{0,15}},
+ blue = {{6,6},{9,6},{9,9},{6,9}}
+ }
+ test:assertPixels(imgdata1, comparepixels, 'fill')
+ -- clear canvas to do some line testing
+ love.graphics.setCanvas(canvas)
+ love.graphics.clear(0, 0, 0, 1)
+ love.graphics.setColor(1, 0, 0, 1)
+ love.graphics.rectangle('line', 1, 1, 15, 15) -- red border
+ love.graphics.setColor(0, 0, 1, 1)
+ love.graphics.rectangle('line', 1, 1, 2, 15) -- 3x16 left aligned blue outline
+ love.graphics.setColor(0, 1, 0, 1)
+ love.graphics.rectangle('line', 11, 1, 5, 15) -- 6x16 right aligned green outline
+ love.graphics.setCanvas()
+ local imgdata2 = love.graphics.readbackTexture(canvas, {1, 1, 0, 0, 16, 16})
+ -- -- check corners and inner corners
+ comparepixels = {
+ red = {{3,0},{9,0},{3,15,9,15}},
+ blue = {{0,0},{2,0},{0,15},{2,15}},
+ green = {{10,0},{15,0},{10,15},{15,15}},
+ black = {{1,1},{1,14},{3,1},{9,1},{3,14},{9,14},{11,1},{14,1},{11,14},{14,14}}
+ }
+ test:assertPixels(imgdata2, comparepixels, 'line')
+ -- -- write to save data for sanity checking
+ -- imgdata1:encode('png', 'love_test_graphics_rectangle_actual1.png')
+ -- imgdata2:encode('png', 'love_test_graphics_rectangle_actual2.png')
+ imgdata1:release()
+ imgdata2:release()
+end
+
+-- love.graphics.arc
+-- love.graphics.circle
+-- love.graphics.clear
+-- love.graphics.discard
+-- love.graphics.draw
+-- love.graphics.drawInstanced
+-- love.graphics.drawInstanced
+-- love.graphics.drawLayer
+-- love.graphics.ellipse
+-- love.graphics.flushBatch
+-- love.graphics.line
+-- love.graphics.points
+-- love.graphics.polygon
+-- love.graphics.present
+-- love.graphics.print
+-- love.graphics.printf
+-- love.graphics.rectangle
+
+-- love.graphics.captureScreenshot
+-- love.graphics.newArrayImage
+-- love.graphics.newCanvas
+-- love.graphics.newCubeImage
+-- love.graphics.newFont
+-- love.graphics.newImage
+-- love.graphics.newImageFont
+-- love.graphics.newMesh
+-- love.graphics.newParticleSystem
+-- love.graphics.newQuad
+-- love.graphics.newShader
+-- love.graphics.newSpriteBatch
+-- love.graphics.newText
+-- love.graphics.newVideo
+-- love.graphics.newVolumeImage
+-- love.graphics.setNewFont
+-- love.graphics.validateShader
+
+-- love.graphics.getBackgroundColor
+-- love.graphics.getBlendMode
+-- love.graphics.getCanvas
+-- love.graphics.getColor
+-- love.graphics.getColorMask
+-- love.graphics.getDefaultFilter
+-- love.graphics.getDepthMode
+-- love.graphics.getFont
+-- love.graphics.getFrontFaceWinding
+-- love.graphics.getLineJoin
+-- love.graphics.getLineStyle
+-- love.graphics.getLineWidth
+-- love.graphics.getMeshCullMode
+-- love.graphics.getPointSize
+-- love.graphics.getScissor
+-- love.graphics.getShader
+-- love.graphics.getStackDepth
+-- love.graphics.getStencilTest
+-- love.graphics.intersectScissor
+-- love.graphics.isActive
+-- love.graphics.isGammaCorrect
+-- love.graphics.isSupported
+-- love.graphics.isWireframe
+-- love.graphics.reset
+-- love.graphics.setBackgroundColor
+-- love.graphics.setBlendMode
+-- love.graphics.setCanvas
+-- love.graphics.setColor
+-- love.graphics.setColorMask
+-- love.graphics.setDefaultFilter
+-- love.graphics.setDepthMode
+-- love.graphics.setFont
+-- love.graphics.setFrontFaceWinding
+-- love.graphics.setLineJoin
+-- love.graphics.setLineStyle
+-- love.graphics.setLineWidth
+-- love.graphics.setMeshCullMode
+-- love.graphics.setPointStyle
+-- love.graphics.setScissor
+-- love.graphics.setShader
+-- love.graphics.setStencilTest
+-- love.graphics.setWireframe
+
+-- love.graphics.applyTransform
+-- love.graphics.inverseTransformPoint
+-- love.graphics.origin
+-- love.graphics.pop
+-- love.graphics.push
+-- love.graphics.replaceTransform
+-- love.graphics.rotate
+-- love.graphics.scale
+-- love.graphics.shear
+-- love.graphics.transformPoint
+-- love.graphics.translate
+
+-- love.graphics.getDPIScale
+-- love.graphics.getDimensions
+-- love.graphics.getHeight
+-- love.graphics.getPixelDimensions
+-- love.graphics.getPixelHeight
+-- love.graphics.getPixelWidth
+-- love.graphics.getWidth
+
+-- love.graphics.getCanvasformats
+-- love.graphics.getImageFormats
+-- love.graphics.getRendererInfo
+-- love.graphics.getStats
+-- love.graphics.getSupported
+-- love.graphics.getSystemLimits
+-- love.graphics.getTextureTypes
\ No newline at end of file
diff --git a/testing/tests/image.lua b/testing/tests/image.lua
new file mode 100644
index 000000000..357bc60be
--- /dev/null
+++ b/testing/tests/image.lua
@@ -0,0 +1,31 @@
+-- love.image
+
+
+-- love.image.isCompressed
+-- @NOTE really we need to test each of the files listed here:
+-- https://love2d.org/wiki/CompressedImageFormat
+love.test.image.isCompressed = function(test)
+ local compressed = love.image.isCompressed('resources/love.dxt1')
+ test:assertEquals(true, compressed, 'check dxt1 valid compressed image')
+end
+
+
+-- love.image.newCompressedData
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.image.newCompressedData = function(test)
+ local imgdata = love.image.newCompressedData('resources/love.dxt1')
+ test:assertObject(imgdata)
+ imgdata:release()
+end
+
+
+-- love.image.newImageData
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.image.newImageData = function(test)
+ local imgdata = love.image.newImageData('resources/love.png')
+ local rawdata = love.image.newImageData(16, 16, 'rgba8', nil)
+ test:assertObject(imgdata)
+ test:assertObject(rawdata)
+ imgdata:release()
+ rawdata:release()
+end
\ No newline at end of file
diff --git a/testing/tests/math.lua b/testing/tests/math.lua
new file mode 100644
index 000000000..626692c2d
--- /dev/null
+++ b/testing/tests/math.lua
@@ -0,0 +1,178 @@
+-- love.math
+
+
+-- love.math.colorFromBytes
+love.test.math.colorFromBytes = function(test)
+ local r, g, b, a = love.math.colorFromBytes(51, 51, 51, 51)
+ test:assertEquals(r, 0.2, 'check r from bytes')
+ test:assertEquals(g, 0.2, 'check g from bytes')
+ test:assertEquals(b, 0.2, 'check b from bytes')
+ test:assertEquals(a, 0.2, 'check a from bytes')
+ r, g, b, a = love.math.colorFromBytes(255, 255, 255, 255)
+ test:assertEquals(r, 1, 'check r from bytes')
+ test:assertEquals(g, 1, 'check g from bytes')
+ test:assertEquals(b, 1, 'check b from bytes')
+ test:assertEquals(a, 1, 'check a from bytes')
+ r, g, b, a = love.math.colorFromBytes(0, 0, 0, 0)
+ test:assertEquals(r, 0, 'check r from bytes')
+ test:assertEquals(g, 0, 'check g from bytes')
+ test:assertEquals(b, 0, 'check b from bytes')
+ test:assertEquals(a, 0, 'check a from bytes')
+end
+
+
+-- love.math.colorToBytes
+love.test.math.colorToBytes = function(test)
+ local r, g, b, a = love.math.colorToBytes(0.2, 0.2, 0.2, 0.2)
+ test:assertEquals(r, 51, 'check bytes from r')
+ test:assertEquals(g, 51, 'check bytes from g')
+ test:assertEquals(b, 51, 'check bytes from b')
+ test:assertEquals(a, 51, 'check bytes from a')
+ r, g, b, a = love.math.colorToBytes(1, 1, 1, 1)
+ test:assertEquals(r, 255, 'check bytes from r')
+ test:assertEquals(g, 255, 'check bytes from g')
+ test:assertEquals(b, 255, 'check bytes from b')
+ test:assertEquals(a, 255, 'check bytes from a')
+ r, g, b, a = love.math.colorToBytes(0, 0, 0, 0)
+ test:assertEquals(r, 0, 'check bytes from r')
+ test:assertEquals(g, 0, 'check bytes from g')
+ test:assertEquals(b, 0, 'check bytes from b')
+ test:assertEquals(a, 0, 'check bytes from a')
+end
+
+
+-- love.math.gammaToLinear
+-- @NOTE I tried doing the same formula as the source from MathModule.cpp
+-- but get test failues due to slight differences
+love.test.math.gammaToLinear = function(test)
+ local lr, lg, lb = love.math.gammaToLinear(1, 0.8, 0.02)
+ --local eg = ((0.8 + 0.055) / 1.055)^2.4
+ --local eb = 0.02 / 12.92
+ test:assertGreaterEqual(0, lr, 'check gamma r to linear')
+ test:assertGreaterEqual(0, lg, 'check gamma g to linear')
+ test:assertGreaterEqual(0, lb, 'check gamma b to linear')
+end
+
+
+-- love.math.getRandomSeed
+-- @NOTE whenever i run this high is always 0, is that intended?
+love.test.math.getRandomSeed = function(test)
+ local low, high = love.math.getRandomSeed()
+ test:assertGreaterEqual(0, low, 'check random seed low')
+ test:assertGreaterEqual(0, high, 'check random seed high')
+end
+
+
+-- love.math.getRandomState
+love.test.math.getRandomState = function(test)
+ local state = love.math.getRandomState()
+ test:assertNotEquals(nil, state, 'check not nil')
+end
+
+
+-- love.math.isConvex
+love.test.math.isConvex = function(test)
+ local isconvex = love.math.isConvex({0, 0, 1, 0, 1, 1, 1, 0, 0, 0}) -- square
+ local notconvex = love.math.isConvex({1, 2, 2, 4, 3, 4, 2, 1, 3, 1}) -- weird shape
+ test:assertEquals(true, isconvex, 'check polygon convex')
+ test:assertEquals(false, notconvex, 'check polygon not convex')
+end
+
+
+-- love.math.linearToGammer
+-- @NOTE I tried doing the same formula as the source from MathModule.cpp
+-- but get test failues due to slight differences
+love.test.math.linearToGamma = function(test)
+ local gr, gg, gb = love.math.linearToGamma(1, 0.8, 0.001)
+ --local eg = 1.055 * (0.8^1/2.4) - 0.055
+ --local eb = 0.001 * 12.92
+ test:assertGreaterEqual(0, gr, 'check linear r to gamme')
+ test:assertGreaterEqual(0, gg, 'check linear g to gamme')
+ test:assertGreaterEqual(0, gb, 'check linear b to gamme')
+end
+
+
+-- love.math.newBezierCurve
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.math.newBezierCurve = function(test)
+ local curve = love.math.newBezierCurve({0, 0, 0, 1, 1, 1, 2, 1})
+ test:assertObject(curve)
+ curve:release()
+end
+
+
+-- love.math.newRandomGenerator
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.math.newRandomGenerator = function(test)
+ local rangen = love.math.newRandomGenerator()
+ test:assertObject(rangen)
+ rangen:release()
+end
+
+
+-- love.math.newTransform
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.math.newTransform = function(test)
+ local transform = love.math.newTransform()
+ test:assertObject(transform)
+ transform:release()
+end
+
+
+-- love.math.noise
+love.test.math.noise = function(test)
+ local noise1 = love.math.noise(100)
+ local noise2 = love.math.noise(1, 10)
+ local noise3 = love.math.noise(1043, 31.123, 999)
+ local noise4 = love.math.noise(99.222, 10067, 8, 1843)
+ test:assertEquals(50000, math.floor(noise1*100000), 'check noise 1 dimension')
+ test:assertEquals(47863, math.floor(noise2*100000), 'check noise 2 dimensions')
+ test:assertEquals(56297, math.floor(noise3*100000), 'check noise 3 dimensions')
+ test:assertEquals(52579, math.floor(noise4*100000), 'check noise 4 dimensions')
+end
+
+
+-- love.math.random
+love.test.math.random = function(test)
+ local random = love.math.random(10)
+ local randomminmax = love.math.random(5, 100)
+ test:assertRange(random, 1, 10, 'check within 1 - 10')
+ test:assertRange(randomminmax, 5, 100, 'check within 5 - 100')
+end
+
+
+-- love.math.randomNormal
+-- @NOTE i dont _really_ get the range expected based on stddev + mean
+-- so feel free to change to be more accurate
+love.test.math.randomNormal = function(test)
+ local random = love.math.randomNormal(1, 0)
+ test:assertRange(random, -3, 3, 'check within -3 - 3')
+end
+
+
+-- love.math.setRandomSeed
+-- @NOTE same with getRandomSeed, high is always 0 when I tested it?
+love.test.math.setRandomSeed = function(test)
+ love.math.setRandomSeed(9001)
+ local low, high = love.math.getRandomSeed()
+ test:assertEquals(9001, low, 'check seed low set')
+ test:assertEquals(0, high, 'check seed high set')
+end
+
+
+-- love.math.setRandomState
+love.test.math.setRandomState = function(test)
+ local rs1 = love.math.getRandomState()
+ love.math.setRandomState(rs1)
+ local rs2 = love.math.getRandomState()
+ test:assertEquals(rs1, rs2, 'check random state set')
+end
+
+
+-- love.math.triangulate
+love.test.math.triangulate = function(test)
+ local triangles1 = love.math.triangulate({0, 0, 1, 0, 1, 1, 1, 0, 0, 0}) -- square
+ local triangles2 = love.math.triangulate({1, 2, 2, 4, 3, 4, 2, 1, 3, 1}) -- weird shape
+ test:assertEquals(3, #triangles1, 'check polygon triangles')
+ test:assertEquals(3, #triangles2, 'check polygon triangles')
+end
\ No newline at end of file
diff --git a/testing/tests/objects.lua b/testing/tests/objects.lua
new file mode 100644
index 000000000..cf39ed5c8
--- /dev/null
+++ b/testing/tests/objects.lua
@@ -0,0 +1,175 @@
+-- objects put in their own test methods to test all attributes and class methods
+
+
+-- File (love.filesystem.newFile)
+love.test.objects.File = function(test)
+
+ -- setup a file to play with
+ local file1 = love.filesystem.openFile('data.txt', 'w')
+ file1:write('helloworld')
+ test:assertObject(file1)
+ file1:close()
+
+ -- test read mode
+ file1:open('r')
+ test:assertEquals('r', file1:getMode(), 'check read mode')
+ local contents, size = file1:read()
+ test:assertEquals('helloworld', contents)
+ test:assertEquals(10, size, 'check file read')
+ test:assertEquals(10, file1:getSize())
+ local ok, err = file1:write('hello')
+ test:assertNotEquals(nil, err, 'check cant write in read mode')
+ local iterator = file1:lines()
+ test:assertNotEquals(nil, iterator, 'check can read lines')
+ test:assertEquals('data.txt', file1:getFilename(), 'check filename matches')
+ file1:close()
+
+ -- test write mode
+ file1:open('w')
+ test:assertEquals('w', file1:getMode(), 'check write mode')
+ contents, size = file1:read()
+ test:assertEquals(nil, contents, 'check cant read file in write mode')
+ test:assertEquals('string', type(size), 'check err message shown')
+ ok, err = file1:write('helloworld')
+ test:assertEquals(true, ok, 'check file write')
+ test:assertEquals(nil, err, 'check no err writing')
+
+ -- test open/closing
+ file1:open('r')
+ test:assertEquals(true, file1:isOpen(), 'check file is open')
+ file1:close()
+ test:assertEquals(false, file1:isOpen(), 'check file gets closed')
+ file1:close()
+
+ -- test buffering
+ -- @NOTE think I'm just not understanding how this is supposed to work?
+ -- I thought if buffering is enabled then nothing should get written until
+ -- buffer overflows?
+ file1:open('a')
+ ok, err = file1:setBuffer('full', 10000)
+ test:assertEquals(true, ok)
+ test:assertEquals('full', file1:getBuffer())
+ file1:write('morecontent')
+ file1:close()
+ file1:open('r')
+ contents, size = file1:read()
+ test:assertEquals('helloworld', contents, 'check buffered content wasnt written')
+ file1:close()
+
+ -- test buffering and flushing
+ file1:open('w')
+ ok, err = file1:setBuffer('full', 10000)
+ test:assertEquals(true, ok)
+ test:assertEquals('full', file1:getBuffer())
+ file1:write('replacedcontent')
+ file1:flush()
+ file1:close()
+ file1:open('r')
+ contents, size = file1:read()
+ test:assertEquals('replacedcontent', contents, 'check buffered content was written')
+ file1:close()
+
+ -- loop through file data with seek/tell until EOF
+ file1:open('r')
+ local counter = 0
+ for i=1,100 do
+ file1:seek(i)
+ test:assertEquals(i, file1:tell())
+ if file1:isEOF() == true then
+ counter = i
+ break
+ end
+ end
+ test:assertEquals(counter, 15)
+ file1:close()
+
+ file1:release()
+
+end
+
+
+-- Source (love.audio.newSource)
+-- love.test.objects.Source = function(test)
+ -- local source1 = love.audio.newSource('resources/click.ogg', 'static')
+ --source1:clone()
+ --source1:getChannelCount()
+ --source1:getDuration()
+ --source1:isRelative()
+ --source1:queue()
+ --source1:getFreeBufferCount()
+ --source1:getType()
+ --source1:isPlaying()
+ --source1:play()
+ --source1:pause()
+ --source1:stop()
+ --source1:seek()
+ --source1:tell()
+ --source1:isLooping()
+ --source1:setLooping()
+ --source1:setAirAbsorption()
+ --source1:getAirAbsorption()
+ --source1:setAttenuationDistances()
+ --source1:getAttenuationDistances()
+ --source1:setCone()
+ --source1:getCone()
+ --source1:setDirection()
+ --source1:getDirection()
+ --source1:setEffect()
+ --source1:getEffect()
+ --source1:getActiveEffects()
+ --source1:setFilter()
+ --source1:getFilter()
+ --source1:setPitch()
+ --source1:getPitch()
+ --source1:setPosition()
+ --source1:getPosition()
+ --source1:setRelative()
+ --source1:setRolloff()
+ --source1:getRolloff()
+ --source1:setVelocity()
+ --source1:getVelocity()
+ --source1:setVolume()
+ --source1:getVolume()
+ --source1:setVolumeLimits()
+ --source1:getVolumeLimits()
+-- end
+
+-- FileData (love.filesystem.newFileData)
+
+-- ByteData (love.data.newByteData)
+-- DataView (love.data.newDataView)
+
+-- FontData (love.font.newFontData)
+-- GlyphData (love.font.newGlyphData)
+-- Rasterizer (love.font.newRasterizer)
+
+-- CompressedImageData (love.image.newCompressedImageData)
+-- ImageData (love.image.newImageData)
+
+-- BezierCurve (love.math.newBezierCurve)
+-- RandomGenerator (love.math.RandomGenerator)
+-- Transform (love.math.Transform)
+
+-- Decoder (love.sound.newDecoder)
+-- SoundData (love.sound.newSoundData)
+
+-- Channel (love.thread.newChannel)
+-- Thread (love.thread.newThread)
+
+-- VideoStream (love.thread.newVideoStream)
+
+-- all the stuff from love.physics! barf
+
+-- (love.graphics objs)
+-- Canvas
+-- Font
+-- Image
+-- Framebugger
+-- Mesh
+-- ParticleSystem
+-- PixelEffect
+-- Quad
+-- Shader
+-- SpriteBatch
+-- Text
+-- Video
\ No newline at end of file
diff --git a/testing/tests/physics.lua b/testing/tests/physics.lua
new file mode 100644
index 000000000..8431f1df6
--- /dev/null
+++ b/testing/tests/physics.lua
@@ -0,0 +1,306 @@
+-- love.physics
+
+
+-- love.physics.getDistance
+love.test.physics.getDistance = function(test)
+ -- setup
+ local shape1 = love.physics.newEdgeShape(0, 0, 5, 5)
+ local shape2 = love.physics.newEdgeShape(10, 10, 15, 15)
+ local world = love.physics.newWorld(0, 0, false)
+ local body = love.physics.newBody(world, 10, 10, 'static')
+ local fixture1 = love.physics.newFixture(body, shape1, 1)
+ local fixture2 = love.physics.newFixture(body, shape2, 1)
+ -- test
+ test:assertEquals(647106, math.floor(love.physics.getDistance(fixture1, fixture2)*100000), 'check distance matches')
+ -- cleanup
+ fixture1:release()
+ fixture2:release()
+ body:release()
+ world:release()
+ shape1:release()
+ shape2:release()
+end
+
+
+-- love.physics.getMeter
+love.test.physics.getMeter = function(test)
+ love.physics.setMeter(30)
+ test:assertEquals(30, love.physics.getMeter(), 'check meter matches')
+end
+
+
+-- love.physics.newBody
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newBody = function(test)
+ -- setup
+ local world = love.physics.newWorld(1, 1, true)
+ local body = love.physics.newBody(world, 10, 10, 'static')
+ -- test
+ test:assertObject(body)
+ -- cleanup
+ body:release()
+ world:release()
+end
+
+
+-- love.physics.newChainShape
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newChainShape = function(test)
+ local obj = love.physics.newChainShape(true, 0, 0, 1, 0, 1, 1, 0, 1)
+ test:assertObject(obj)
+ obj:release()
+end
+
+
+-- love.physics.newCircleShape
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newCircleShape = function(test)
+ local obj = love.physics.newCircleShape(10)
+ test:assertObject(obj)
+ obj:release()
+end
+
+
+-- love.physics.newDistanceJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newDistanceJoint = function(test)
+ -- setup
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ -- test
+ local obj = love.physics.newDistanceJoint(body1, body2, 10, 10, 20, 20, true)
+ test:assertObject(obj)
+ -- cleanup
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newEdgeShape
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newEdgeShape = function(test)
+ local obj = love.physics.newEdgeShape(0, 0, 10, 10)
+ test:assertObject(obj)
+ obj:release()
+end
+
+
+-- love.physics.newFixture
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newFixture = function(test)
+ -- setup
+ local world = love.physics.newWorld(1, 1, true)
+ local body = love.physics.newBody(world, 10, 10, 'static')
+ local shape = love.physics.newCircleShape(10)
+ -- test
+ local obj = love.physics.newFixture(body, shape, 1)
+ test:assertObject(obj)
+ -- cleanup
+ obj:release()
+ shape:release()
+ body:release()
+ world:release()
+end
+
+
+-- love.physics.newFrictionJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newFrictionJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local obj = love.physics.newFrictionJoint(body1, body2, 15, 15, true)
+ test:assertObject(obj)
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newGearJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newGearJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local body3 = love.physics.newBody(world, 30, 30, 'static')
+ local body4 = love.physics.newBody(world, 40, 40, 'static')
+ local joint1 = love.physics.newPrismaticJoint(body1, body2, 10, 10, 20, 20, true)
+ local joint2 = love.physics.newPrismaticJoint(body3, body4, 30, 30, 40, 40, true)
+ local obj = love.physics.newGearJoint(joint1, joint2, 1, true)
+ test:assertObject(obj)
+ obj:release()
+ joint1:release()
+ joint2:release()
+ body1:release()
+ body2:release()
+ body3:release()
+ body4:release()
+ world:release()
+end
+
+
+-- love.physics.newMotorJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newMotorJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local obj = love.physics.newMotorJoint(body1, body2, 1)
+ test:assertObject(obj)
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newMouseJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newMouseJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body = love.physics.newBody(world, 10, 10, 'static')
+ local obj = love.physics.newMouseJoint(body, 10, 10)
+ test:assertObject(obj)
+ obj:release()
+ body:release()
+ world:release()
+end
+
+
+-- love.physics.newPolygonShape
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newPolygonShape = function(test)
+ local obj = love.physics.newPolygonShape({0, 0, 2, 3, 2, 1, 3, 1, 5, 1})
+ test:assertObject(obj)
+ obj:release()
+end
+
+
+-- love.physics.newPrismaticJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newPrismaticJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local obj = love.physics.newPrismaticJoint(body1, body2, 10, 10, 20, 20, true)
+ test:assertObject(obj)
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newPulleyJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newPulleyJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local obj = love.physics.newPulleyJoint(body1, body2, 10, 10, 20, 20, 15, 15, 25, 25, 1, true)
+ test:assertObject(obj)
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newRectangleShape
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newRectangleShape = function(test)
+ local shape1 = love.physics.newRectangleShape(10, 20)
+ local shape2 = love.physics.newRectangleShape(10, 10, 40, 30, 10)
+ test:assertObject(shape1)
+ test:assertObject(shape2)
+end
+
+
+-- love.physics.newRevoluteJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newRevoluteJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local obj = love.physics.newRevoluteJoint(body1, body2, 10, 10, true)
+ test:assertObject(obj)
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newRopeJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newRopeJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local obj = love.physics.newRopeJoint(body1, body2, 10, 10, 20, 20, 50, true)
+ test:assertObject(obj)
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newWeldJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newWeldJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local obj = love.physics.newWeldJoint(body1, body2, 10, 10, true)
+ test:assertObject(obj)
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newWheelJoint
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newWheelJoint = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ local body1 = love.physics.newBody(world, 10, 10, 'static')
+ local body2 = love.physics.newBody(world, 20, 20, 'static')
+ local obj = love.physics.newWheelJoint(body1, body2, 10, 10, 5, 5, true)
+ test:assertNotEquals(nil, obj)
+ test:assertEquals('userdata', type(obj))
+ test:assertNotEquals(nil, obj:type())
+ obj:release()
+ body1:release()
+ body2:release()
+ world:release()
+end
+
+
+-- love.physics.newWorld
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.physics.newWorld = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ test:assertObject(world)
+ world:release()
+end
+
+
+-- love.physics.setMeter
+love.test.physics.setMeter = function(test)
+ local world = love.physics.newWorld(1, 1, true)
+ love.physics.setMeter(30)
+ local body = love.physics.newBody(world, 300, 300, "dynamic")
+ love.physics.setMeter(10)
+ local x, y = body:getPosition()
+ test:assertEquals(100, x, 'check pos x')
+ test:assertEquals(100, y, 'check pos y')
+ body:release()
+ world:release()
+end
\ No newline at end of file
diff --git a/testing/tests/sound.lua b/testing/tests/sound.lua
new file mode 100644
index 000000000..296f6c6a0
--- /dev/null
+++ b/testing/tests/sound.lua
@@ -0,0 +1,19 @@
+-- love.sound
+
+
+-- love.sound.newDecoder
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.sound.newDecoder = function(test)
+ local decoder = love.sound.newDecoder('resources/click.ogg')
+ test:assertObject(decoder)
+end
+
+
+-- love.sound.newSoundData
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.sound.newSoundData = function(test)
+ local sounddata = love.sound.newSoundData('resources/click.ogg')
+ test:assertObject(sounddata)
+ local soundbeep = love.sound.newSoundData(math.floor((1/32)*44100), 44100, 16, 1)
+ test:assertObject(soundbeep)
+end
\ No newline at end of file
diff --git a/testing/tests/system.lua b/testing/tests/system.lua
new file mode 100644
index 000000000..9715866b5
--- /dev/null
+++ b/testing/tests/system.lua
@@ -0,0 +1,68 @@
+-- love.system
+
+
+-- love.system.getClipboardText
+love.test.system.getClipboardText = function(test)
+ -- ignore if not using window
+ if love.test.windowmode == false then return test:skipTest('clipboard only available in window mode') end
+ -- setup
+ love.system.setClipboardText('helloworld')
+ -- test
+ test:assertEquals('helloworld', love.system.getClipboardText(), 'check clipboard match')
+end
+
+
+-- love.system.getOS
+love.test.system.getOS = function(test)
+ local os = love.system.getOS()
+ test:assertMatch({'OS X', 'Windows', 'Linux', 'Android', 'iOS'}, os, 'check value matches')
+end
+
+
+-- love.system.getPowerInfo
+love.test.system.getPowerInfo = function(test)
+ local state, percent, seconds = love.system.getPowerInfo()
+ test:assertMatch({'unknown', 'battery', 'nobattery', 'charging', 'charged'}, state, 'check value matches')
+ if percent ~= nil then
+ test:assertRange(percent, 0, 100, 'check value within range')
+ end
+ if seconds ~= nil then
+ test:assertRange(seconds, 0, 100, 'check value within range')
+ end
+end
+
+
+-- love.system.getProcessorCount
+love.test.system.getProcessorCount = function(test)
+ test:assertGreaterEqual(0, love.system.getProcessorCount(), 'check not nil') -- youd hope right
+end
+
+
+-- love.system.hasBackgroundMusic
+love.test.system.hasBackgroundMusic = function(test)
+ test:assertNotEquals(nil, love.system.hasBackgroundMusic(), 'check not nil')
+end
+
+
+-- love.system.openURL
+love.test.system.openURL = function(test)
+ test:skipTest('gets annoying to test everytime')
+ --test:assertNotEquals(nil, love.system.openURL('https://love2d.org'), 'check open URL')
+end
+
+
+-- love.system.getClipboardText
+love.test.system.setClipboardText = function(test)
+ -- ignore if not using window
+ if love.test.windowmode == false then return test:skipTest('clipboard only available in window mode') end
+ -- test
+ love.system.setClipboardText('helloworld')
+ test:assertEquals('helloworld', love.system.getClipboardText(), 'check set text')
+end
+
+
+-- love.system.vibrate
+-- @NOTE cant really test this
+love.test.system.vibrate = function(test)
+ test:skipTest('cant really test this')
+end
\ No newline at end of file
diff --git a/testing/tests/thread.lua b/testing/tests/thread.lua
new file mode 100644
index 000000000..ed27e5b4d
--- /dev/null
+++ b/testing/tests/thread.lua
@@ -0,0 +1,28 @@
+-- love.thread
+
+
+-- love.thread.getChannel
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.thread.getChannel = function(test)
+ local channel = love.thread.getChannel('test')
+ test:assertObject(channel)
+ channel:release()
+end
+
+
+-- love.thread.newChannel
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.thread.newChannel = function(test)
+ local channel = love.thread.newChannel()
+ test:assertObject(channel)
+ channel:release()
+end
+
+
+-- love.thread.newThread
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.thread.newThread = function(test)
+ local thread = love.thread.newThread('classes/TestSuite.lua')
+ test:assertObject(thread)
+ thread:release()
+end
\ No newline at end of file
diff --git a/testing/tests/timer.lua b/testing/tests/timer.lua
new file mode 100644
index 000000000..8228e7333
--- /dev/null
+++ b/testing/tests/timer.lua
@@ -0,0 +1,45 @@
+-- love.timer
+
+
+-- love.timer.getAverageDelta
+-- @NOTE not sure if you could reliably get a specific delta?
+love.test.timer.getAverageDelta = function(test)
+ test:assertNotEquals(nil, love.timer.getAverageDelta(), 'check not nil')
+end
+
+-- love.timer.getDelta
+-- @NOTE not sure if you could reliably get a specific delta?
+love.test.timer.getDelta = function(test)
+ test:assertNotEquals(nil, love.timer.getDelta(), 'check not nil')
+end
+
+
+-- love.timer.getFPS
+-- @NOTE not sure if you could reliably get a specific FPS?
+love.test.timer.getFPS = function(test)
+ test:assertNotEquals(nil, love.timer.getFPS(), 'check not nil')
+end
+
+
+-- love.timer.getTime
+love.test.timer.getTime = function(test)
+ local starttime = love.timer.getTime()
+ love.timer.sleep(1)
+ local endtime = love.timer.getTime() - starttime
+ test:assertEquals(1, math.floor(endtime), 'check 1s passes')
+end
+
+
+-- love.timer.sleep
+love.test.timer.sleep = function(test)
+ local starttime = love.timer.getTime()
+ love.timer.sleep(1)
+ test:assertEquals(1, math.floor(love.timer.getTime() - starttime), 'check 1s passes')
+end
+
+
+-- love.timer.step
+-- @NOTE not sure if you could reliably get a specific step val?
+love.test.timer.step = function(test)
+ test:assertNotEquals(nil, love.timer.step(), 'check not nil')
+end
\ No newline at end of file
diff --git a/testing/tests/video.lua b/testing/tests/video.lua
new file mode 100644
index 000000000..cd806c9ce
--- /dev/null
+++ b/testing/tests/video.lua
@@ -0,0 +1,10 @@
+-- love.video
+
+
+-- love.video.newVideoStream
+-- @NOTE this is just basic nil checking, full obj test are in objects.lua
+love.test.video.newVideoStream = function(test)
+ local videostream = love.video.newVideoStream('resources/sample.ogv')
+ test:assertObject(videostream)
+ videostream:release()
+end
\ No newline at end of file
diff --git a/testing/tests/window.lua b/testing/tests/window.lua
new file mode 100644
index 000000000..d89c2bfff
--- /dev/null
+++ b/testing/tests/window.lua
@@ -0,0 +1,336 @@
+-- love.window
+
+
+-- love.window.close
+-- @NOTE closing window should cause graphics to no longer be active
+love.test.window.close = function(test)
+ love.window.close()
+ local active = false
+ if love.graphics ~= nil then active = love.graphics.isActive() end
+ test:assertEquals(false, active, 'check window active')
+ love.window.setMode(256, 256) -- reset
+end
+
+
+-- love.window.fromPixels
+-- @NOTE dependent on the DPI value returned I think
+love.test.window.fromPixels = function(test)
+ local dpi = love.window.getDPIScale()
+ local pixels = love.window.fromPixels(100)
+ test:assertEquals(100/dpi, pixels, 'check dpi ratio')
+end
+
+
+-- love.window.getDPIScale
+-- @NOTE i dont think there's a clever way to check this
+love.test.window.getDPIScale = function(test)
+ test:assertNotEquals(nil, test, 'check not nil')
+end
+
+
+-- love.window.getDesktopDimensions
+-- @NOTE dependent on hardware so best can do is not nil
+love.test.window.getDesktopDimensions = function(test)
+ local w, h = love.window.getDesktopDimensions()
+ test:assertNotEquals(nil, w, 'check not nil')
+ test:assertNotEquals(nil, h, 'check not nil')
+end
+
+
+-- love.window.getDisplayCount
+-- @NOTE cant wait for the test suite to be run headless and fail here
+love.test.window.getDisplayCount = function(test)
+ local count = love.window.getDisplayCount()
+ test:assertGreaterEqual(1, count, 'check 1 display')
+end
+
+
+-- love.window.getDisplayName
+-- @NOTE dependent on hardware so best can do is not nil
+love.test.window.getDisplayName = function(test)
+ local name = love.window.getDisplayName(1)
+ test:assertNotEquals(nil, name, 'check not nil')
+end
+
+
+-- love.window.getDisplayOrientation
+-- @NOTE dependent on hardware so best can do is not nil
+love.test.window.getDisplayOrientation = function(test)
+ local orientation = love.window.getDisplayOrientation(1)
+ test:assertNotEquals(nil, orientation, 'check not nil')
+end
+
+
+-- love.window.getFullscreen
+love.test.window.getFullscreen = function(test)
+ test:assertEquals(false, love.window.getFullscreen(), 'check not fullscreen')
+ love.window.setFullscreen(true)
+ test:assertEquals(true, love.window.getFullscreen(), 'check now fullscreen')
+ love.window.setFullscreen(false)
+end
+
+
+-- love.window.getFullscreenModes
+-- @NOTE dependent on hardware so best can do is not nil
+love.test.window.getFullscreenModes = function(test)
+ local modes = love.window.getFullscreenModes(1)
+ test:assertNotEquals(nil, modes, 'check not nil')
+end
+
+
+-- love.window.getIcon
+love.test.window.getIcon = function(test)
+ test:assertEquals(nil, love.window.getIcon(), 'check nil by default') -- nil if not set
+ local icon = love.image.newImageData('resources/love.png')
+ love.window.setIcon(icon)
+ test:assertNotEquals(nil, love.window.getIcon(), 'check not nil')
+ icon:release()
+end
+
+
+-- love.window.getMode
+-- @NOTE could prob add more checks on the flags here based on conf.lua
+love.test.window.getMode = function(test)
+ local w, h, flags = love.window.getMode()
+ test:assertEquals(256, w, 'check w')
+ test:assertEquals(256, h, 'check h')
+ test:assertEquals(false, flags["fullscreen"], 'check fullscreen')
+end
+
+
+-- love.window.getPosition
+-- @NOTE anything we could check display index agaisn't in getPosition return?
+love.test.window.getPosition = function(test)
+ love.window.setPosition(100, 100, 1)
+ local x, y, _ = love.window.getPosition()
+ test:assertEquals(100, x, 'check position x')
+ test:assertEquals(100, y, 'check position y')
+end
+
+
+-- love.window.getSafeArea
+-- @NOTE dependent on hardware so best can do is not nil
+love.test.window.getSafeArea = function(test)
+ local x, y, w, h = love.window.getSafeArea()
+ test:assertNotEquals(nil, x, 'check not nil')
+ test:assertNotEquals(nil, y, 'check not nil')
+ test:assertNotEquals(nil, w, 'check not nil')
+ test:assertNotEquals(nil, h, 'check not nil')
+end
+
+
+-- love.window.getTitle
+love.test.window.getTitle = function(test)
+ love.window.setTitle('love.testing')
+ test:assertEquals('love.testing', love.window.getTitle(), 'check title match')
+ love.window.setTitle('love.test')
+end
+
+
+-- love.window.getVSync
+love.test.window.getVSync = function(test)
+ test:assertNotEquals(nil, love.window.getVSync(), 'check not nil')
+ love.window.setVSync(false)
+ test:assertEquals(0, love.window.getVSync(), 'check vsync off')
+ love.window.setVSync(true)
+ test:assertEquals(1, love.window.getVSync(), 'check vsync on')
+end
+
+
+-- love.window.hasFocus
+-- @NOTE cant really test as cant force focus?
+love.test.window.hasFocus = function(test)
+ test:assertNotEquals(nil, love.window.hasFocus(), 'check not nil')
+end
+
+
+-- love.window.hasMouseFocus
+-- @NOTE cant really test as cant force focus?
+love.test.window.hasMouseFocus = function(test)
+ test:assertNotEquals(nil, love.window.hasMouseFocus(), 'check not nil')
+end
+
+
+-- love.window.isDisplaySleepEnabled
+love.test.window.isDisplaySleepEnabled = function(test)
+ test:assertNotEquals(nil, love.window.isDisplaySleepEnabled(), 'check not nil')
+ love.window.setDisplaySleepEnabled(false)
+ test:assertEquals(false, love.window.isDisplaySleepEnabled(), 'check sleep disabled')
+ love.window.setDisplaySleepEnabled(true)
+ test:assertEquals(true, love.window.isDisplaySleepEnabled(), 'check sleep enabled')
+end
+
+
+-- love.window.isMaximized
+love.test.window.isMaximized = function(test)
+ love.window.minimize()
+ test:assertEquals(false, love.window.isMaximized(), 'check window maximized')
+ love.window.maximize()
+ test:assertEquals(true, love.window.isMaximized(), 'check window not maximized')
+ love.window.restore()
+end
+
+
+-- love.window.isMinimized
+love.test.window.isMinimized = function(test)
+ test:assertEquals(false, love.window.isMinimized(), 'check window not minimized')
+ love.window.minimize()
+ test:assertEquals(true, love.window.isMinimized(), 'check window minimized')
+ love.window.restore()
+end
+
+
+-- love.window.isOpen
+love.test.window.isOpen = function(test)
+ test:assertEquals(true, love.window.isOpen(), 'check window open')
+ love.window.close()
+ test:assertEquals(false, love.window.isOpen(), 'check window closed')
+ love.window.setMode(256, 256) -- reset
+end
+
+
+-- love.window.isVisible
+love.test.window.isVisible = function(test)
+ test:assertEquals(true, love.window.isVisible(), 'check window visible')
+ love.window.close()
+ test:assertEquals(false, love.window.isVisible(), 'check window not visible')
+ love.window.setMode(256, 256) -- reset
+end
+
+
+-- love.window.maximize
+love.test.window.maximize = function(test)
+ love.window.maximize()
+ test:assertEquals(true, love.window.isMaximized(), 'check window maximized')
+ love.window.restore()
+end
+
+
+-- love.window.minimize
+love.test.window.minimize = function(test)
+ love.window.minimize()
+ test:assertEquals(true, love.window.isMinimized(), 'check window minimized')
+ love.window.restore()
+end
+
+
+-- love.window.requestAttention
+love.window.requestAttention = function(test)
+ test:skipTest('cant really test this')
+end
+
+
+-- love.window.restore
+love.test.window.restore = function(test)
+ love.window.minimize()
+ test:assertEquals(true, love.window.isMinimized(), 'check window minimized')
+ love.window.restore()
+ test:assertEquals(false, love.window.isMinimized(), 'check window restored')
+end
+
+
+-- love.window.setDisplaySleepEnabled
+love.test.window.setDisplaySleepEnabled = function(test)
+ love.window.setDisplaySleepEnabled(false)
+ test:assertEquals(false, love.window.isDisplaySleepEnabled(), 'check sleep disabled')
+ love.window.setDisplaySleepEnabled(true)
+ test:assertEquals(true, love.window.isDisplaySleepEnabled(), 'check sleep enabled')
+end
+
+
+-- love.window.setFullscreen
+love.test.window.setFullscreen = function(test)
+ love.window.setFullscreen(true)
+ test:assertEquals(true, love.window.getFullscreen(), 'check fullscreen')
+ love.window.setFullscreen(false)
+ test:assertEquals(false, love.window.getFullscreen(), 'check not fullscreen')
+end
+
+
+-- love.window.setIcon
+-- @NOTE could check the image data itself?
+love.test.window.setIcon = function(test)
+ local icon = love.image.newImageData('resources/love.png')
+ love.window.setIcon(icon)
+ test:assertNotEquals(nil, love.window.getIcon(), 'check icon not nil')
+ icon:release()
+end
+
+
+-- love.window.setMode
+-- @NOTE same as getMode could be checking more flag properties
+love.test.window.setMode = function(test)
+ love.window.setMode(512, 512, {
+ fullscreen = false,
+ resizable = false
+ })
+ local width, height, flags = love.window.getMode()
+ test:assertEquals(512, width, 'check window w match')
+ test:assertEquals(512, height, 'check window h match')
+ test:assertEquals(false, flags["fullscreen"], 'check window not fullscreen')
+ test:assertEquals(false, flags["resizable"], 'check window not resizeable')
+ love.window.setMode(256, 256, {
+ fullscreen = false,
+ resizable = true
+ })
+end
+
+-- love.window.setPosition
+love.test.window.setPosition = function(test)
+ love.window.setPosition(100, 100, 1)
+ local x, y, _ = love.window.getPosition()
+ test:assertEquals(100, x, 'check position x')
+ test:assertEquals(100, y, 'check position y')
+end
+
+
+-- love.window.setTitle
+love.test.window.setTitle = function(test)
+ love.window.setTitle('love.testing')
+ test:assertEquals('love.testing', love.window.getTitle(), 'check title matches')
+ love.window.setTitle('love.test')
+end
+
+
+-- love.window.setVSync
+love.test.window.setVSync = function(test)
+ love.window.setVSync(false)
+ test:assertEquals(0, love.window.getVSync(), 'check vsync off')
+ love.window.setVSync(true)
+ test:assertEquals(1, love.window.getVSync(), 'check vsync on')
+end
+
+
+-- love.window.showMessageBox
+-- @NOTE if running headless would need to skip anyway cos can't press it
+-- skipping here cos it's annoying
+love.test.window.showMessageBox = function(test)
+ test:skipTest('skipping cos annoying to test with')
+end
+
+
+-- love.window.toPixels
+love.test.window.toPixels = function(test)
+ local dpi = love.window.getDPIScale()
+ local pixels = love.window.toPixels(50)
+ test:assertEquals(50*dpi, pixels, 'check dpi ratio')
+end
+
+
+-- love.window.updateMode
+love.test.window.updateMode = function(test)
+ love.window.setMode(512, 512, {
+ fullscreen = false,
+ resizable = false
+ })
+ love.window.updateMode(256, 256, nil)
+ local width, height, flags = love.window.getMode()
+ test:assertEquals(256, width, 'check window w match')
+ test:assertEquals(256, height, 'check window h match')
+ test:assertEquals(false, flags["fullscreen"], 'check window not fullscreen')
+ test:assertEquals(false, flags["resizable"], 'check window not resizeable')
+ love.window.setMode(256, 256, {
+ fullscreen = false,
+ resizable = true
+ })
+end
\ No newline at end of file