Files
FlexLove/testing/__tests__/flexlove_test.lua
2025-11-19 12:14:58 -05:00

1335 lines
32 KiB
Lua

local luaunit = require("testing.luaunit")
require("testing.loveStub")
local FlexLove = require("FlexLove")
local Color = require("modules.Color")
local Theme = require("modules.Theme")
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.assertEquals(FlexLove._VERSION, "0.2.3")
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)
-- Extreme positions
element = FlexLove.new({ x = 1000000, y = 1000000, 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")
-- Extreme values
FlexLove.wheelmoved(1000000, 1000000)
luaunit.assertTrue(true)
FlexLove.wheelmoved(-1000000, -1000000)
luaunit.assertTrue(true)
-- 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)
-- Extreme coordinates
element = FlexLove.getElementAtPosition(1000000, 1000000)
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
function TestFlexLoveUnhappyPaths:testExtremeZIndexValues()
FlexLove.setMode("retained")
local element1 = FlexLove.new({ width = 100, height = 100, z = -1000000 })
local element2 = FlexLove.new({ width = 100, height = 100, z = 1000000 })
luaunit.assertNotNil(element1)
luaunit.assertNotNil(element2)
FlexLove.draw() -- Should not crash during z-index sorting
end
-- 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