easings
This commit is contained in:
310
testing/__tests__/easing_test.lua
Normal file
310
testing/__tests__/easing_test.lua
Normal file
@@ -0,0 +1,310 @@
|
||||
local luaunit = require("testing.luaunit")
|
||||
require("testing.loveStub")
|
||||
|
||||
local Easing = require("modules.Easing")
|
||||
local ErrorHandler = require("modules.ErrorHandler")
|
||||
local ErrorCodes = require("modules.ErrorCodes")
|
||||
|
||||
-- Initialize ErrorHandler
|
||||
ErrorHandler.init({ ErrorCodes = ErrorCodes })
|
||||
|
||||
TestEasing = {}
|
||||
|
||||
function TestEasing:setUp()
|
||||
-- Reset state before each test
|
||||
end
|
||||
|
||||
-- Test that all easing functions exist
|
||||
function TestEasing:testAllEasingFunctionsExist()
|
||||
local easings = {
|
||||
-- Linear
|
||||
"linear",
|
||||
-- Quad
|
||||
"easeInQuad", "easeOutQuad", "easeInOutQuad",
|
||||
-- Cubic
|
||||
"easeInCubic", "easeOutCubic", "easeInOutCubic",
|
||||
-- Quart
|
||||
"easeInQuart", "easeOutQuart", "easeInOutQuart",
|
||||
-- Quint
|
||||
"easeInQuint", "easeOutQuint", "easeInOutQuint",
|
||||
-- Expo
|
||||
"easeInExpo", "easeOutExpo", "easeInOutExpo",
|
||||
-- Sine
|
||||
"easeInSine", "easeOutSine", "easeInOutSine",
|
||||
-- Circ
|
||||
"easeInCirc", "easeOutCirc", "easeInOutCirc",
|
||||
-- Back
|
||||
"easeInBack", "easeOutBack", "easeInOutBack",
|
||||
-- Elastic
|
||||
"easeInElastic", "easeOutElastic", "easeInOutElastic",
|
||||
-- Bounce
|
||||
"easeInBounce", "easeOutBounce", "easeInOutBounce",
|
||||
}
|
||||
|
||||
for _, name in ipairs(easings) do
|
||||
luaunit.assertNotNil(Easing[name], "Easing function " .. name .. " should exist")
|
||||
luaunit.assertEquals(type(Easing[name]), "function", name .. " should be a function")
|
||||
end
|
||||
end
|
||||
|
||||
-- Test that all easing functions accept t parameter (0-1)
|
||||
function TestEasing:testEasingFunctionsAcceptParameter()
|
||||
local result = Easing.linear(0.5)
|
||||
luaunit.assertNotNil(result)
|
||||
luaunit.assertEquals(type(result), "number")
|
||||
end
|
||||
|
||||
-- Test linear easing
|
||||
function TestEasing:testLinear()
|
||||
luaunit.assertEquals(Easing.linear(0), 0)
|
||||
luaunit.assertEquals(Easing.linear(0.5), 0.5)
|
||||
luaunit.assertEquals(Easing.linear(1), 1)
|
||||
end
|
||||
|
||||
-- Test easeInQuad
|
||||
function TestEasing:testEaseInQuad()
|
||||
luaunit.assertEquals(Easing.easeInQuad(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeInQuad(0.5), 0.25, 0.01)
|
||||
luaunit.assertEquals(Easing.easeInQuad(1), 1)
|
||||
end
|
||||
|
||||
-- Test easeOutQuad
|
||||
function TestEasing:testEaseOutQuad()
|
||||
luaunit.assertEquals(Easing.easeOutQuad(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeOutQuad(0.5), 0.75, 0.01)
|
||||
luaunit.assertEquals(Easing.easeOutQuad(1), 1)
|
||||
end
|
||||
|
||||
-- Test easeInOutQuad
|
||||
function TestEasing:testEaseInOutQuad()
|
||||
luaunit.assertEquals(Easing.easeInOutQuad(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeInOutQuad(0.5), 0.5, 0.01)
|
||||
luaunit.assertEquals(Easing.easeInOutQuad(1), 1)
|
||||
end
|
||||
|
||||
-- Test easeInSine
|
||||
function TestEasing:testEaseInSine()
|
||||
luaunit.assertEquals(Easing.easeInSine(0), 0)
|
||||
local mid = Easing.easeInSine(0.5)
|
||||
luaunit.assertTrue(mid > 0 and mid < 1, "easeInSine(0.5) should be between 0 and 1")
|
||||
luaunit.assertAlmostEquals(Easing.easeInSine(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test easeOutSine
|
||||
function TestEasing:testEaseOutSine()
|
||||
luaunit.assertEquals(Easing.easeOutSine(0), 0)
|
||||
local mid = Easing.easeOutSine(0.5)
|
||||
luaunit.assertTrue(mid > 0 and mid < 1, "easeOutSine(0.5) should be between 0 and 1")
|
||||
luaunit.assertAlmostEquals(Easing.easeOutSine(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test easeInOutSine
|
||||
function TestEasing:testEaseInOutSine()
|
||||
luaunit.assertEquals(Easing.easeInOutSine(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeInOutSine(0.5), 0.5, 0.01)
|
||||
luaunit.assertAlmostEquals(Easing.easeInOutSine(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test easeInQuint
|
||||
function TestEasing:testEaseInQuint()
|
||||
luaunit.assertEquals(Easing.easeInQuint(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeInQuint(0.5), 0.03125, 0.01)
|
||||
luaunit.assertEquals(Easing.easeInQuint(1), 1)
|
||||
end
|
||||
|
||||
-- Test easeOutQuint
|
||||
function TestEasing:testEaseOutQuint()
|
||||
luaunit.assertEquals(Easing.easeOutQuint(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeOutQuint(0.5), 0.96875, 0.01)
|
||||
luaunit.assertEquals(Easing.easeOutQuint(1), 1)
|
||||
end
|
||||
|
||||
-- Test easeInCirc
|
||||
function TestEasing:testEaseInCirc()
|
||||
luaunit.assertEquals(Easing.easeInCirc(0), 0)
|
||||
local mid = Easing.easeInCirc(0.5)
|
||||
luaunit.assertTrue(mid > 0 and mid < 1, "easeInCirc(0.5) should be between 0 and 1")
|
||||
luaunit.assertAlmostEquals(Easing.easeInCirc(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test easeOutCirc
|
||||
function TestEasing:testEaseOutCirc()
|
||||
luaunit.assertEquals(Easing.easeOutCirc(0), 0)
|
||||
local mid = Easing.easeOutCirc(0.5)
|
||||
luaunit.assertTrue(mid > 0 and mid < 1, "easeOutCirc(0.5) should be between 0 and 1")
|
||||
luaunit.assertAlmostEquals(Easing.easeOutCirc(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test easeInOutCirc
|
||||
function TestEasing:testEaseInOutCirc()
|
||||
luaunit.assertEquals(Easing.easeInOutCirc(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeInOutCirc(0.5), 0.5, 0.01)
|
||||
luaunit.assertAlmostEquals(Easing.easeInOutCirc(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test easeInBack (should overshoot at start)
|
||||
function TestEasing:testEaseInBack()
|
||||
luaunit.assertEquals(Easing.easeInBack(0), 0)
|
||||
local early = Easing.easeInBack(0.3)
|
||||
luaunit.assertTrue(early < 0, "easeInBack should go negative (overshoot) early on")
|
||||
luaunit.assertAlmostEquals(Easing.easeInBack(1), 1, 0.001)
|
||||
end
|
||||
|
||||
-- Test easeOutBack (should overshoot at end)
|
||||
function TestEasing:testEaseOutBack()
|
||||
luaunit.assertAlmostEquals(Easing.easeOutBack(0), 0, 0.001)
|
||||
local late = Easing.easeOutBack(0.7)
|
||||
luaunit.assertTrue(late > 0.7, "easeOutBack should overshoot at the end")
|
||||
luaunit.assertAlmostEquals(Easing.easeOutBack(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test easeInElastic (should oscillate)
|
||||
function TestEasing:testEaseInElastic()
|
||||
luaunit.assertEquals(Easing.easeInElastic(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeInElastic(1), 1, 0.01)
|
||||
-- Elastic should go negative at some point
|
||||
local hasNegative = false
|
||||
for i = 1, 9 do
|
||||
local t = i / 10
|
||||
if Easing.easeInElastic(t) < 0 then
|
||||
hasNegative = true
|
||||
break
|
||||
end
|
||||
end
|
||||
luaunit.assertTrue(hasNegative, "easeInElastic should have negative values (oscillation)")
|
||||
end
|
||||
|
||||
-- Test easeOutElastic (should oscillate)
|
||||
function TestEasing:testEaseOutElastic()
|
||||
luaunit.assertEquals(Easing.easeOutElastic(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeOutElastic(1), 1, 0.01)
|
||||
-- Elastic should go above 1 at some point
|
||||
local hasOvershoot = false
|
||||
for i = 1, 9 do
|
||||
local t = i / 10
|
||||
if Easing.easeOutElastic(t) > 1 then
|
||||
hasOvershoot = true
|
||||
break
|
||||
end
|
||||
end
|
||||
luaunit.assertTrue(hasOvershoot, "easeOutElastic should overshoot 1 (oscillation)")
|
||||
end
|
||||
|
||||
-- Test easeInBounce
|
||||
function TestEasing:testEaseInBounce()
|
||||
luaunit.assertEquals(Easing.easeInBounce(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeInBounce(1), 1, 0.01)
|
||||
-- Bounce should have multiple "bounces" (local minima)
|
||||
local result = Easing.easeInBounce(0.5)
|
||||
luaunit.assertTrue(result >= 0 and result <= 1, "easeInBounce should stay within 0-1 range")
|
||||
end
|
||||
|
||||
-- Test easeOutBounce
|
||||
function TestEasing:testEaseOutBounce()
|
||||
luaunit.assertEquals(Easing.easeOutBounce(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeOutBounce(1), 1, 0.01)
|
||||
-- Bounce should have bounces
|
||||
local result = Easing.easeOutBounce(0.8)
|
||||
luaunit.assertTrue(result >= 0 and result <= 1, "easeOutBounce should stay within 0-1 range")
|
||||
end
|
||||
|
||||
-- Test easeInOutBounce
|
||||
function TestEasing:testEaseInOutBounce()
|
||||
luaunit.assertEquals(Easing.easeInOutBounce(0), 0)
|
||||
luaunit.assertAlmostEquals(Easing.easeInOutBounce(0.5), 0.5, 0.01)
|
||||
luaunit.assertAlmostEquals(Easing.easeInOutBounce(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test configurable back() factory
|
||||
function TestEasing:testBackFactory()
|
||||
local customBack = Easing.back(2.5)
|
||||
luaunit.assertEquals(type(customBack), "function")
|
||||
luaunit.assertEquals(customBack(0), 0)
|
||||
luaunit.assertEquals(customBack(1), 1)
|
||||
-- Should overshoot with custom amount
|
||||
local mid = customBack(0.3)
|
||||
luaunit.assertTrue(mid < 0, "Custom back easing should overshoot")
|
||||
end
|
||||
|
||||
-- Test configurable elastic() factory
|
||||
function TestEasing:testElasticFactory()
|
||||
local customElastic = Easing.elastic(1.5, 0.4)
|
||||
luaunit.assertEquals(type(customElastic), "function")
|
||||
luaunit.assertEquals(customElastic(0), 0)
|
||||
luaunit.assertAlmostEquals(customElastic(1), 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test Easing.list() method
|
||||
function TestEasing:testList()
|
||||
local list = Easing.list()
|
||||
luaunit.assertEquals(type(list), "table")
|
||||
luaunit.assertEquals(#list, 31, "Should have exactly 31 easing functions")
|
||||
|
||||
-- Check that linear is in the list
|
||||
local hasLinear = false
|
||||
for _, name in ipairs(list) do
|
||||
if name == "linear" then
|
||||
hasLinear = true
|
||||
break
|
||||
end
|
||||
end
|
||||
luaunit.assertTrue(hasLinear, "List should contain 'linear'")
|
||||
end
|
||||
|
||||
-- Test Easing.get() method
|
||||
function TestEasing:testGet()
|
||||
local linear = Easing.get("linear")
|
||||
luaunit.assertNotNil(linear)
|
||||
luaunit.assertEquals(type(linear), "function")
|
||||
luaunit.assertEquals(linear(0.5), 0.5)
|
||||
|
||||
-- Test non-existent easing
|
||||
local nonExistent = Easing.get("nonExistentEasing")
|
||||
luaunit.assertNil(nonExistent)
|
||||
end
|
||||
|
||||
-- Test that all InOut easings are symmetric around 0.5
|
||||
function TestEasing:testInOutSymmetry()
|
||||
local inOutEasings = {
|
||||
"easeInOutQuad", "easeInOutCubic", "easeInOutQuart", "easeInOutQuint",
|
||||
"easeInOutExpo", "easeInOutSine", "easeInOutCirc", "easeInOutBack",
|
||||
"easeInOutElastic", "easeInOutBounce"
|
||||
}
|
||||
|
||||
for _, name in ipairs(inOutEasings) do
|
||||
local easing = Easing[name]
|
||||
-- At t=0.5, all InOut easings should be close to 0.5
|
||||
local mid = easing(0.5)
|
||||
luaunit.assertAlmostEquals(mid, 0.5, 0.1, name .. " should be close to 0.5 at t=0.5")
|
||||
end
|
||||
end
|
||||
|
||||
-- Test boundary conditions for all easings
|
||||
function TestEasing:testBoundaryConditions()
|
||||
local easings = {
|
||||
"linear",
|
||||
"easeInQuad", "easeOutQuad", "easeInOutQuad",
|
||||
"easeInCubic", "easeOutCubic", "easeInOutCubic",
|
||||
"easeInQuart", "easeOutQuart", "easeInOutQuart",
|
||||
"easeInQuint", "easeOutQuint", "easeInOutQuint",
|
||||
"easeInExpo", "easeOutExpo", "easeInOutExpo",
|
||||
"easeInSine", "easeOutSine", "easeInOutSine",
|
||||
"easeInCirc", "easeOutCirc", "easeInOutCirc",
|
||||
"easeInBack", "easeOutBack", "easeInOutBack",
|
||||
"easeInElastic", "easeOutElastic", "easeInOutElastic",
|
||||
"easeInBounce", "easeOutBounce", "easeInOutBounce",
|
||||
}
|
||||
|
||||
for _, name in ipairs(easings) do
|
||||
local easing = Easing[name]
|
||||
-- All easings should start at 0
|
||||
local start = easing(0)
|
||||
luaunit.assertAlmostEquals(start, 0, 0.01, name .. " should start at 0")
|
||||
|
||||
-- All easings should end at 1
|
||||
local finish = easing(1)
|
||||
luaunit.assertAlmostEquals(finish, 1, 0.01, name .. " should end at 1")
|
||||
end
|
||||
end
|
||||
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
353
testing/__tests__/keyframe_animation_test.lua
Normal file
353
testing/__tests__/keyframe_animation_test.lua
Normal file
@@ -0,0 +1,353 @@
|
||||
local luaunit = require("testing.luaunit")
|
||||
require("testing.loveStub")
|
||||
|
||||
local Animation = require("modules.Animation")
|
||||
local ErrorHandler = require("modules.ErrorHandler")
|
||||
local ErrorCodes = require("modules.ErrorCodes")
|
||||
|
||||
-- Initialize ErrorHandler for Animation module
|
||||
ErrorHandler.init({ ErrorCodes = ErrorCodes })
|
||||
Animation.initializeErrorHandler(ErrorHandler)
|
||||
|
||||
TestKeyframeAnimation = {}
|
||||
|
||||
function TestKeyframeAnimation:setUp()
|
||||
-- Reset state before each test
|
||||
end
|
||||
|
||||
-- Test basic keyframe animation creation
|
||||
function TestKeyframeAnimation:testCreateKeyframeAnimation()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 2,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0, opacity = 0}},
|
||||
{at = 1, values = {x = 100, opacity = 1}},
|
||||
}
|
||||
})
|
||||
|
||||
luaunit.assertNotNil(anim)
|
||||
luaunit.assertEquals(type(anim), "table")
|
||||
luaunit.assertEquals(anim.duration, 2)
|
||||
luaunit.assertNotNil(anim.keyframes)
|
||||
luaunit.assertEquals(#anim.keyframes, 2)
|
||||
end
|
||||
|
||||
-- Test keyframe animation with multiple waypoints
|
||||
function TestKeyframeAnimation:testMultipleWaypoints()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 3,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0, opacity = 0}},
|
||||
{at = 0.25, values = {x = 50, opacity = 1}},
|
||||
{at = 0.75, values = {x = 150, opacity = 1}},
|
||||
{at = 1, values = {x = 200, opacity = 0}},
|
||||
}
|
||||
})
|
||||
|
||||
luaunit.assertEquals(#anim.keyframes, 4)
|
||||
luaunit.assertEquals(anim.keyframes[1].at, 0)
|
||||
luaunit.assertEquals(anim.keyframes[2].at, 0.25)
|
||||
luaunit.assertEquals(anim.keyframes[3].at, 0.75)
|
||||
luaunit.assertEquals(anim.keyframes[4].at, 1)
|
||||
end
|
||||
|
||||
-- Test keyframe sorting
|
||||
function TestKeyframeAnimation:testKeyframeSorting()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 1, values = {x = 100}},
|
||||
{at = 0, values = {x = 0}},
|
||||
{at = 0.5, values = {x = 50}},
|
||||
}
|
||||
})
|
||||
|
||||
-- Should be sorted by 'at' position
|
||||
luaunit.assertEquals(anim.keyframes[1].at, 0)
|
||||
luaunit.assertEquals(anim.keyframes[2].at, 0.5)
|
||||
luaunit.assertEquals(anim.keyframes[3].at, 1)
|
||||
end
|
||||
|
||||
-- Test keyframe interpolation at start
|
||||
function TestKeyframeAnimation:testInterpolationAtStart()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0, opacity = 0}},
|
||||
{at = 1, values = {x = 100, opacity = 1}},
|
||||
}
|
||||
})
|
||||
|
||||
anim.elapsed = 0
|
||||
local result = anim:interpolate()
|
||||
|
||||
luaunit.assertNotNil(result.x)
|
||||
luaunit.assertNotNil(result.opacity)
|
||||
luaunit.assertAlmostEquals(result.x, 0, 0.01)
|
||||
luaunit.assertAlmostEquals(result.opacity, 0, 0.01)
|
||||
end
|
||||
|
||||
-- Test keyframe interpolation at end
|
||||
function TestKeyframeAnimation:testInterpolationAtEnd()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0, opacity = 0}},
|
||||
{at = 1, values = {x = 100, opacity = 1}},
|
||||
}
|
||||
})
|
||||
|
||||
anim.elapsed = 1
|
||||
local result = anim:interpolate()
|
||||
|
||||
luaunit.assertAlmostEquals(result.x, 100, 0.01)
|
||||
luaunit.assertAlmostEquals(result.opacity, 1, 0.01)
|
||||
end
|
||||
|
||||
-- Test keyframe interpolation at midpoint
|
||||
function TestKeyframeAnimation:testInterpolationAtMidpoint()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0}},
|
||||
{at = 1, values = {x = 100}},
|
||||
}
|
||||
})
|
||||
|
||||
anim.elapsed = 0.5
|
||||
local result = anim:interpolate()
|
||||
|
||||
luaunit.assertAlmostEquals(result.x, 50, 0.01)
|
||||
end
|
||||
|
||||
-- Test per-keyframe easing
|
||||
function TestKeyframeAnimation:testPerKeyframeEasing()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0}, easing = "easeInQuad"},
|
||||
{at = 0.5, values = {x = 50}, easing = "linear"},
|
||||
{at = 1, values = {x = 100}},
|
||||
}
|
||||
})
|
||||
|
||||
-- At t=0.25 (middle of first segment with easeInQuad)
|
||||
anim.elapsed = 0.25
|
||||
anim._resultDirty = true -- Mark dirty to force recalculation
|
||||
local result1 = anim:interpolate()
|
||||
-- easeInQuad at 0.5 should give 0.25, so x = 0 + (50-0) * 0.25 = 12.5
|
||||
luaunit.assertTrue(result1.x < 25, "easeInQuad should slow start")
|
||||
|
||||
-- At t=0.75 (middle of second segment with linear)
|
||||
anim.elapsed = 0.75
|
||||
anim._resultDirty = true -- Mark dirty to force recalculation
|
||||
local result2 = anim:interpolate()
|
||||
-- linear at 0.5 should give 0.5, so x = 50 + (100-50) * 0.5 = 75
|
||||
luaunit.assertAlmostEquals(result2.x, 75, 1)
|
||||
end
|
||||
|
||||
-- Test findKeyframes method
|
||||
function TestKeyframeAnimation:testFindKeyframes()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0}},
|
||||
{at = 0.25, values = {x = 25}},
|
||||
{at = 0.75, values = {x = 75}},
|
||||
{at = 1, values = {x = 100}},
|
||||
}
|
||||
})
|
||||
|
||||
-- Test finding keyframes at different progress values
|
||||
local prev1, next1 = anim:findKeyframes(0.1)
|
||||
luaunit.assertEquals(prev1.at, 0)
|
||||
luaunit.assertEquals(next1.at, 0.25)
|
||||
|
||||
local prev2, next2 = anim:findKeyframes(0.5)
|
||||
luaunit.assertEquals(prev2.at, 0.25)
|
||||
luaunit.assertEquals(next2.at, 0.75)
|
||||
|
||||
local prev3, next3 = anim:findKeyframes(0.9)
|
||||
luaunit.assertEquals(prev3.at, 0.75)
|
||||
luaunit.assertEquals(next3.at, 1)
|
||||
end
|
||||
|
||||
-- Test keyframe animation with update
|
||||
function TestKeyframeAnimation:testKeyframeAnimationUpdate()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {opacity = 0}},
|
||||
{at = 1, values = {opacity = 1}},
|
||||
}
|
||||
})
|
||||
|
||||
-- Update halfway through
|
||||
anim:update(0.5)
|
||||
local result = anim:interpolate()
|
||||
|
||||
luaunit.assertAlmostEquals(result.opacity, 0.5, 0.01)
|
||||
luaunit.assertFalse(anim:update(0)) -- Not complete yet
|
||||
|
||||
-- Update to completion
|
||||
luaunit.assertTrue(anim:update(0.6)) -- Should complete
|
||||
luaunit.assertEquals(anim:getState(), "completed")
|
||||
end
|
||||
|
||||
-- Test keyframe animation with callbacks
|
||||
function TestKeyframeAnimation:testKeyframeAnimationCallbacks()
|
||||
local startCalled = false
|
||||
local updateCalled = false
|
||||
local completeCalled = false
|
||||
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0}},
|
||||
{at = 1, values = {x = 100}},
|
||||
},
|
||||
onStart = function() startCalled = true end,
|
||||
onUpdate = function() updateCalled = true end,
|
||||
onComplete = function() completeCalled = true end,
|
||||
})
|
||||
|
||||
anim:update(0.5)
|
||||
luaunit.assertTrue(startCalled)
|
||||
luaunit.assertTrue(updateCalled)
|
||||
luaunit.assertFalse(completeCalled)
|
||||
|
||||
anim:update(0.6)
|
||||
luaunit.assertTrue(completeCalled)
|
||||
end
|
||||
|
||||
-- Test missing keyframes (error handling)
|
||||
function TestKeyframeAnimation:testMissingKeyframes()
|
||||
-- Should create default keyframes with warning
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {}
|
||||
})
|
||||
|
||||
luaunit.assertNotNil(anim)
|
||||
luaunit.assertEquals(#anim.keyframes, 2) -- Should have default start and end
|
||||
end
|
||||
|
||||
-- Test single keyframe (error handling)
|
||||
function TestKeyframeAnimation:testSingleKeyframe()
|
||||
-- Should create default keyframes with warning
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0.5, values = {x = 50}}
|
||||
}
|
||||
})
|
||||
|
||||
luaunit.assertNotNil(anim)
|
||||
luaunit.assertTrue(#anim.keyframes >= 2) -- Should have at least 2 keyframes
|
||||
end
|
||||
|
||||
-- Test keyframes without start (at=0)
|
||||
function TestKeyframeAnimation:testKeyframesWithoutStart()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0.5, values = {x = 50}},
|
||||
{at = 1, values = {x = 100}},
|
||||
}
|
||||
})
|
||||
|
||||
-- Should auto-add keyframe at 0
|
||||
luaunit.assertEquals(anim.keyframes[1].at, 0)
|
||||
luaunit.assertEquals(anim.keyframes[1].values.x, 50) -- Should copy first keyframe values
|
||||
end
|
||||
|
||||
-- Test keyframes without end (at=1)
|
||||
function TestKeyframeAnimation:testKeyframesWithoutEnd()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0}},
|
||||
{at = 0.5, values = {x = 50}},
|
||||
}
|
||||
})
|
||||
|
||||
-- Should auto-add keyframe at 1
|
||||
luaunit.assertEquals(anim.keyframes[#anim.keyframes].at, 1)
|
||||
luaunit.assertEquals(anim.keyframes[#anim.keyframes].values.x, 50) -- Should copy last keyframe values
|
||||
end
|
||||
|
||||
-- Test keyframe with invalid props
|
||||
function TestKeyframeAnimation:testInvalidKeyframeProps()
|
||||
-- Should handle gracefully with warnings
|
||||
local anim = Animation.keyframes({
|
||||
duration = 0, -- Invalid
|
||||
keyframes = "not a table" -- Invalid
|
||||
})
|
||||
|
||||
luaunit.assertNotNil(anim)
|
||||
luaunit.assertEquals(anim.duration, 1) -- Should use default
|
||||
end
|
||||
|
||||
-- Test complex multi-property keyframes
|
||||
function TestKeyframeAnimation:testMultiPropertyKeyframes()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 2,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0, y = 0, opacity = 0, width = 50}},
|
||||
{at = 0.33, values = {x = 100, y = 50, opacity = 1, width = 100}},
|
||||
{at = 0.66, values = {x = 200, y = 100, opacity = 1, width = 150}},
|
||||
{at = 1, values = {x = 300, y = 150, opacity = 0, width = 200}},
|
||||
}
|
||||
})
|
||||
|
||||
-- Test interpolation at 0.5 (middle of second segment)
|
||||
anim.elapsed = 1.0 -- t = 0.5
|
||||
local result = anim:interpolate()
|
||||
|
||||
luaunit.assertNotNil(result.x)
|
||||
luaunit.assertNotNil(result.y)
|
||||
luaunit.assertNotNil(result.opacity)
|
||||
luaunit.assertNotNil(result.width)
|
||||
|
||||
-- Should be interpolating between keyframes at 0.33 and 0.66
|
||||
luaunit.assertTrue(result.x > 100 and result.x < 200)
|
||||
luaunit.assertTrue(result.y > 50 and result.y < 100)
|
||||
end
|
||||
|
||||
-- Test keyframe with easing function (not string)
|
||||
function TestKeyframeAnimation:testKeyframeWithEasingFunction()
|
||||
local customEasing = function(t) return t * t end
|
||||
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0}, easing = customEasing},
|
||||
{at = 1, values = {x = 100}},
|
||||
}
|
||||
})
|
||||
|
||||
anim.elapsed = 0.5
|
||||
local result = anim:interpolate()
|
||||
|
||||
-- At t=0.5, easing(0.5) = 0.25, so x = 0 + 100 * 0.25 = 25
|
||||
luaunit.assertAlmostEquals(result.x, 25, 1)
|
||||
end
|
||||
|
||||
-- Test caching behavior with keyframes
|
||||
function TestKeyframeAnimation:testKeyframeCaching()
|
||||
local anim = Animation.keyframes({
|
||||
duration = 1,
|
||||
keyframes = {
|
||||
{at = 0, values = {x = 0}},
|
||||
{at = 1, values = {x = 100}},
|
||||
}
|
||||
})
|
||||
|
||||
anim.elapsed = 0.5
|
||||
local result1 = anim:interpolate()
|
||||
local result2 = anim:interpolate() -- Should return cached result
|
||||
|
||||
luaunit.assertEquals(result1, result2) -- Should be same table
|
||||
end
|
||||
|
||||
os.exit(luaunit.LuaUnit.run())
|
||||
Reference in New Issue
Block a user