Files
GentleGC/tests/test_memory.lua
T
OEVGandClaude Sonnet 5.5 00c7860f31 GentleGC v2.7 (unified edition)
Squashed history of the earlier README and patch commits.
Original history kept locally on branch backup/pre-squash.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwtugSE5NbbiWQCbbKX
2026-10-31 21:33:36 +00:00

155 lines
6.2 KiB
Lua

-- Per-addon memory: the !!!!GentleGC_Memory companion measuring a simulated
-- login, and Gentle GC reporting it.
-- Run: lua5.0 tests/test_memory.lua GentleGC.lua "!!!!GentleGC_Memory/GentleGC_Memory.lua"
local MAIN, COMPANION = arg[1], arg[2]
format = string.format
local now = 1000
function GetTime() return now end
function UnitAffectingCombat() return false end
function UnitIsAFK() return false end
function date() return "2026-10-21 12:00" end
local log = {}
DEFAULT_CHAT_FRAME = { AddMessage = function(_, m) table.insert(log, m); print(" " .. m) end }
local origCG = collectgarbage
local ADDONS, LOADED
-- frames, with events dispatched in registration order like the client
local FRAMES
function CreateFrame()
local f = { scripts = {}, events = {} }
function f:RegisterEvent(e) self.events[e] = true end
function f:UnregisterEvent(e) self.events[e] = nil end
function f:UnregisterAllEvents() self.events = {} end
function f:RegisterAllEvents() end
function f:SetScript(k, fn) self.scripts[k] = fn end
table.insert(FRAMES, f)
return f
end
local function fire(e, a1)
for _, f in ipairs(FRAMES) do
if f.events[e] and f.scripts.OnEvent then event, arg1 = e, a1; this = f; f.scripts.OnEvent() end
end
end
function GetNumAddOns() return table.getn(ADDONS) end
function GetAddOnInfo(i)
local a = type(i) == "number" and ADDONS[i] or nil
if not a then return nil end
return a.name, a.name, "", a.enabled and 1 or nil
end
function IsAddOnLoaded(name) return LOADED[name] and 1 or nil end
local function count(pat) local n = 0; for _, m in ipairs(log) do if string.find(m, pat) then n = n + 1 end end; return n end
local function lastMatch(pat) for i = table.getn(log), 1, -1 do if string.find(log[i], pat) then return log[i] end end; return "" end
local function check(name, c) print((c and "PASS " or "FAIL ") .. name); if not c then FAILED = true end end
-- an addon's data: tables kept in a global, roughly `mb` megabytes
KEEP = {}
local function allocate(name, mb)
local t = {}
for i = 1, mb * 9000 do t[i] = { i } end
KEEP[name] = t
end
-- one full login: every enabled addon in order, then login and world events
-- sizes: MB per addon; withCompanion: install the companion
local function login(sizes, withCompanion)
FRAMES, LOADED, KEEP = {}, {}, {}
collectgarbage = origCG
SlashCmdList = {}
GentleGCMemory = nil
origCG()
for _, a in ipairs(ADDONS) do
if a.enabled and (withCompanion or a.name ~= "!!!!GentleGC_Memory") then
if a.name == "!!!!GentleGC_Memory" then
dofile(COMPANION)
elseif a.name == "GentleGC" then
dofile(MAIN)
else
allocate(a.name, sizes[a.name] or 0)
end
LOADED[a.name] = true
fire("ADDON_LOADED", a.name)
end
end
allocate("variables", 0.5)
fire("PLAYER_LOGIN")
allocate("login setup", 3)
fire("PLAYER_ENTERING_WORLD")
end
local SIZES = { ["!!!ClassicAPI"] = 1, Atlas = 2, Bagshui = 5, pfQuest = 20, Wrekkit = 1 }
ADDONS = {
{ name = "!!!ClassicAPI", enabled = true },
{ name = "!!!!GentleGC_Memory", enabled = true },
{ name = "Atlas", enabled = true },
{ name = "Bagshui", enabled = true },
{ name = "GentleGC", enabled = true },
{ name = "pfQuest", enabled = true },
{ name = "Wrekkit", enabled = true },
}
-- (the client loads "!!!!" before "!!!", but the test puts ClassicAPI first to
-- check the "loaded before" list)
local function rowMB(name)
local m = GentleGCMemoryDB.measured
for i = 1, table.getn(m.rows) do if m.rows[i][1] == name then return m.rows[i][2] / 1024 end end
end
local function near(v, want) return v and math.abs(v - want) <= math.max(0.35, want * 0.15) end
print("1. first login with the companion: measured")
login(SIZES, true)
local m = GentleGCMemoryDB and GentleGCMemoryDB.measured
check("a measurement was saved", m ~= nil and table.getn(m.rows) >= 5)
check("first login is the reason", m and m.reason == "first login")
check("Atlas ~2 MB", near(rowMB("Atlas"), 2))
check("Bagshui ~5 MB", near(rowMB("Bagshui"), 5))
check("pfQuest ~20 MB", near(rowMB("pfQuest"), 20))
check("Wrekkit ~1 MB", near(rowMB("Wrekkit"), 1))
check("ClassicAPI listed as loaded before", m and m.before[1] == "!!!ClassicAPI")
check("summary printed after loading", string.find(lastMatch("measured your addons"), "Biggest: pfQuest") ~= nil)
check("companion stopped measuring", GentleGCMemory.measuring == false)
print("2. /ggc addons")
local cmd = SlashCmdList.GENTLEGC
cmd("addons")
check("header line", count("addon memory at login on 2026%-10%-21") == 1)
local first
for i = 1, table.getn(log) do if string.find(log[i], "addon memory at login") then first = log[i + 1] end end
check("biggest addon on the first row", first and string.find(first, "pfQuest") ~= nil)
check("login stage shown", count("then addons setting up at login") == 1)
check("loaded-before line", count("counted in the interface number: !!!ClassicAPI") == 1)
cmd("")
check("status names the biggest addons", string.find(lastMatch("biggest addons"), "pfQuest") ~= nil)
check("measuring collections not counted as other addons'", count("by other addons") == 0)
check("nor as automatic", string.find(lastMatch("session:"), "0 automatic") ~= nil)
print("3. next login, same addons: not measured again")
local when = m
login(SIZES, true)
check("same measurement kept", GentleGCMemoryDB.measured == when)
check("no summary this time", count("measured your addons") == 1)
print("4. an addon added: measured again")
table.insert(ADDONS, { name = "ShaguTweaks", enabled = true })
SIZES.ShaguTweaks = 3
login(SIZES, true)
check("new measurement", GentleGCMemoryDB.measured ~= when and GentleGCMemoryDB.measured.reason == "addons changed")
check("ShaguTweaks ~3 MB", near(rowMB("ShaguTweaks"), 3))
print("5. /ggc addons measure: measured on the next reload")
local before = GentleGCMemoryDB.measured
SlashCmdList.GENTLEGC("addons measure")
check("asks for a reload", count("type /reload to measure") == 1)
login(SIZES, true)
check("measured because it was requested", GentleGCMemoryDB.measured ~= before and GentleGCMemoryDB.measured.reason == "requested")
print("6. without the companion")
GentleGCMemoryDB = nil
login(SIZES, false)
SlashCmdList.GENTLEGC("addons")
check("explains the companion folder", count("needs the !!!!GentleGC_Memory folder") == 1)
print(FAILED and "SOME TESTS FAILED" or "ALL TESTS PASSED")