1307 lines
31 KiB
Lua
1307 lines
31 KiB
Lua
local luaunit = require("testing.luaunit")
|
|
local ErrorHandler = require("modules.ErrorHandler")
|
|
require("testing.loveStub")
|
|
local FlexLove = require("FlexLove")
|
|
ErrorHandler.init({})
|
|
|
|
TestFlexLove = {}
|
|
|
|
function TestFlexLove:setUp()
|
|
-- Reset FlexLove state before each test
|
|
FlexLove.destroy()
|
|
FlexLove.setMode("retained")
|
|
end
|
|
|
|
function TestFlexLove:tearDown()
|
|
FlexLove.destroy()
|
|
end
|
|
|
|
-- Test: Module loads and has expected properties
|
|
function TestFlexLove:testModuleLoads()
|
|
luaunit.assertNotNil(FlexLove)
|
|
luaunit.assertNotNil(FlexLove._VERSION)
|
|
luaunit.assertNotNil(FlexLove._DESCRIPTION)
|
|
luaunit.assertNotNil(FlexLove._URL)
|
|
luaunit.assertNotNil(FlexLove._LICENSE)
|
|
end
|
|
|
|
-- Test: init() with no config
|
|
function TestFlexLove:testInitNoConfig()
|
|
FlexLove.init()
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: init() with empty config
|
|
function TestFlexLove:testInitEmptyConfig()
|
|
FlexLove.init({})
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: init() with baseScale
|
|
function TestFlexLove:testInitWithBaseScale()
|
|
FlexLove.init({
|
|
baseScale = {
|
|
width = 1920,
|
|
height = 1080,
|
|
},
|
|
})
|
|
|
|
luaunit.assertNotNil(FlexLove.baseScale)
|
|
luaunit.assertEquals(FlexLove.baseScale.width, 1920)
|
|
luaunit.assertEquals(FlexLove.baseScale.height, 1080)
|
|
end
|
|
|
|
-- Test: init() with partial baseScale (uses defaults)
|
|
function TestFlexLove:testInitWithPartialBaseScale()
|
|
FlexLove.init({
|
|
baseScale = {},
|
|
})
|
|
|
|
luaunit.assertNotNil(FlexLove.baseScale)
|
|
luaunit.assertEquals(FlexLove.baseScale.width, 1920)
|
|
luaunit.assertEquals(FlexLove.baseScale.height, 1080)
|
|
end
|
|
|
|
-- Test: init() with string theme
|
|
function TestFlexLove:testInitWithStringTheme()
|
|
-- Pre-register a theme
|
|
local theme = Theme.new({
|
|
name = "test",
|
|
components = {},
|
|
})
|
|
|
|
-- init() tries to load and then set active, which may fail if theme path doesn't exist
|
|
-- Just check that it doesn't crash
|
|
FlexLove.init({
|
|
theme = "test",
|
|
})
|
|
|
|
-- The theme setting may fail silently, so just check it doesn't crash
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: init() with table theme
|
|
function TestFlexLove:testInitWithTableTheme()
|
|
FlexLove.init({
|
|
theme = {
|
|
name = "custom",
|
|
components = {},
|
|
},
|
|
})
|
|
|
|
luaunit.assertEquals(FlexLove.defaultTheme, "custom")
|
|
end
|
|
|
|
-- Test: init() with invalid theme (should not crash)
|
|
function TestFlexLove:testInitWithInvalidTheme()
|
|
FlexLove.init({
|
|
theme = "nonexistent-theme",
|
|
})
|
|
|
|
-- Should not crash, just print warning
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: init() with immediateMode = true
|
|
function TestFlexLove:testInitWithImmediateMode()
|
|
FlexLove.init({
|
|
immediateMode = true,
|
|
})
|
|
|
|
luaunit.assertEquals(FlexLove.getMode(), "immediate")
|
|
end
|
|
|
|
-- Test: init() with immediateMode = false
|
|
function TestFlexLove:testInitWithRetainedMode()
|
|
FlexLove.init({
|
|
immediateMode = false,
|
|
})
|
|
|
|
luaunit.assertEquals(FlexLove.getMode(), "retained")
|
|
end
|
|
|
|
-- Test: init() with autoFrameManagement
|
|
function TestFlexLove:testInitWithAutoFrameManagement()
|
|
FlexLove.init({
|
|
autoFrameManagement = true,
|
|
})
|
|
|
|
luaunit.assertEquals(FlexLove._autoFrameManagement, true)
|
|
end
|
|
|
|
-- Test: init() with state configuration
|
|
function TestFlexLove:testInitWithStateConfig()
|
|
FlexLove.init({
|
|
stateRetentionFrames = 5,
|
|
maxStateEntries = 100,
|
|
})
|
|
|
|
luaunit.assertTrue(true) -- Should configure StateManager
|
|
end
|
|
|
|
-- Test: setMode() to immediate
|
|
function TestFlexLove:testSetModeImmediate()
|
|
FlexLove.setMode("immediate")
|
|
luaunit.assertTrue(FlexLove._immediateMode)
|
|
luaunit.assertFalse(FlexLove._frameStarted)
|
|
end
|
|
|
|
-- Test: setMode() to retained
|
|
function TestFlexLove:testSetModeRetained()
|
|
FlexLove.setMode("immediate") -- First set to immediate
|
|
FlexLove.setMode("retained") -- Then to retained
|
|
|
|
luaunit.assertFalse(FlexLove._immediateMode)
|
|
luaunit.assertEquals(FlexLove._frameNumber, 0)
|
|
end
|
|
|
|
-- Test: setMode() with invalid mode
|
|
function TestFlexLove:testSetModeInvalid()
|
|
local success = pcall(function()
|
|
FlexLove.setMode("invalid")
|
|
end)
|
|
|
|
luaunit.assertFalse(success)
|
|
end
|
|
|
|
-- Test: getMode() returns correct mode
|
|
function TestFlexLove:testGetMode()
|
|
FlexLove.setMode("immediate")
|
|
luaunit.assertEquals(FlexLove.getMode(), "immediate")
|
|
|
|
FlexLove.setMode("retained")
|
|
luaunit.assertEquals(FlexLove.getMode(), "retained")
|
|
end
|
|
|
|
-- Test: beginFrame() in immediate mode
|
|
function TestFlexLove:testBeginFrameImmediate()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
|
|
luaunit.assertTrue(FlexLove._frameStarted)
|
|
luaunit.assertEquals(#FlexLove._currentFrameElements, 0)
|
|
end
|
|
|
|
-- Test: beginFrame() in retained mode (should do nothing)
|
|
function TestFlexLove:testBeginFrameRetained()
|
|
FlexLove.setMode("retained")
|
|
local frameNumber = FlexLove._frameNumber or 0
|
|
FlexLove.beginFrame()
|
|
|
|
-- Frame number should not change in retained mode
|
|
luaunit.assertEquals(FlexLove._frameNumber or 0, frameNumber)
|
|
end
|
|
|
|
-- Test: endFrame() in immediate mode
|
|
function TestFlexLove:testEndFrameImmediate()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
FlexLove.endFrame()
|
|
|
|
luaunit.assertFalse(FlexLove._frameStarted)
|
|
end
|
|
|
|
-- Test: endFrame() in retained mode (should do nothing)
|
|
function TestFlexLove:testEndFrameRetained()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.endFrame()
|
|
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: new() creates element in retained mode
|
|
function TestFlexLove:testNewRetainedMode()
|
|
FlexLove.setMode("retained")
|
|
local element = FlexLove.new({ width = 100, height = 100 })
|
|
|
|
luaunit.assertNotNil(element)
|
|
luaunit.assertEquals(element.width, 100)
|
|
luaunit.assertEquals(element.height, 100)
|
|
end
|
|
|
|
-- Test: new() creates element in immediate mode
|
|
function TestFlexLove:testNewImmediateMode()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
|
|
local element = FlexLove.new({
|
|
id = "test-element",
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
luaunit.assertNotNil(element)
|
|
luaunit.assertEquals(element.width, 100)
|
|
luaunit.assertEquals(element.height, 100)
|
|
|
|
FlexLove.endFrame()
|
|
end
|
|
|
|
-- Test: new() auto-begins frame if not started
|
|
function TestFlexLove:testNewAutoBeginFrame()
|
|
FlexLove.setMode("immediate")
|
|
|
|
local element = FlexLove.new({
|
|
id = "auto-begin-test",
|
|
width = 50,
|
|
height = 50,
|
|
})
|
|
|
|
luaunit.assertNotNil(element)
|
|
luaunit.assertTrue(FlexLove._autoBeganFrame)
|
|
|
|
FlexLove.endFrame()
|
|
end
|
|
|
|
-- Test: new() generates ID if not provided (immediate mode)
|
|
function TestFlexLove:testNewGeneratesID()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
|
|
local element = FlexLove.new({ width = 100, height = 100 })
|
|
|
|
luaunit.assertNotNil(element.id)
|
|
luaunit.assertTrue(element.id ~= "")
|
|
|
|
FlexLove.endFrame()
|
|
end
|
|
|
|
-- Test: draw() with no arguments
|
|
function TestFlexLove:testDrawNoArgs()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.draw()
|
|
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: draw() with gameDrawFunc
|
|
function TestFlexLove:testDrawWithGameFunc()
|
|
FlexLove.setMode("retained")
|
|
|
|
local called = false
|
|
FlexLove.draw(function()
|
|
called = true
|
|
end)
|
|
|
|
luaunit.assertTrue(called)
|
|
end
|
|
|
|
-- Test: draw() with postDrawFunc
|
|
function TestFlexLove:testDrawWithPostFunc()
|
|
FlexLove.setMode("retained")
|
|
|
|
local called = false
|
|
FlexLove.draw(nil, function()
|
|
called = true
|
|
end)
|
|
|
|
luaunit.assertTrue(called)
|
|
end
|
|
|
|
-- Test: draw() with both functions
|
|
function TestFlexLove:testDrawWithBothFuncs()
|
|
FlexLove.setMode("retained")
|
|
|
|
local gameCalled = false
|
|
local postCalled = false
|
|
|
|
FlexLove.draw(function()
|
|
gameCalled = true
|
|
end, function()
|
|
postCalled = true
|
|
end)
|
|
|
|
luaunit.assertTrue(gameCalled)
|
|
luaunit.assertTrue(postCalled)
|
|
end
|
|
|
|
-- Test: draw() with elements (no backdrop blur)
|
|
function TestFlexLove:testDrawWithElements()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({
|
|
width = 100,
|
|
height = 100,
|
|
backgroundColor = Color.new(1, 1, 1, 1),
|
|
})
|
|
|
|
FlexLove.draw()
|
|
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: draw() auto-ends frame in immediate mode
|
|
function TestFlexLove:testDrawAutoEndFrame()
|
|
FlexLove.setMode("immediate")
|
|
|
|
local element = FlexLove.new({
|
|
id = "auto-end-test",
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
-- draw() should call endFrame() if _autoBeganFrame is true
|
|
FlexLove.draw()
|
|
|
|
luaunit.assertFalse(FlexLove._autoBeganFrame)
|
|
end
|
|
|
|
-- Test: update() with no elements
|
|
function TestFlexLove:testUpdateNoElements()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.update(0.016)
|
|
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: update() in retained mode with elements
|
|
function TestFlexLove:testUpdateRetainedMode()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
FlexLove.update(0.016)
|
|
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: update() in immediate mode (should skip element updates)
|
|
function TestFlexLove:testUpdateImmediateMode()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
|
|
local element = FlexLove.new({
|
|
id = "update-test",
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
FlexLove.endFrame()
|
|
FlexLove.update(0.016)
|
|
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: resize() with no baseScale
|
|
function TestFlexLove:testResizeNoBaseScale()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.resize()
|
|
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: resize() with baseScale
|
|
function TestFlexLove:testResizeWithBaseScale()
|
|
FlexLove.init({
|
|
baseScale = {
|
|
width = 1920,
|
|
height = 1080,
|
|
},
|
|
})
|
|
|
|
FlexLove.resize()
|
|
|
|
luaunit.assertNotNil(FlexLove.scaleFactors)
|
|
end
|
|
|
|
-- Test: resize() with elements
|
|
function TestFlexLove:testResizeWithElements()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
FlexLove.resize()
|
|
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: destroy() clears all elements
|
|
function TestFlexLove:testDestroy()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
FlexLove.destroy()
|
|
|
|
luaunit.assertEquals(#FlexLove.topElements, 0)
|
|
luaunit.assertNil(FlexLove.baseScale)
|
|
luaunit.assertNil(FlexLove._focusedElement)
|
|
end
|
|
|
|
-- Test: textinput() with no focused element
|
|
function TestFlexLove:testTextInputNoFocus()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.textinput("a")
|
|
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: textinput() with focused element
|
|
function TestFlexLove:testTextInputWithFocus()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({
|
|
width = 100,
|
|
height = 100,
|
|
editable = true,
|
|
})
|
|
|
|
FlexLove._focusedElement = element
|
|
FlexLove.textinput("a")
|
|
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: keypressed() with no focused element
|
|
function TestFlexLove:testKeyPressedNoFocus()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.keypressed("return", "return", false)
|
|
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: keypressed() with focused element
|
|
function TestFlexLove:testKeyPressedWithFocus()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({
|
|
width = 100,
|
|
height = 100,
|
|
editable = true,
|
|
})
|
|
|
|
FlexLove._focusedElement = element
|
|
FlexLove.keypressed("return", "return", false)
|
|
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: wheelmoved() in retained mode with no elements
|
|
function TestFlexLove:testWheelMovedRetainedNoElements()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.wheelmoved(0, 1)
|
|
|
|
luaunit.assertTrue(true) -- Should not error
|
|
end
|
|
|
|
-- Test: wheelmoved() in immediate mode
|
|
function TestFlexLove:testWheelMovedImmediate()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
|
|
local element = FlexLove.new({
|
|
id = "wheel-test",
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
FlexLove.endFrame()
|
|
FlexLove.wheelmoved(0, 1)
|
|
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: getStateCount() in retained mode
|
|
function TestFlexLove:testGetStateCountRetained()
|
|
FlexLove.setMode("retained")
|
|
local count = FlexLove.getStateCount()
|
|
|
|
luaunit.assertEquals(count, 0)
|
|
end
|
|
|
|
-- Test: getStateCount() in immediate mode
|
|
function TestFlexLove:testGetStateCountImmediate()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
|
|
local element = FlexLove.new({
|
|
id = "state-test",
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
FlexLove.endFrame()
|
|
|
|
local count = FlexLove.getStateCount()
|
|
luaunit.assertTrue(count >= 0)
|
|
end
|
|
|
|
-- Test: clearState() in retained mode (should do nothing)
|
|
function TestFlexLove:testClearStateRetained()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.clearState("test-id")
|
|
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: clearState() in immediate mode
|
|
function TestFlexLove:testClearStateImmediate()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
|
|
local element = FlexLove.new({
|
|
id = "clear-test",
|
|
width = 100,
|
|
height = 100,
|
|
})
|
|
|
|
FlexLove.endFrame()
|
|
|
|
FlexLove.clearState("clear-test")
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: clearAllStates() in retained mode
|
|
function TestFlexLove:testClearAllStatesRetained()
|
|
FlexLove.setMode("retained")
|
|
FlexLove.clearAllStates()
|
|
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: clearAllStates() in immediate mode
|
|
function TestFlexLove:testClearAllStatesImmediate()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
FlexLove.endFrame()
|
|
|
|
FlexLove.clearAllStates()
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: getStateStats() in retained mode
|
|
function TestFlexLove:testGetStateStatsRetained()
|
|
FlexLove.setMode("retained")
|
|
local stats = FlexLove.getStateStats()
|
|
|
|
luaunit.assertEquals(stats.stateCount, 0)
|
|
luaunit.assertEquals(stats.frameNumber, 0)
|
|
end
|
|
|
|
-- Test: getStateStats() in immediate mode
|
|
function TestFlexLove:testGetStateStatsImmediate()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
FlexLove.endFrame()
|
|
|
|
local stats = FlexLove.getStateStats()
|
|
luaunit.assertNotNil(stats)
|
|
end
|
|
|
|
-- Test: getElementAtPosition() with no elements
|
|
function TestFlexLove:testGetElementAtPositionNoElements()
|
|
FlexLove.setMode("retained")
|
|
local element = FlexLove.getElementAtPosition(50, 50)
|
|
|
|
luaunit.assertNil(element)
|
|
end
|
|
|
|
-- Test: getElementAtPosition() with element at position
|
|
function TestFlexLove:testGetElementAtPosition()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({
|
|
x = 0,
|
|
y = 0,
|
|
width = 100,
|
|
height = 100,
|
|
onEvent = function() end,
|
|
})
|
|
|
|
local found = FlexLove.getElementAtPosition(50, 50)
|
|
luaunit.assertEquals(found, element)
|
|
end
|
|
|
|
-- Test: getElementAtPosition() outside element bounds
|
|
function TestFlexLove:testGetElementAtPositionOutside()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({
|
|
x = 0,
|
|
y = 0,
|
|
width = 100,
|
|
height = 100,
|
|
onEvent = function() end,
|
|
})
|
|
|
|
local found = FlexLove.getElementAtPosition(200, 200)
|
|
luaunit.assertNil(found)
|
|
end
|
|
|
|
-- Test: deferCallback() queues callback
|
|
function TestFlexLove:testDeferCallbackQueuesCallback()
|
|
FlexLove.setMode("retained")
|
|
|
|
local called = false
|
|
FlexLove.deferCallback(function()
|
|
called = true
|
|
end)
|
|
|
|
-- Callback should not be called immediately
|
|
luaunit.assertFalse(called)
|
|
|
|
-- Callback should be called after executeDeferredCallbacks
|
|
FlexLove.draw()
|
|
luaunit.assertFalse(called) -- Still not called
|
|
|
|
FlexLove.executeDeferredCallbacks()
|
|
luaunit.assertTrue(called) -- Now called
|
|
end
|
|
|
|
-- Test: deferCallback() with multiple callbacks
|
|
function TestFlexLove:testDeferCallbackMultiple()
|
|
FlexLove.setMode("retained")
|
|
|
|
local order = {}
|
|
FlexLove.deferCallback(function()
|
|
table.insert(order, 1)
|
|
end)
|
|
FlexLove.deferCallback(function()
|
|
table.insert(order, 2)
|
|
end)
|
|
FlexLove.deferCallback(function()
|
|
table.insert(order, 3)
|
|
end)
|
|
|
|
FlexLove.draw()
|
|
FlexLove.executeDeferredCallbacks()
|
|
|
|
luaunit.assertEquals(#order, 3)
|
|
luaunit.assertEquals(order[1], 1)
|
|
luaunit.assertEquals(order[2], 2)
|
|
luaunit.assertEquals(order[3], 3)
|
|
end
|
|
|
|
-- Test: deferCallback() with non-function argument
|
|
function TestFlexLove:testDeferCallbackInvalidArgument()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- Should warn but not crash
|
|
FlexLove.deferCallback("not a function")
|
|
FlexLove.deferCallback(123)
|
|
FlexLove.deferCallback(nil)
|
|
|
|
FlexLove.draw()
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: deferCallback() clears queue after execution
|
|
function TestFlexLove:testDeferCallbackClearsQueue()
|
|
FlexLove.setMode("retained")
|
|
|
|
local callCount = 0
|
|
FlexLove.deferCallback(function()
|
|
callCount = callCount + 1
|
|
end)
|
|
|
|
FlexLove.draw()
|
|
FlexLove.executeDeferredCallbacks() -- First execution
|
|
luaunit.assertEquals(callCount, 1)
|
|
|
|
FlexLove.draw()
|
|
FlexLove.executeDeferredCallbacks() -- Second execution should not call again
|
|
luaunit.assertEquals(callCount, 1)
|
|
end
|
|
|
|
-- Test: deferCallback() handles callback errors gracefully
|
|
function TestFlexLove:testDeferCallbackWithError()
|
|
FlexLove.setMode("retained")
|
|
|
|
local called = false
|
|
FlexLove.deferCallback(function()
|
|
error("Intentional error")
|
|
end)
|
|
FlexLove.deferCallback(function()
|
|
called = true
|
|
end)
|
|
|
|
-- Should not crash, second callback should still execute
|
|
FlexLove.draw()
|
|
FlexLove.executeDeferredCallbacks()
|
|
luaunit.assertTrue(called)
|
|
end
|
|
|
|
-- Test: External modules are exposed
|
|
function TestFlexLove:testExternalModulesExposed()
|
|
luaunit.assertNotNil(FlexLove.Animation)
|
|
luaunit.assertNotNil(FlexLove.Color)
|
|
luaunit.assertNotNil(FlexLove.Theme)
|
|
luaunit.assertNotNil(FlexLove.enums)
|
|
end
|
|
|
|
-- Test: Enums are accessible
|
|
function TestFlexLove:testEnumsAccessible()
|
|
luaunit.assertNotNil(FlexLove.enums.FlexDirection)
|
|
luaunit.assertNotNil(FlexLove.enums.JustifyContent)
|
|
luaunit.assertNotNil(FlexLove.enums.AlignItems)
|
|
end
|
|
|
|
-- ==========================================
|
|
-- UNHAPPY PATH TESTS
|
|
-- ==========================================
|
|
|
|
TestFlexLoveUnhappyPaths = {}
|
|
|
|
function TestFlexLoveUnhappyPaths:setUp()
|
|
FlexLove.destroy()
|
|
FlexLove.setMode("retained")
|
|
end
|
|
|
|
function TestFlexLoveUnhappyPaths:tearDown()
|
|
FlexLove.destroy()
|
|
end
|
|
|
|
-- Test: init() with invalid config types
|
|
function TestFlexLoveUnhappyPaths:testInitWithInvalidConfigTypes()
|
|
-- nil and false should work (become {} via `config or {}`)
|
|
FlexLove.init(nil)
|
|
luaunit.assertTrue(true)
|
|
|
|
FlexLove.init(false)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- String and number will error when trying to index them (config.errorLogFile, config.baseScale, etc.)
|
|
local success = pcall(function()
|
|
FlexLove.init("invalid")
|
|
end)
|
|
-- String indexing may work in Lua (returns nil), check actual behavior
|
|
-- Actually strings can be indexed but will return nil for most keys
|
|
-- So this might not error! Let's just verify it doesn't crash
|
|
luaunit.assertTrue(true)
|
|
|
|
success = pcall(function()
|
|
FlexLove.init(123)
|
|
end)
|
|
-- Numbers can't be indexed, should error
|
|
luaunit.assertFalse(success)
|
|
end
|
|
|
|
-- Test: init() with invalid baseScale values
|
|
function TestFlexLoveUnhappyPaths:testInitWithInvalidBaseScale()
|
|
-- Negative width/height (should work, just unusual)
|
|
FlexLove.init({
|
|
baseScale = {
|
|
width = -1920,
|
|
height = -1080,
|
|
},
|
|
})
|
|
luaunit.assertTrue(true) -- Should not crash
|
|
|
|
-- Zero width/height (division by zero risk)
|
|
local success = pcall(function()
|
|
FlexLove.init({
|
|
baseScale = {
|
|
width = 0,
|
|
height = 0,
|
|
},
|
|
})
|
|
end)
|
|
-- May or may not error depending on implementation
|
|
|
|
-- Non-numeric values (should error)
|
|
success = pcall(function()
|
|
FlexLove.init({
|
|
baseScale = {
|
|
width = "invalid",
|
|
height = "invalid",
|
|
},
|
|
})
|
|
end)
|
|
luaunit.assertFalse(success) -- Should error on division
|
|
end
|
|
|
|
-- Test: init() with invalid theme types
|
|
function TestFlexLoveUnhappyPaths:testInitWithInvalidThemeTypes()
|
|
-- Numeric theme
|
|
FlexLove.init({ theme = 123 })
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Boolean theme
|
|
FlexLove.init({ theme = true })
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Function theme
|
|
FlexLove.init({ theme = function() end })
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: init() with invalid immediateMode values
|
|
function TestFlexLoveUnhappyPaths:testInitWithInvalidImmediateMode()
|
|
FlexLove.init({ immediateMode = "yes" })
|
|
luaunit.assertTrue(true)
|
|
|
|
FlexLove.init({ immediateMode = 1 })
|
|
luaunit.assertTrue(true)
|
|
|
|
FlexLove.init({ immediateMode = {} })
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: init() with invalid state config values
|
|
function TestFlexLoveUnhappyPaths:testInitWithInvalidStateConfig()
|
|
-- Negative values
|
|
FlexLove.init({
|
|
stateRetentionFrames = -5,
|
|
maxStateEntries = -100,
|
|
})
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Non-numeric values
|
|
FlexLove.init({
|
|
stateRetentionFrames = "five",
|
|
maxStateEntries = "hundred",
|
|
})
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Zero values
|
|
FlexLove.init({
|
|
stateRetentionFrames = 0,
|
|
maxStateEntries = 0,
|
|
})
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: setMode() with nil
|
|
function TestFlexLoveUnhappyPaths:testSetModeNil()
|
|
local success = pcall(function()
|
|
FlexLove.setMode(nil)
|
|
end)
|
|
luaunit.assertFalse(success)
|
|
end
|
|
|
|
-- Test: setMode() with number
|
|
function TestFlexLoveUnhappyPaths:testSetModeNumber()
|
|
local success = pcall(function()
|
|
FlexLove.setMode(123)
|
|
end)
|
|
luaunit.assertFalse(success)
|
|
end
|
|
|
|
-- Test: setMode() with table
|
|
function TestFlexLoveUnhappyPaths:testSetModeTable()
|
|
local success = pcall(function()
|
|
FlexLove.setMode({ mode = "immediate" })
|
|
end)
|
|
luaunit.assertFalse(success)
|
|
end
|
|
|
|
-- Test: setMode() with empty string
|
|
function TestFlexLoveUnhappyPaths:testSetModeEmptyString()
|
|
local success = pcall(function()
|
|
FlexLove.setMode("")
|
|
end)
|
|
luaunit.assertFalse(success)
|
|
end
|
|
|
|
-- Test: setMode() with case-sensitive variation
|
|
function TestFlexLoveUnhappyPaths:testSetModeCaseSensitive()
|
|
local success = pcall(function()
|
|
FlexLove.setMode("Immediate")
|
|
end)
|
|
luaunit.assertFalse(success)
|
|
|
|
success = pcall(function()
|
|
FlexLove.setMode("RETAINED")
|
|
end)
|
|
luaunit.assertFalse(success)
|
|
end
|
|
|
|
-- Test: beginFrame() multiple times without endFrame()
|
|
function TestFlexLoveUnhappyPaths:testBeginFrameMultipleTimes()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
local frameNum1 = FlexLove._frameNumber
|
|
|
|
FlexLove.beginFrame() -- Call again without ending
|
|
local frameNum2 = FlexLove._frameNumber
|
|
|
|
-- Frame number should increment each time
|
|
luaunit.assertTrue(frameNum2 > frameNum1)
|
|
end
|
|
|
|
-- Test: endFrame() without beginFrame()
|
|
function TestFlexLoveUnhappyPaths:testEndFrameWithoutBegin()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.endFrame() -- Should not crash
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: endFrame() multiple times
|
|
function TestFlexLoveUnhappyPaths:testEndFrameMultipleTimes()
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.beginFrame()
|
|
FlexLove.endFrame()
|
|
FlexLove.endFrame() -- Call again
|
|
luaunit.assertTrue(true) -- Should not crash
|
|
end
|
|
|
|
-- Test: new() with nil props
|
|
function TestFlexLoveUnhappyPaths:testNewWithNilProps()
|
|
FlexLove.setMode("retained")
|
|
local element = FlexLove.new(nil)
|
|
luaunit.assertNotNil(element)
|
|
end
|
|
|
|
-- Test: new() with invalid width/height
|
|
function TestFlexLoveUnhappyPaths:testNewWithInvalidDimensions()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- Negative dimensions
|
|
local element = FlexLove.new({ width = -100, height = -50 })
|
|
luaunit.assertNotNil(element)
|
|
|
|
-- Zero dimensions
|
|
element = FlexLove.new({ width = 0, height = 0 })
|
|
luaunit.assertNotNil(element)
|
|
|
|
-- Invalid string dimensions (now returns fallback 0px with warning)
|
|
local success, element = pcall(function()
|
|
return FlexLove.new({ width = "invalid", height = "invalid" })
|
|
end)
|
|
luaunit.assertTrue(success) -- Units.parse returns fallback (0, "px") instead of erroring
|
|
luaunit.assertNotNil(element)
|
|
end
|
|
|
|
-- Test: new() with invalid position
|
|
function TestFlexLoveUnhappyPaths:testNewWithInvalidPosition()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- Negative positions
|
|
local element = FlexLove.new({ x = -1000, y = -1000, width = 100, height = 100 })
|
|
luaunit.assertNotNil(element)
|
|
end
|
|
|
|
-- Test: new() with circular parent reference
|
|
function TestFlexLoveUnhappyPaths:testNewWithCircularParent()
|
|
FlexLove.setMode("retained")
|
|
|
|
local parent = FlexLove.new({ width = 200, height = 200 })
|
|
local child = FlexLove.new({ width = 100, height = 100, parent = parent })
|
|
|
|
-- Try to make parent a child of child (circular reference)
|
|
-- This should be prevented by the design
|
|
luaunit.assertNotEquals(parent.parent, child)
|
|
end
|
|
|
|
-- Test: new() in immediate mode without frame
|
|
function TestFlexLoveUnhappyPaths:testNewImmediateModeNoFrame()
|
|
FlexLove.setMode("immediate")
|
|
-- Don't call beginFrame()
|
|
|
|
local element = FlexLove.new({ width = 100, height = 100 })
|
|
luaunit.assertNotNil(element)
|
|
luaunit.assertTrue(FlexLove._autoBeganFrame)
|
|
end
|
|
|
|
-- Test: draw() with invalid function types
|
|
function TestFlexLoveUnhappyPaths:testDrawWithInvalidFunctions()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- Non-function gameDrawFunc
|
|
FlexLove.draw("not a function", nil)
|
|
luaunit.assertTrue(true)
|
|
|
|
FlexLove.draw(123, nil)
|
|
luaunit.assertTrue(true)
|
|
|
|
FlexLove.draw({}, nil)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Non-function postDrawFunc
|
|
FlexLove.draw(nil, "not a function")
|
|
luaunit.assertTrue(true)
|
|
|
|
FlexLove.draw(nil, 456)
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: draw() with function that errors
|
|
function TestFlexLoveUnhappyPaths:testDrawWithErroringFunction()
|
|
FlexLove.setMode("retained")
|
|
|
|
local success = pcall(function()
|
|
FlexLove.draw(function()
|
|
error("Intentional error")
|
|
end)
|
|
end)
|
|
|
|
luaunit.assertFalse(success)
|
|
end
|
|
|
|
-- Test: update() with invalid dt
|
|
function TestFlexLoveUnhappyPaths:testUpdateWithInvalidDt()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- Negative dt
|
|
FlexLove.update(-0.016)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Zero dt
|
|
FlexLove.update(0)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Huge dt
|
|
FlexLove.update(1000)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- nil dt
|
|
local success = pcall(function()
|
|
FlexLove.update(nil)
|
|
end)
|
|
-- May or may not error depending on implementation
|
|
end
|
|
|
|
-- Test: textinput() with invalid text
|
|
function TestFlexLoveUnhappyPaths:testTextInputWithInvalidText()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- nil text
|
|
local success = pcall(function()
|
|
FlexLove.textinput(nil)
|
|
end)
|
|
-- Should handle gracefully
|
|
|
|
-- Number
|
|
FlexLove.textinput(123)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Empty string
|
|
FlexLove.textinput("")
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Very long string
|
|
FlexLove.textinput(string.rep("a", 10000))
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: keypressed() with invalid keys
|
|
function TestFlexLoveUnhappyPaths:testKeyPressedWithInvalidKeys()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- nil key
|
|
local success = pcall(function()
|
|
FlexLove.keypressed(nil, nil, false)
|
|
end)
|
|
|
|
-- Empty strings
|
|
FlexLove.keypressed("", "", false)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Invalid key names
|
|
FlexLove.keypressed("invalidkey", "invalidscancode", false)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Non-boolean isrepeat
|
|
FlexLove.keypressed("a", "a", "yes")
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: wheelmoved() with invalid values
|
|
function TestFlexLoveUnhappyPaths:testWheelMovedWithInvalidValues()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- nil values
|
|
local success = pcall(function()
|
|
FlexLove.wheelmoved(nil, nil)
|
|
end)
|
|
-- May or may not error
|
|
end
|
|
|
|
-- Test: resize() repeatedly in quick succession
|
|
function TestFlexLoveUnhappyPaths:testResizeRapidly()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({ width = 100, height = 100 })
|
|
|
|
for i = 1, 100 do
|
|
FlexLove.resize()
|
|
end
|
|
|
|
luaunit.assertTrue(true) -- Should not crash
|
|
end
|
|
|
|
-- Test: destroy() twice
|
|
function TestFlexLoveUnhappyPaths:testDestroyTwice()
|
|
FlexLove.setMode("retained")
|
|
|
|
local element = FlexLove.new({ width = 100, height = 100 })
|
|
|
|
FlexLove.destroy()
|
|
FlexLove.destroy() -- Call again
|
|
|
|
luaunit.assertTrue(true) -- Should not crash
|
|
end
|
|
|
|
-- Test: clearState() with invalid ID types
|
|
function TestFlexLoveUnhappyPaths:testClearStateWithInvalidIds()
|
|
FlexLove.setMode("immediate")
|
|
|
|
-- nil ID (should error)
|
|
local success = pcall(function()
|
|
FlexLove.clearState(nil)
|
|
end)
|
|
luaunit.assertFalse(success)
|
|
|
|
-- Number ID (should work, gets converted to string)
|
|
FlexLove.clearState(123)
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Table ID (may error)
|
|
success = pcall(function()
|
|
FlexLove.clearState({})
|
|
end)
|
|
-- May or may not work depending on tostring implementation
|
|
|
|
-- Empty string (should work)
|
|
FlexLove.clearState("")
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Non-existent ID (should work, just does nothing)
|
|
FlexLove.clearState("nonexistent-id-12345")
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: getElementAtPosition() with invalid coordinates
|
|
function TestFlexLoveUnhappyPaths:testGetElementAtPositionWithInvalidCoords()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- Negative coordinates
|
|
local element = FlexLove.getElementAtPosition(-100, -100)
|
|
luaunit.assertNil(element)
|
|
|
|
-- nil coordinates
|
|
local success = pcall(function()
|
|
FlexLove.getElementAtPosition(nil, nil)
|
|
end)
|
|
-- May or may not error
|
|
end
|
|
|
|
-- Test: Creating element with conflicting properties
|
|
function TestFlexLoveUnhappyPaths:testNewWithConflictingProperties()
|
|
FlexLove.setMode("retained")
|
|
|
|
-- Both width auto and explicit
|
|
local element = FlexLove.new({
|
|
width = 100,
|
|
autosizing = { width = true },
|
|
})
|
|
luaunit.assertNotNil(element)
|
|
|
|
-- Conflicting positioning
|
|
element = FlexLove.new({
|
|
positioning = "flex",
|
|
x = 100, -- Absolute position with flex
|
|
y = 100,
|
|
})
|
|
luaunit.assertNotNil(element)
|
|
end
|
|
|
|
-- Test: Multiple mode switches
|
|
function TestFlexLoveUnhappyPaths:testMultipleModeSwitches()
|
|
for i = 1, 10 do
|
|
FlexLove.setMode("immediate")
|
|
FlexLove.setMode("retained")
|
|
end
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: Creating elements during draw
|
|
function TestFlexLoveUnhappyPaths:testCreatingElementsDuringDraw()
|
|
FlexLove.setMode("retained")
|
|
|
|
local drawCalled = false
|
|
FlexLove.draw(function()
|
|
-- Try to create element during draw
|
|
local element = FlexLove.new({ width = 100, height = 100 })
|
|
luaunit.assertNotNil(element)
|
|
drawCalled = true
|
|
end)
|
|
|
|
luaunit.assertTrue(drawCalled)
|
|
end
|
|
|
|
-- Test: State operations in retained mode (should do nothing)
|
|
function TestFlexLoveUnhappyPaths:testStateOperationsInRetainedMode()
|
|
FlexLove.setMode("retained")
|
|
|
|
local count = FlexLove.getStateCount()
|
|
luaunit.assertEquals(count, 0)
|
|
|
|
FlexLove.clearState("any-id")
|
|
FlexLove.clearAllStates()
|
|
|
|
local stats = FlexLove.getStateStats()
|
|
luaunit.assertEquals(stats.stateCount, 0)
|
|
luaunit.assertEquals(stats.frameNumber, 0)
|
|
end
|
|
|
|
-- Test: Extreme z-index values
|
|
|
|
-- Test: Creating deeply nested element hierarchy
|
|
function TestFlexLoveUnhappyPaths:testDeeplyNestedHierarchy()
|
|
FlexLove.setMode("retained")
|
|
|
|
local parent = FlexLove.new({ width = 500, height = 500 })
|
|
local current = parent
|
|
|
|
-- Create 100 levels of nesting
|
|
for i = 1, 100 do
|
|
local child = FlexLove.new({
|
|
width = 10,
|
|
height = 10,
|
|
parent = current,
|
|
})
|
|
current = child
|
|
end
|
|
|
|
luaunit.assertTrue(true) -- Should not crash
|
|
end
|
|
|
|
-- Test: Error logging configuration edge cases
|
|
function TestFlexLoveUnhappyPaths:testErrorLoggingEdgeCases()
|
|
-- Empty error log file path
|
|
FlexLove.init({ errorLogFile = "" })
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Invalid path characters
|
|
FlexLove.init({ errorLogFile = "/invalid/path/\0/file.log" })
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Both enableErrorLogging and errorLogFile
|
|
FlexLove.init({
|
|
enableErrorLogging = true,
|
|
errorLogFile = "test.log",
|
|
})
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
-- Test: Immediate mode frame management edge cases
|
|
function TestFlexLoveUnhappyPaths:testImmediateModeFrameEdgeCases()
|
|
FlexLove.setMode("immediate")
|
|
|
|
-- Begin, draw (should auto-end), then end again
|
|
FlexLove.beginFrame()
|
|
FlexLove.draw()
|
|
FlexLove.endFrame() -- Extra end
|
|
luaunit.assertTrue(true)
|
|
|
|
-- Multiple draws without frames
|
|
FlexLove.draw()
|
|
FlexLove.draw()
|
|
FlexLove.draw()
|
|
luaunit.assertTrue(true)
|
|
end
|
|
|
|
if not _G.RUNNING_ALL_TESTS then
|
|
os.exit(luaunit.LuaUnit.run())
|
|
end
|