Files
FlexLove/testing/__tests__/10_performance_tests.lua
2025-11-03 11:49:06 -05:00

1412 lines
49 KiB
Lua

package.path = package.path .. ";?.lua"
local luaunit = require("testing.luaunit")
require("testing.loveStub")
local FlexLove = require("FlexLove")
local Gui, enums = FlexLove.Gui, FlexLove.enums
local Positioning = enums.Positioning
local FlexDirection = enums.FlexDirection
local JustifyContent = enums.JustifyContent
local AlignItems = enums.AlignItems
local FlexWrap = enums.FlexWrap
-- Create test cases for performance testing
TestPerformance = {}
function TestPerformance:setUp()
-- Clean up before each test
Gui.destroy()
end
function TestPerformance:tearDown()
-- Clean up after each test
Gui.destroy()
end
-- Helper function to measure execution time
local function measureTime(func)
local start_time = os.clock()
local result = func()
local end_time = os.clock()
return end_time - start_time, result
end
-- Helper function to create test containers
local function createTestContainer(props)
props = props or {}
local defaults = {
x = 0,
y = 0,
width = 800,
height = 600,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.FLEX_START,
alignItems = AlignItems.STRETCH,
flexWrap = FlexWrap.NOWRAP,
gap = 0,
}
for key, value in pairs(props) do
defaults[key] = value
end
return Gui.new(defaults)
end
-- Helper function to create many children
local function createManyChildren(parent, count, child_props)
child_props = child_props or {}
local children = {}
for i = 1, count do
local props = {
width = child_props.w or 50,
height = child_props.h or 30,
}
-- Add any additional properties
for key, value in pairs(child_props) do
if key ~= "w" and key ~= "h" then
props[key] = value
end
end
local child = Gui.new(props)
child.parent = parent
table.insert(parent.children, child)
table.insert(children, child)
end
return children
end
-- Test 1: Basic Layout Performance Benchmark
function TestPerformance:testBasicLayoutPerformanceBenchmark()
local container = createTestContainer()
-- Test with small number of children (baseline)
local children_10 = createManyChildren(container, 10)
local time_10, _ = measureTime(function()
container:layoutChildren()
end)
-- Clear and test with medium number of children
container.children = {}
local children_50 = createManyChildren(container, 50)
local time_50, _ = measureTime(function()
container:layoutChildren()
end)
-- Clear and test with larger number of children
container.children = {}
local children_100 = createManyChildren(container, 100)
local time_100, _ = measureTime(function()
container:layoutChildren()
end)
print(string.format("Performance Benchmark:"))
print(string.format(" 10 children: %.6f seconds", time_10))
print(string.format(" 50 children: %.6f seconds", time_50))
print(string.format(" 100 children: %.6f seconds", time_100))
-- Assert reasonable performance (should complete within 1 second)
luaunit.assertTrue(time_10 < 0.05, "10 children layout should complete within 0.05 seconds")
luaunit.assertTrue(time_50 < 0.05, "50 children layout should complete within 0.05 seconds")
luaunit.assertTrue(time_100 < 0.05, "100 children layout should complete within 0.05 seconds")
-- Performance should scale reasonably (not exponentially)
-- Allow some overhead but ensure it's not exponential growth
luaunit.assertTrue(time_100 <= time_10 * 50, "Performance should not degrade exponentially")
end
-- Test 2: Scalability Testing with Large Numbers
function TestPerformance:testScalabilityWithLargeNumbers()
local container = createTestContainer()
-- Test progressively larger numbers of children
local test_sizes = { 10, 50, 100, 200 }
local times = {}
for _, size in ipairs(test_sizes) do
container.children = {} -- Clear previous children
local children = createManyChildren(container, size)
local time, _ = measureTime(function()
container:layoutChildren()
end)
times[size] = time
print(string.format("Scalability Test - %d children: %.6f seconds", size, time))
-- Each test should complete within reasonable time
luaunit.assertTrue(time < 0.05, string.format("%d children should layout within 0.05 seconds", size))
end
-- Check that performance scales linearly or sub-linearly
-- Time for 200 children should not be more than 20x time for 10 children
luaunit.assertTrue(times[200] <= times[10] * 20, "Performance should scale sub-linearly")
end
-- Test 3: Complex Nested Layout Performance
function TestPerformance:testComplexNestedLayoutPerformance()
-- Create a deeply nested structure with multiple levels
local root = createTestContainer({
width = 1000,
height = 800,
flexDirection = FlexDirection.VERTICAL,
})
local time, _ = measureTime(function()
-- Level 1: 5 sections
for i = 1, 5 do
local section = Gui.new({
width = 950,
height = 150,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
})
section.parent = root
table.insert(root.children, section)
-- Level 2: 4 columns per section
for j = 1, 4 do
local column = Gui.new({
width = 200,
height = 140,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
alignItems = AlignItems.CENTER,
})
column.parent = section
table.insert(section.children, column)
-- Level 3: 3 items per column
for k = 1, 3 do
local item = Gui.new({
width = 180,
height = 40,
})
item.parent = column
table.insert(column.children, item)
end
end
end
-- Layout the entire structure
root:layoutChildren()
end)
print(string.format("Complex Nested Layout (5x4x3 = 60 total elements): %.6f seconds", time))
-- Complex nested layout should complete within reasonable time
luaunit.assertTrue(time < 0.05, "Complex nested layout should complete within 0.05 seconds")
-- Verify structure was created correctly
luaunit.assertEquals(#root.children, 5) -- 5 sections
luaunit.assertEquals(#root.children[1].children, 4) -- 4 columns per section
luaunit.assertEquals(#root.children[1].children[1].children, 3) -- 3 items per column
end
-- Test 4: Flex Wrap Performance with Many Elements
function TestPerformance:testFlexWrapPerformanceWithManyElements()
local container = createTestContainer({
width = 400,
height = 600,
flexDirection = FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
justifyContent = JustifyContent.SPACE_AROUND,
alignItems = AlignItems.CENTER,
})
-- Create many children that will wrap
local children = createManyChildren(container, 50, {
width = 80,
height = 50,
})
local time, _ = measureTime(function()
container:layoutChildren()
end)
print(string.format("Flex Wrap Performance (50 wrapping elements): %.6f seconds", time))
-- Flex wrap with many elements should complete within reasonable time
luaunit.assertTrue(time < 0.05, "Flex wrap layout should complete within 0.05 seconds")
-- Verify that elements are positioned (wrapped layout worked)
luaunit.assertTrue(children[1].x >= 0 and children[1].y >= 0, "First child should be positioned")
luaunit.assertTrue(children[#children].x >= 0 and children[#children].y >= 0, "Last child should be positioned")
end
-- Test 5: Dynamic Layout Change Performance
function TestPerformance:testDynamicLayoutChangePerformance()
local container = createTestContainer()
local children = createManyChildren(container, 30)
-- Initial layout
container:layoutChildren()
-- Test performance of multiple layout property changes
local time, _ = measureTime(function()
for i = 1, 10 do
-- Change flex direction
container.flexDirection = (i % 2 == 0) and FlexDirection.VERTICAL or FlexDirection.HORIZONTAL
container:layoutChildren()
-- Change justify content
container.justifyContent = (i % 3 == 0) and JustifyContent.CENTER or JustifyContent.FLEX_START
container:layoutChildren()
-- Change align items
container.alignItems = (i % 4 == 0) and AlignItems.CENTER or AlignItems.STRETCH
container:layoutChildren()
end
end)
print(string.format("Dynamic Layout Changes (30 relayouts): %.6f seconds", time))
-- Dynamic layout changes should complete within reasonable time
luaunit.assertTrue(time < 0.05, "Dynamic layout changes should complete within 0.05 seconds")
-- Verify final layout is valid
luaunit.assertTrue(children[1].x >= 0 and children[1].y >= 0, "Children should be positioned after changes")
end
-- Test 6: Memory Usage Pattern Test
function TestPerformance:testMemoryUsagePattern()
-- This test checks that we don't have obvious memory leaks during layout operations
local container = createTestContainer()
-- Create and destroy many children multiple times
local time, _ = measureTime(function()
for cycle = 1, 5 do
-- Create children
local children = createManyChildren(container, 100)
container:layoutChildren()
-- Clear children (simulating component cleanup)
container.children = {}
-- Force garbage collection to test for leaks
collectgarbage("collect")
end
end)
print(string.format("Memory Usage Pattern Test (5 cycles, 100 elements each): %.6f seconds", time))
-- Memory pattern test should complete within reasonable time
luaunit.assertTrue(time < 0.05, "Memory usage pattern test should complete within 0.05 seconds")
-- Verify container is clean after cycles
luaunit.assertEquals(#container.children, 0, "Container should be clean after memory test")
end
-- Test 7: Performance with Different Layout Strategies
function TestPerformance:testPerformanceWithDifferentLayoutStrategies()
local strategies = {
{
name = "Simple Horizontal",
props = {
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.FLEX_START,
alignItems = AlignItems.STRETCH,
},
},
{
name = "Centered Vertical",
props = {
flexDirection = FlexDirection.VERTICAL,
justifyContent = JustifyContent.CENTER,
alignItems = AlignItems.CENTER,
},
},
{
name = "Wrapped Space-Between",
props = {
flexDirection = FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
justifyContent = JustifyContent.SPACE_BETWEEN,
alignItems = AlignItems.FLEX_START,
},
},
}
local times = {}
for _, strategy in ipairs(strategies) do
local container = createTestContainer(strategy.props)
local children = createManyChildren(container, 40)
local time, _ = measureTime(function()
container:layoutChildren()
end)
times[strategy.name] = time
print(string.format("Layout Strategy '%s': %.6f seconds", strategy.name, time))
-- Each strategy should complete within reasonable time
luaunit.assertTrue(time < 0.05, string.format("'%s' layout should complete within 0.05 second", strategy.name))
end
-- All strategies should perform reasonably similarly
-- None should be more than 10x slower than the fastest
local min_time = math.huge
local max_time = 0
for _, time in pairs(times) do
min_time = math.min(min_time, time)
max_time = math.max(max_time, time)
end
luaunit.assertTrue(max_time <= min_time * 10, "Layout strategies should have similar performance characteristics")
end
-- Test 8: Stress Test with Maximum Elements
function TestPerformance:testStressTestWithMaximumElements()
local container = createTestContainer({
width = 1200,
height = 900,
flexDirection = FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
})
-- Create a large number of children for stress testing
local stress_count = 300
local children = createManyChildren(container, stress_count, {
width = 30,
height = 20,
})
local time, _ = measureTime(function()
container:layoutChildren()
end)
print(string.format("Stress Test (%d elements): %.6f seconds", stress_count, time))
-- Stress test should complete within reasonable time even with many elements
luaunit.assertTrue(time < 0.05, string.format("Stress test with %d elements should complete within 0.05 seconds", stress_count))
-- Verify that all children are positioned
local positioned_count = 0
for _, child in ipairs(children) do
if child.x >= 0 and child.y >= 0 then
positioned_count = positioned_count + 1
end
end
luaunit.assertEquals(positioned_count, stress_count, "All children should be positioned in stress test")
end
-- Test 9: Complex Real-World Application Performance - Enterprise Dashboard
function TestPerformance:testComplexEnterpriseApplicationPerformance()
print("\n=== Test 9: Complex Enterprise Dashboard Performance ===")
-- Create enterprise-grade dashboard with deep nesting (5 levels)
local dashboard = createTestContainer({
width = 1920,
height = 1080,
flexDirection = FlexDirection.VERTICAL,
gap = 10,
})
local time, structure_info = measureTime(function()
-- Level 1: Header, Main Content, Footer
local header = Gui.new({
width = 1900,
height = 80,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
alignItems = AlignItems.CENTER,
gap = 20,
})
header.parent = dashboard
table.insert(dashboard.children, header)
local main_content = Gui.new({
width = 1900,
height = 980,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
gap = 15,
})
main_content.parent = dashboard
table.insert(dashboard.children, main_content)
local footer = Gui.new({
width = 1900,
height = 60,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.CENTER,
})
footer.parent = dashboard
table.insert(dashboard.children, footer)
-- Level 2: Header components (logo, navigation, user actions)
local header_sections = { "logo", "navigation", "search", "notifications", "user_menu" }
for i, section_name in ipairs(header_sections) do
local section = Gui.new({
width = section_name == "navigation" and 400 or 150,
height = 60,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.CENTER,
alignItems = AlignItems.CENTER,
})
section.parent = header
table.insert(header.children, section)
-- Level 3: Section items
local item_count = section_name == "navigation" and 6 or 3
for j = 1, item_count do
local item = Gui.new({
width = section_name == "navigation" and 60 or 45,
height = 40,
})
item.parent = section
table.insert(section.children, item)
end
end
-- Level 2: Main content areas (sidebar, dashboard grid)
local sidebar = Gui.new({
width = 280,
height = 960,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
gap = 10,
})
sidebar.parent = main_content
table.insert(main_content.children, sidebar)
local dashboard_grid = Gui.new({
width = 1600,
height = 960,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
gap = 20,
})
dashboard_grid.parent = main_content
table.insert(main_content.children, dashboard_grid)
-- Level 3: Sidebar navigation items (complex menu structure)
local menu_categories = {
{ name = "Analytics", items = 5 },
{ name = "Reports", items = 7 },
{ name = "Users", items = 4 },
{ name = "Settings", items = 6 },
{ name = "Tools", items = 8 },
}
for _, category in ipairs(menu_categories) do
local category_container = Gui.new({
width = 260,
height = 40 + (category.items * 35),
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
gap = 2,
})
category_container.parent = sidebar
table.insert(sidebar.children, category_container)
-- Category header
local category_header = Gui.new({ width = 250, height = 35 })
category_header.parent = category_container
table.insert(category_container.children, category_header)
-- Level 4: Menu items with sub-indicators
for i = 1, category.items do
local menu_item = Gui.new({
width = 240,
height = 30,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
alignItems = AlignItems.CENTER,
})
menu_item.parent = category_container
table.insert(category_container.children, menu_item)
-- Level 5: Menu item components (text, icon, badge)
local item_icon = Gui.new({ width = 20, height = 20 })
item_icon.parent = menu_item
table.insert(menu_item.children, item_icon)
local item_text = Gui.new({ width = 180, height = 25 })
item_text.parent = menu_item
table.insert(menu_item.children, item_text)
local item_badge = Gui.new({ width = 25, height = 18 })
item_badge.parent = menu_item
table.insert(menu_item.children, item_badge)
end
end
-- Level 3: Dashboard grid (4x3 widget grid with complex internals)
for row = 1, 4 do
local grid_row = Gui.new({
width = 1580,
height = 220,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
gap = 20,
})
grid_row.parent = dashboard_grid
table.insert(dashboard_grid.children, grid_row)
for col = 1, 3 do
local widget = Gui.new({
width = 500,
height = 200,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
gap = 8,
})
widget.parent = grid_row
table.insert(grid_row.children, widget)
-- Level 4: Widget components (header, content, footer)
local widget_header = Gui.new({
width = 480,
height = 40,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
alignItems = AlignItems.CENTER,
})
widget_header.parent = widget
table.insert(widget.children, widget_header)
local widget_content = Gui.new({
width = 480,
height = 120,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
justifyContent = JustifyContent.SPACE_AROUND,
alignItems = AlignItems.CENTER,
gap = 5,
})
widget_content.parent = widget
table.insert(widget.children, widget_content)
local widget_footer = Gui.new({
width = 480,
height = 32,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.FLEX_END,
alignItems = AlignItems.CENTER,
})
widget_footer.parent = widget
table.insert(widget.children, widget_footer)
-- Level 5: Widget content elements (charts, metrics, etc.)
local content_elements = (row * col) % 4 == 0 and 12 or 8
for i = 1, content_elements do
local element = Gui.new({
width = content_elements > 10 and 35 or 55,
height = content_elements > 10 and 25 or 35,
})
element.parent = widget_content
table.insert(widget_content.children, element)
end
-- Widget header components
local widget_title = Gui.new({ width = 200, height = 30 })
widget_title.parent = widget_header
table.insert(widget_header.children, widget_title)
local widget_actions = Gui.new({
width = 120,
height = 30,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
gap = 5,
})
widget_actions.parent = widget_header
table.insert(widget_header.children, widget_actions)
for j = 1, 3 do
local action_btn = Gui.new({ width = 30, height = 25 })
action_btn.parent = widget_actions
table.insert(widget_actions.children, action_btn)
end
-- Widget footer components
local footer_info = Gui.new({ width = 100, height = 25 })
footer_info.parent = widget_footer
table.insert(widget_footer.children, footer_info)
end
end
-- Perform complete layout
dashboard:layoutChildren()
-- Calculate structure metrics
local total_elements = 0
local max_depth = 0
local function countElements(element, depth)
total_elements = total_elements + 1
max_depth = math.max(max_depth, depth)
for _, child in ipairs(element.children) do
countElements(child, depth + 1)
end
end
countElements(dashboard, 1)
return {
total_elements = total_elements,
max_depth = max_depth,
widgets = 12,
menu_items = 30,
}
end)
print(string.format("Enterprise Dashboard Performance:"))
print(string.format(" Total Elements: %d", structure_info.total_elements))
print(string.format(" Maximum Depth: %d levels", structure_info.max_depth))
print(string.format(" Layout Time: %.6f seconds", time))
print(string.format(" Elements/Second: %.0f", structure_info.total_elements / time))
-- Performance assertions for enterprise-grade application
luaunit.assertTrue(time < 0.05, "Enterprise dashboard should layout within 0.05 seconds")
luaunit.assertTrue(structure_info.total_elements > 200, "Should have created substantial element count")
luaunit.assertTrue(structure_info.max_depth >= 5, "Should have deep nesting structure")
-- Verify critical components are positioned
luaunit.assertEquals(#dashboard.children, 3, "Should have header, main, footer")
luaunit.assertTrue(#dashboard.children[2].children >= 2, "Main should have sidebar and grid")
end
-- Test 10: High-Frequency Dynamic Layout Updates Performance
function TestPerformance:testHighFrequencyDynamicLayoutUpdates()
print("\n=== Test 10: High-Frequency Dynamic Updates Performance ===")
local container = createTestContainer({
width = 1200,
height = 800,
flexDirection = FlexDirection.VERTICAL,
gap = 10,
})
-- Create dynamic content structure
local sections = {}
for i = 1, 8 do
local section = Gui.new({
width = 1180,
height = 90,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
alignItems = AlignItems.CENTER,
gap = 8,
})
section.parent = container
table.insert(container.children, section)
table.insert(sections, section)
-- Create dynamic items in each section
for j = 1, 10 do
local item = Gui.new({
width = 100,
height = 70,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
justifyContent = JustifyContent.CENTER,
alignItems = AlignItems.CENTER,
})
item.parent = section
table.insert(section.children, item)
-- Sub-items for more complex updates
for k = 1, 3 do
local sub_item = Gui.new({ width = 80, height = 20 })
sub_item.parent = item
table.insert(item.children, sub_item)
end
end
end
-- Initial layout
container:layoutChildren()
local update_scenarios = {
{ name = "Direction Changes", iterations = 50 },
{ name = "Justify Content Cycling", iterations = 40 },
{ name = "Gap Modifications", iterations = 30 },
{ name = "Size Adjustments", iterations = 35 },
{ name = "Wrap Toggle", iterations = 25 },
}
local total_updates = 0
local total_time = 0
for _, scenario in ipairs(update_scenarios) do
local scenario_time = measureTime(function()
for i = 1, scenario.iterations do
if scenario.name == "Direction Changes" then
local section = sections[(i % #sections) + 1]
section.flexDirection = (i % 2 == 0) and FlexDirection.VERTICAL or FlexDirection.HORIZONTAL
section:layoutChildren()
elseif scenario.name == "Justify Content Cycling" then
local section = sections[(i % #sections) + 1]
local justifies = { JustifyContent.FLEX_START, JustifyContent.CENTER, JustifyContent.FLEX_END, JustifyContent.SPACE_BETWEEN }
section.justifyContent = justifies[(i % #justifies) + 1]
section:layoutChildren()
elseif scenario.name == "Gap Modifications" then
local section = sections[(i % #sections) + 1]
section.gap = (i % 20) + 5
section:layoutChildren()
elseif scenario.name == "Size Adjustments" then
local section = sections[(i % #sections) + 1]
for _, child in ipairs(section.children) do
child.w = 80 + (i % 40)
child.h = 60 + (i % 20)
end
section:layoutChildren()
elseif scenario.name == "Wrap Toggle" then
local section = sections[(i % #sections) + 1]
section.flexWrap = (i % 2 == 0) and FlexWrap.WRAP or FlexWrap.NOWRAP
section:layoutChildren()
end
end
end)
total_updates = total_updates + scenario.iterations
total_time = total_time + scenario_time
print(
string.format(
" %s (%d updates): %.6f seconds (%.3f ms/update)",
scenario.name,
scenario.iterations,
scenario_time,
(scenario_time * 1000) / scenario.iterations
)
)
end
print(string.format("High-Frequency Updates Summary:"))
print(string.format(" Total Updates: %d", total_updates))
print(string.format(" Total Time: %.6f seconds", total_time))
print(string.format(" Average Update Time: %.3f ms", (total_time * 1000) / total_updates))
print(string.format(" Updates Per Second: %.0f", total_updates / total_time))
-- Performance assertions
luaunit.assertTrue(total_time < 15.0, "High-frequency updates should complete within 15 seconds")
luaunit.assertTrue((total_time * 1000) / total_updates < 50, "Average update should be under 50ms")
luaunit.assertTrue(total_updates / total_time > 10, "Should achieve at least 10 updates per second")
-- Verify final state is valid
luaunit.assertEquals(#container.children, 8, "All sections should still exist")
for _, section in ipairs(sections) do
luaunit.assertEquals(#section.children, 10, "All items should still exist in sections")
end
end
-- Test 11: Complex Animation-Ready Layout Performance
function TestPerformance:testComplexAnimationReadyLayoutPerformance()
print("\n=== Test 11: Complex Animation-Ready Layout Performance ===")
-- Create animation-heavy interface structure
local animation_container = createTestContainer({
width = 1400,
height = 900,
flexDirection = FlexDirection.VERTICAL,
gap = 15,
})
local animation_elements = {}
local time, metrics = measureTime(function()
-- Create multiple animated sections with complex layouts
for section_id = 1, 6 do
local section = Gui.new({
width = 1380,
height = 140,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_AROUND,
alignItems = AlignItems.CENTER,
gap = 12,
})
section.parent = animation_container
table.insert(animation_container.children, section)
-- Create animated cards/panels
for card_id = 1, 8 do
local card = Gui.new({
width = 160,
height = 120,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
gap = 5,
})
card.parent = section
table.insert(section.children, card)
table.insert(animation_elements, card)
-- Card header with animated elements
local card_header = Gui.new({
width = 150,
height = 30,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
alignItems = AlignItems.CENTER,
})
card_header.parent = card
table.insert(card.children, card_header)
-- Animated header components
local title = Gui.new({ width = 100, height = 25 })
title.parent = card_header
table.insert(card_header.children, title)
local status_indicator = Gui.new({ width = 20, height = 20 })
status_indicator.parent = card_header
table.insert(card_header.children, status_indicator)
-- Card content with animated metrics
local card_content = Gui.new({
width = 150,
height = 60,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
justifyContent = JustifyContent.SPACE_AROUND,
alignItems = AlignItems.CENTER,
gap = 3,
})
card_content.parent = card
table.insert(card.children, card_content)
-- Animated metrics/values
local metric_count = 4 + (section_id % 3)
for i = 1, metric_count do
local metric = Gui.new({
width = 35,
height = 25,
positioning = Positioning.FLEX,
justifyContent = JustifyContent.CENTER,
alignItems = AlignItems.CENTER,
})
metric.parent = card_content
table.insert(card_content.children, metric)
table.insert(animation_elements, metric)
end
-- Card footer with action buttons
local card_footer = Gui.new({
width = 150,
height = 25,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.FLEX_END,
gap = 5,
})
card_footer.parent = card
table.insert(card.children, card_footer)
for i = 1, 2 do
local action_btn = Gui.new({ width = 25, height = 20 })
action_btn.parent = card_footer
table.insert(card_footer.children, action_btn)
table.insert(animation_elements, action_btn)
end
end
end
-- Perform initial layout
animation_container:layoutChildren()
-- Simulate animation frame updates (position/size changes)
local animation_frames = 60 -- Simulate 1 second at 60fps
local frame_times = {}
for frame = 1, animation_frames do
local frame_start = os.clock()
-- Simulate animated property changes
for i, element in ipairs(animation_elements) do
if (frame + i) % 10 == 0 then
-- Animate size changes
element.w = element.width + math.sin(frame * 0.1 + i) * 2
element.h = element.height + math.cos(frame * 0.1 + i) * 1
end
if (frame + i) % 15 == 0 then
-- Animate gap changes in parent containers
if element.parent and element.parent.gap then
element.parent.gap = 5 + math.abs(math.sin(frame * 0.05)) * 10
end
end
end
-- Relayout for animation frame
animation_container:layoutChildren()
local frame_time = os.clock() - frame_start
table.insert(frame_times, frame_time)
end
return {
total_elements = #animation_elements,
animation_frames = animation_frames,
frame_times = frame_times,
}
end)
-- Calculate animation performance metrics
local total_frame_time = 0
local max_frame_time = 0
local min_frame_time = math.huge
for _, frame_time in ipairs(metrics.frame_times) do
total_frame_time = total_frame_time + frame_time
max_frame_time = math.max(max_frame_time, frame_time)
min_frame_time = math.min(min_frame_time, frame_time)
end
local avg_frame_time = total_frame_time / metrics.animation_frames
local target_fps = 60
local target_frame_time = 1.0 / target_fps
print(string.format("Animation-Ready Layout Performance:"))
print(string.format(" Setup Time: %.6f seconds", time - total_frame_time))
print(string.format(" Animation Elements: %d", metrics.total_elements))
print(string.format(" Animation Frames: %d", metrics.animation_frames))
print(string.format(" Total Animation Time: %.6f seconds", total_frame_time))
print(string.format(" Average Frame Time: %.6f seconds (%.1f fps equivalent)", avg_frame_time, 1.0 / avg_frame_time))
print(string.format(" Min Frame Time: %.6f seconds", min_frame_time))
print(string.format(" Max Frame Time: %.6f seconds", max_frame_time))
print(string.format(" 60fps Target: %.6f seconds/frame", target_frame_time))
-- Performance assertions for animation-ready layouts
luaunit.assertTrue(time < 0.05, "Animation setup should complete within 0.05 seconds")
luaunit.assertTrue(avg_frame_time < target_frame_time * 2, "Average frame time should be reasonable for 30fps+")
luaunit.assertTrue(max_frame_time < 0.05, "No single frame should take more than 50ms")
luaunit.assertTrue(metrics.total_elements > 100, "Should have substantial number of animated elements")
-- Verify structure integrity after animations
luaunit.assertEquals(#animation_container.children, 6, "All sections should remain")
local total_cards = 0
for _, section in ipairs(animation_container.children) do
total_cards = total_cards + #section.children
end
luaunit.assertEquals(total_cards, 48, "All cards should remain after animation")
end
-- Test 12: Memory-Intensive Layout Performance with Cleanup
function TestPerformance:testMemoryIntensiveLayoutPerformanceWithCleanup()
print("\n=== Test 12: Memory-Intensive Layout with Cleanup ===")
local memory_cycles = 8
local elements_per_cycle = 150
local cycle_times = {}
local cleanup_times = {}
local total_time = measureTime(function()
for cycle = 1, memory_cycles do
print(string.format(" Memory Cycle %d/%d", cycle, memory_cycles))
-- Create intensive layout structure
local cycle_start = os.clock()
local root = createTestContainer({
width = 1600,
height = 1000,
flexDirection = FlexDirection.VERTICAL,
gap = 8,
})
local all_elements = {}
-- Create memory-intensive nested structure
for level1 = 1, 10 do
local section = Gui.new({
width = 1580,
height = 95,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
justifyContent = JustifyContent.SPACE_AROUND,
gap = 5,
})
section.parent = root
table.insert(root.children, section)
table.insert(all_elements, section)
for level2 = 1, 15 do
local container = Gui.new({
width = 100,
height = 85,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
gap = 2,
})
container.parent = section
table.insert(section.children, container)
table.insert(all_elements, container)
for level3 = 1, 3 do
local item = Gui.new({
width = 95,
height = 25,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.CENTER,
alignItems = AlignItems.CENTER,
})
item.parent = container
table.insert(container.children, item)
table.insert(all_elements, item)
-- Add some leaf nodes for memory pressure
for level4 = 1, 2 do
local leaf = Gui.new({ width = 40, height = 20 })
leaf.parent = item
table.insert(item.children, leaf)
table.insert(all_elements, leaf)
end
end
end
end
-- Perform layout
root:layoutChildren()
local cycle_time = os.clock() - cycle_start
table.insert(cycle_times, cycle_time)
print(string.format(" Created %d elements in %.6f seconds", #all_elements, cycle_time))
-- Cleanup phase
local cleanup_start = os.clock()
-- Clear all references systematically
for _, element in ipairs(all_elements) do
element.children = {}
element.parent = nil
end
-- Clear root structure
root.children = {}
Gui.destroy()
-- Force garbage collection
collectgarbage("collect")
local cleanup_time = os.clock() - cleanup_start
table.insert(cleanup_times, cleanup_time)
print(string.format(" Cleanup completed in %.6f seconds", cleanup_time))
end
end)
-- Calculate memory performance metrics
local total_cycle_time = 0
local total_cleanup_time = 0
local max_cycle_time = 0
local max_cleanup_time = 0
for i = 1, memory_cycles do
total_cycle_time = total_cycle_time + cycle_times[i]
total_cleanup_time = total_cleanup_time + cleanup_times[i]
max_cycle_time = math.max(max_cycle_time, cycle_times[i])
max_cleanup_time = math.max(max_cleanup_time, cleanup_times[i])
end
local avg_cycle_time = total_cycle_time / memory_cycles
local avg_cleanup_time = total_cleanup_time / memory_cycles
print(string.format("Memory-Intensive Layout Performance:"))
print(string.format(" Memory Cycles: %d", memory_cycles))
print(string.format(" Elements Per Cycle: ~%d", elements_per_cycle * 6)) -- Approximate
print(string.format(" Total Test Time: %.6f seconds", total_time))
print(string.format(" Average Cycle Time: %.6f seconds", avg_cycle_time))
print(string.format(" Average Cleanup Time: %.6f seconds", avg_cleanup_time))
print(string.format(" Max Cycle Time: %.6f seconds", max_cycle_time))
print(string.format(" Max Cleanup Time: %.6f seconds", max_cleanup_time))
print(string.format(" Cycle Efficiency: %.1f elements/second", (elements_per_cycle * 6) / avg_cycle_time))
-- Performance assertions for memory-intensive operations
luaunit.assertTrue(total_time < 30.0, "Memory-intensive test should complete within 30 seconds")
luaunit.assertTrue(avg_cycle_time < 5.0, "Average cycle should complete within 5 seconds")
luaunit.assertTrue(avg_cleanup_time < 2.0, "Average cleanup should complete within 2 seconds")
luaunit.assertTrue(max_cycle_time <= avg_cycle_time * 3, "No cycle should be extremely slow")
luaunit.assertTrue(max_cleanup_time <= avg_cleanup_time * 3, "No cleanup should be extremely slow")
-- Verify clean state after all cycles
local final_container = createTestContainer()
luaunit.assertEquals(#final_container.children, 0, "Should start with clean container after cycles")
end
-- Test 13: Extreme Scale Performance Benchmark
function TestPerformance:testExtremeScalePerformanceBenchmark()
print("\n=== Test 13: Extreme Scale Performance Benchmark ===")
-- Test with extremely large layouts to find breaking points
local scale_tests = {
{ name = "Massive Flat Layout", elements = 1000, depth = 1 },
{ name = "Deep Nesting", elements = 200, depth = 10 },
{ name = "Wide Branching", elements = 500, depth = 4 },
{ name = "Mixed Complex", elements = 800, depth = 6 },
}
local benchmark_results = {}
for _, test_config in ipairs(scale_tests) do
print(string.format(" Running %s test...", test_config.name))
local test_time, test_metrics = measureTime(function()
local root = createTestContainer({
width = 2000,
height = 1500,
flexDirection = FlexDirection.VERTICAL,
flexWrap = FlexWrap.WRAP,
gap = 5,
})
local created_elements = 0
local max_actual_depth = 0
if test_config.name == "Massive Flat Layout" then
-- Create very wide, flat structure
local items_per_row = 50
local rows = math.ceil(test_config.elements / items_per_row)
for row = 1, rows do
local row_container = Gui.new({
width = 1980,
height = 25,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
gap = 2,
})
row_container.parent = root
table.insert(root.children, row_container)
created_elements = created_elements + 1
local items_in_this_row = math.min(items_per_row, test_config.elements - (row - 1) * items_per_row)
for col = 1, items_in_this_row do
local item = Gui.new({ width = 35, height = 20 })
item.parent = row_container
table.insert(row_container.children, item)
created_elements = created_elements + 1
end
end
max_actual_depth = 2
elseif test_config.name == "Deep Nesting" then
-- Create deep nested structure
local current_parent = root
local elements_per_level = math.ceil(test_config.elements / test_config.depth)
for depth = 1, test_config.depth do
local level_container = Gui.new({
width = 1900 - (depth * 50),
height = 1400 - (depth * 100),
positioning = Positioning.FLEX,
flexDirection = (depth % 2 == 0) and FlexDirection.VERTICAL or FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
gap = math.max(1, 10 - depth),
})
level_container.parent = current_parent
table.insert(current_parent.children, level_container)
created_elements = created_elements + 1
if depth < test_config.depth then
current_parent = level_container
else
-- Final level - add many elements
for i = 1, elements_per_level do
local leaf = Gui.new({ width = 30 + (i % 20), height = 25 + (i % 15) })
leaf.parent = level_container
table.insert(level_container.children, leaf)
created_elements = created_elements + 1
end
end
end
max_actual_depth = test_config.depth
elseif test_config.name == "Wide Branching" then
-- Create structure with wide branching at each level
local function createBranching(parent, remaining_elements, current_depth, max_depth)
if current_depth >= max_depth or remaining_elements <= 0 then
return 0
end
local children_count = math.min(20, math.ceil(remaining_elements / (max_depth - current_depth)))
local elements_used = 0
for i = 1, children_count do
local branch = Gui.new({
width = 150 - (current_depth * 15),
height = 100 - (current_depth * 10),
positioning = Positioning.FLEX,
flexDirection = (i % 2 == 0) and FlexDirection.VERTICAL or FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_AROUND,
gap = math.max(1, 8 - current_depth * 2),
})
branch.parent = parent
table.insert(parent.children, branch)
elements_used = elements_used + 1
if current_depth < max_depth - 1 then
elements_used = elements_used + createBranching(branch, remaining_elements - elements_used, current_depth + 1, max_depth)
end
if elements_used >= remaining_elements then
break
end
end
return elements_used
end
created_elements = 1 + createBranching(root, test_config.elements - 1, 1, test_config.depth)
max_actual_depth = test_config.depth
elseif test_config.name == "Mixed Complex" then
-- Create mixed complex structure with varying patterns
local sections = math.ceil(test_config.depth / 2)
local elements_per_section = math.ceil(test_config.elements / sections)
for section_id = 1, sections do
local section = Gui.new({
width = 1900,
height = 200,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
flexWrap = FlexWrap.WRAP,
justifyContent = JustifyContent.SPACE_BETWEEN,
gap = 10,
})
section.parent = root
table.insert(root.children, section)
created_elements = created_elements + 1
-- Create subsections with different patterns
local subsections = 5 + (section_id % 3)
for sub_id = 1, subsections do
local subsection = Gui.new({
width = 300,
height = 180,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.VERTICAL,
justifyContent = JustifyContent.SPACE_AROUND,
gap = 5,
})
subsection.parent = section
table.insert(section.children, subsection)
created_elements = created_elements + 1
-- Add elements with varying complexity
local elements_in_subsection = math.ceil(elements_per_section / subsections)
for elem_id = 1, elements_in_subsection do
if elem_id % 3 == 0 then
-- Complex element with children
local complex_elem = Gui.new({
width = 280,
height = 35,
positioning = Positioning.FLEX,
flexDirection = FlexDirection.HORIZONTAL,
justifyContent = JustifyContent.SPACE_BETWEEN,
gap = 3,
})
complex_elem.parent = subsection
table.insert(subsection.children, complex_elem)
created_elements = created_elements + 1
for child_id = 1, 4 do
local child = Gui.new({ width = 60, height = 30 })
child.parent = complex_elem
table.insert(complex_elem.children, child)
created_elements = created_elements + 1
end
else
-- Simple element
local simple_elem = Gui.new({ width = 270, height = 25 })
simple_elem.parent = subsection
table.insert(subsection.children, simple_elem)
created_elements = created_elements + 1
end
if created_elements >= test_config.elements then
break
end
end
if created_elements >= test_config.elements then
break
end
end
if created_elements >= test_config.elements then
break
end
end
max_actual_depth = 4
end
-- Perform layout
root:layoutChildren()
return {
created_elements = created_elements,
max_depth = max_actual_depth,
}
end)
local elements_per_second = test_metrics.created_elements / test_time
benchmark_results[test_config.name] = {
time = test_time,
elements = test_metrics.created_elements,
elements_per_second = elements_per_second,
depth = test_metrics.max_depth,
}
print(string.format(" %s: %d elements, %.6f seconds (%.0f elem/sec)", test_config.name, test_metrics.created_elements, test_time, elements_per_second))
-- Individual test assertions
luaunit.assertTrue(test_time < 1.0, string.format("%s should complete within 1 seconds", test_config.name))
luaunit.assertTrue(test_metrics.created_elements > 50, string.format("%s should create substantial elements", test_config.name))
end
-- Overall benchmark analysis
print(string.format("Extreme Scale Benchmark Summary:"))
local total_elements = 0
local total_time = 0
local best_performance = 0
local worst_performance = math.huge
for test_name, result in pairs(benchmark_results) do
total_elements = total_elements + result.elements
total_time = total_time + result.time
best_performance = math.max(best_performance, result.elements_per_second)
worst_performance = math.min(worst_performance, result.elements_per_second)
print(string.format(" %s: %.0f elements/second", test_name, result.elements_per_second))
end
local avg_performance = total_elements / total_time
print(string.format(" Overall Average: %.0f elements/second", avg_performance))
print(string.format(" Best Performance: %.0f elements/second", best_performance))
print(string.format(" Worst Performance: %.0f elements/second", worst_performance))
print(string.format(" Performance Range: %.1fx", best_performance / worst_performance))
-- Extreme scale performance assertions
luaunit.assertTrue(total_time < 60.0, "All extreme scale tests should complete within 60 seconds")
luaunit.assertTrue(avg_performance > 50, "Should achieve at least 50 elements/second average")
luaunit.assertTrue(best_performance > 100, "Best case should achieve at least 100 elements/second")
luaunit.assertTrue(best_performance / worst_performance < 20, "Performance variance should be reasonable")
luaunit.assertTrue(total_elements > 2000, "Should have processed substantial total elements")
end
luaunit.LuaUnit.run()