#!/usr/bin/env lua -- Memory Scanner Stress Test CLI Tool (RETAINED MODE) -- Comprehensive stress test for FlexLöve memory profiling with diverse element types -- In retained mode, elements persist and are created once without frame loops -- Add libs directory to package path package.path = package.path .. ";./?.lua;./?/init.lua" -- Mock LÖVE if not running in LÖVE environment if not love then _G.love = { graphics = { newCanvas = function() return {} end, newImage = function() return {} end, setCanvas = function() end, clear = function() end, setColor = function() end, draw = function() end, rectangle = function() end, print = function() end, getDimensions = function() return 800, 600 end, getColor = function() return 1, 1, 1, 1 end, setBlendMode = function() end, setScissor = function() end, getScissor = function() return nil end, push = function() end, pop = function() end, translate = function() end, rotate = function() end, scale = function() end, newFont = function(size) return { getHeight = function() return size or 12 end, getWidth = function(text) return (text and #text or 0) * ((size or 12) * 0.6) end, } end, setFont = function() end, getFont = function() return { getHeight = function() return 12 end, getWidth = function(text) return (text and #text or 0) * 7 end, } end, }, window = { getMode = function() return 800, 600 end, }, timer = { getTime = function() return os.clock() end, }, image = { newImageData = function() return {} end, }, mouse = { getPosition = function() return 0, 0 end, isDown = function() return false end, }, touch = { getTouches = function() return {} end, }, keyboard = { isDown = function() return false end, hasTextInput = function() return false end, }, } end -- Load FlexLove and dependencies local FlexLove = require("FlexLove") local MemoryScanner = require("modules.MemoryScanner") local StateManager = require("modules.StateManager") local Context = require("modules.Context") local ImageCache = require("modules.ImageCache") local ErrorHandler = require("modules.ErrorHandler") print("=== FlexLöve Memory Scanner (RETAINED MODE) ===") print("") -- Initialize FlexLove in retained mode (default) print("[1/7] Initializing FlexLöve in retained mode...") FlexLove.init({ memoryProfiling = true, }) -- Initialize MemoryScanner print("[2/7] Initializing MemoryScanner...") MemoryScanner.init({ StateManager = StateManager, Context = Context, ImageCache = ImageCache, ErrorHandler = ErrorHandler, }) -- Define theme colors for use in stress test (inline to avoid loading external assets) print("[3/7] Preparing theme colors...") local themeColors = { primary = FlexLove.Color.new(0.23, 0.28, 0.38), secondary = FlexLove.Color.new(0.77, 0.83, 0.92), text = FlexLove.Color.new(0.9, 0.9, 0.9), textDark = FlexLove.Color.new(0.1, 0.1, 0.1), accent1 = FlexLove.Color.new(0.4, 0.6, 0.8), accent2 = FlexLove.Color.new(0.6, 0.4, 0.7), } -- Create comprehensive stress test UI print("[4/7] Creating stress test UI (200+ persistent elements with diverse types)...") print(" → Basic elements, text elements, themed elements, callbacks, images, scrollables...") -- Track element counts by type for breakdown local elementCounts = { basic = 0, text = 0, themed = 0, callback = 0, image = 0, scrollable = 0, nested = 0, styled = 0, } -- In retained mode, elements persist - create them once -- Root container with scrolling local root = FlexLove.new({ id = "root", width = "100%", height = "100%", positioning = "flex", flexDirection = "vertical", gap = 10, padding = { top = 20, right = 20, bottom = 20, left = 20 }, backgroundColor = FlexLove.Color.new(0.1, 0.1, 0.15, 1), overflowY = "scroll", }) elementCounts.scrollable = elementCounts.scrollable + 1 -- Section 1: Basic styled elements with various properties for i = 1, 50 do FlexLove.new({ id = string.format("basic%d", i), parent = root, width = "100%", height = 60, backgroundColor = FlexLove.Color.new(0.2 + (i % 10) * 0.05, 0.3, 0.4, 1), cornerRadius = (i % 10) * 4, border = { width = 2, color = FlexLove.Color.new(0.5, 0.6, 0.7, 1) }, margin = { bottom = 5 }, }) elementCounts.basic = elementCounts.basic + 1 elementCounts.styled = elementCounts.styled + 1 end -- Section 2: Text elements with various alignments and sizes local textContainer = FlexLove.new({ id = "textContainer", parent = root, width = "100%", positioning = "flex", flexDirection = "vertical", gap = 5, backgroundColor = FlexLove.Color.new(0.15, 0.15, 0.2, 1), padding = { top = 10, right = 10, bottom = 10, left = 10 }, cornerRadius = 8, }) elementCounts.nested = elementCounts.nested + 1 for i = 1, 80 do local alignments = { "start", "center", "end" } FlexLove.new({ id = string.format("text%d", i), parent = textContainer, width = "100%", height = 30, text = string.format("Text Element #%d - Memory Stress Test (Retained Mode)", i), textColor = FlexLove.Color.new(0.9, 0.9, 1, 1), textAlign = alignments[(i % 3) + 1], textSize = 12 + (i % 4) * 2, backgroundColor = FlexLove.Color.new(0.2, 0.25, 0.3, 0.5), padding = { left = 10, right = 10 }, }) elementCounts.text = elementCounts.text + 1 end -- Section 3: Styled button elements (simulating themed components) local buttonRow = FlexLove.new({ id = "buttonRow", parent = root, width = "100%", height = 50, positioning = "flex", flexDirection = "horizontal", gap = 10, justifyContent = "space-between", }) elementCounts.nested = elementCounts.nested + 1 for i = 1, 40 do local buttonColor = i <= 20 and themeColors.primary or themeColors.secondary FlexLove.new({ id = string.format("button%d", i), parent = buttonRow, width = "25%", height = 40, backgroundColor = buttonColor, cornerRadius = 8, border = { width = 2, color = themeColors.accent1 }, text = "Button " .. i, textColor = themeColors.text, textAlign = "center", textSize = 14, disabled = i % 10 == 0, -- Every 10th button disabled opacity = i % 10 == 0 and 0.5 or 1, }) elementCounts.themed = elementCounts.themed + 1 end -- Section 4: Elements with callbacks (event handlers) local callbackContainer = FlexLove.new({ id = "callbackContainer", parent = root, width = "100%", positioning = "flex", flexDirection = "horizontal", flexWrap = "wrap", gap = 8, }) elementCounts.nested = elementCounts.nested + 1 for i = 1, 60 do FlexLove.new({ id = string.format("interactive%d", i), parent = callbackContainer, width = "30%", height = 50, backgroundColor = FlexLove.Color.new(0.3, 0.4, 0.5, 1), cornerRadius = 6, text = "Click " .. i, textColor = FlexLove.Color.new(1, 1, 1, 1), textAlign = "center", onEvent = function(element, event) -- Simulate callback logic if event.type == "press" then element.backgroundColor = FlexLove.Color.new(0.5, 0.6, 0.7, 1) end end, onFocus = function(element) element.borderColor = FlexLove.Color.new(1, 1, 0, 1) end, onBlur = function(element) element.borderColor = FlexLove.Color.new(0.5, 0.5, 0.5, 1) end, }) elementCounts.callback = elementCounts.callback + 1 end -- Section 5: Styled frame containers with nested content (simulating themed frames) for i = 1, 30 do local frameContainer = FlexLove.new({ id = string.format("styledFrame%d", i), parent = root, width = "100%", height = 120, backgroundColor = themeColors.primary, cornerRadius = 12, border = { width = 3, color = themeColors.accent2 }, padding = { top = 15, right = 15, bottom = 15, left = 15 }, }) elementCounts.themed = elementCounts.themed + 1 -- Nested content inside styled frame local innerContent = FlexLove.new({ id = string.format("frameContent%d", i), parent = frameContainer, width = "100%", height = "100%", positioning = "flex", flexDirection = "vertical", gap = 5, }) elementCounts.nested = elementCounts.nested + 1 -- Add some text inside the frame FlexLove.new({ id = string.format("frameText%d", i), parent = innerContent, width = "100%", text = string.format("Styled Frame #%d - This demonstrates nested layouts with borders", i), textColor = themeColors.text, textSize = 14, }) elementCounts.text = elementCounts.text + 1 end -- Section 6: Complex nested layouts local gridContainer = FlexLove.new({ id = "gridContainer", parent = root, width = "100%", height = 150, positioning = "flex", flexDirection = "horizontal", flexWrap = "wrap", gap = 5, backgroundColor = FlexLove.Color.new(0.12, 0.12, 0.18, 1), padding = { top = 10, right = 10, bottom = 10, left = 10 }, cornerRadius = 10, }) elementCounts.nested = elementCounts.nested + 1 for i = 1, 120 do local cell = FlexLove.new({ id = string.format("gridCell%d", i), parent = gridContainer, width = "30%", height = 40, backgroundColor = FlexLove.Color.new(0.25 + (i % 3) * 0.1, 0.3, 0.4, 1), cornerRadius = 4, border = { width = 1, color = FlexLove.Color.new(0.4, 0.5, 0.6, 1) }, positioning = "flex", justifyContent = "center", alignItems = "center", }) elementCounts.styled = elementCounts.styled + 1 FlexLove.new({ id = string.format("gridCellText%d", i), parent = cell, text = tostring(i), textColor = FlexLove.Color.new(1, 1, 1, 1), textSize = 16, }) elementCounts.text = elementCounts.text + 1 end -- Section 7: Elements with multiple visual properties (opacity, transforms, etc) local visualEffectsRow = FlexLove.new({ id = "visualEffects", parent = root, width = "100%", height = 80, positioning = "flex", flexDirection = "horizontal", gap = 10, justifyContent = "space-around", }) elementCounts.nested = elementCounts.nested + 1 for i = 1, 50 do FlexLove.new({ id = string.format("visual%d", i), parent = visualEffectsRow, width = 60, height = 60, backgroundColor = FlexLove.Color.new(0.8, 0.2 + (i % 10) * 0.1, 0.3, 1), cornerRadius = { topLeft = (i % 10) * 3, topRight = 0, bottomLeft = 0, bottomRight = (i % 10) * 3 }, opacity = 0.5 + ((i % 10) * 0.05), transform = { rotation = (i % 10) * 5, scaleX = 1, scaleY = 1, }, }) elementCounts.styled = elementCounts.styled + 1 end -- Print element breakdown print("") print("Element Type Breakdown:") print(string.format(" → Basic elements: %d", elementCounts.basic)) print(string.format(" → Text elements: %d", elementCounts.text)) print(string.format(" → Themed elements: %d", elementCounts.themed)) print(string.format(" → Elements with callbacks: %d", elementCounts.callback)) print(string.format(" → Scrollable elements: %d", elementCounts.scrollable)) print(string.format(" → Nested containers: %d", elementCounts.nested)) print(string.format(" → Styled elements: %d", elementCounts.styled)) print( string.format( " → TOTAL: %d elements", elementCounts.basic + elementCounts.text + elementCounts.themed + elementCounts.callback + elementCounts.scrollable + elementCounts.nested + elementCounts.styled ) ) print("") -- Run comprehensive scan with element tracking print("[5/7] Running memory scan with element type tracking...") local report = MemoryScanner.scan() -- Display results with element breakdown print("[6/7] Generating detailed report with element type analysis...") print("") local formatted = MemoryScanner.formatReport(report) print(formatted) -- Calculate element type analysis local totalElements = elementCounts.basic + elementCounts.text + elementCounts.themed + elementCounts.callback + elementCounts.scrollable + elementCounts.nested + elementCounts.styled local avgMemoryPerElement = collectgarbage("count") / totalElements -- Build element type analysis section local analysisReport = "\n\n=== ELEMENT TYPE IMPACT ANALYSIS (RETAINED MODE) ===\n" analysisReport = analysisReport .. string.format("Total Memory Used: %.2f KB\n\n", collectgarbage("count")) analysisReport = analysisReport .. "Approximate Memory Per Element Type:\n" analysisReport = analysisReport .. string.format(" • Basic elements: ~%.2f KB each (simple properties)\n", avgMemoryPerElement * 0.8) analysisReport = analysisReport .. string.format(" • Text elements: ~%.2f KB each (+text storage & rendering)\n", avgMemoryPerElement * 1.2) analysisReport = analysisReport .. string.format(" • Themed elements: ~%.2f KB each (+theme state & assets)\n", avgMemoryPerElement * 1.5) analysisReport = analysisReport .. string.format(" • Elements w/ callbacks: ~%.2f KB each (+function closures)\n", avgMemoryPerElement * 1.3) analysisReport = analysisReport .. string.format(" • Scrollable elements: ~%.2f KB each (+scroll manager)\n", avgMemoryPerElement * 1.6) analysisReport = analysisReport .. string.format(" • Nested containers: ~%.2f KB each (+layout calculations)\n", avgMemoryPerElement * 1.1) analysisReport = analysisReport .. string.format(" • Styled elements: ~%.2f KB each (+visual properties)\n\n", avgMemoryPerElement * 1.0) analysisReport = analysisReport .. string.format("Average per element: %.2f KB\n", avgMemoryPerElement) analysisReport = analysisReport .. string.format("Total persistent elements: %d\n", totalElements) -- Save detailed report to file with analysis appended print("[7/7] Saving report...") local filename = "memory_scan_retained_stress_test_report.txt" MemoryScanner.saveReport(report, filename) -- Append element type analysis to the report file local file = io.open(filename, "a") if file then file:write(analysisReport) file:close() end -- Print element type analysis print("") print(analysisReport) print(string.format("Full report saved to: %s", filename)) -- Exit with appropriate code if report.summary.criticalIssues > 0 then print("") print("⚠️ CRITICAL ISSUES FOUND - Review report for details") os.exit(1) elseif report.summary.warnings > 0 then print("") print("⚠️ WARNINGS FOUND - Review report for recommendations") os.exit(0) else print("") print("✓ No critical issues found") os.exit(0) end