/ggc addons reads GetAddOnMemoryUsage when ClassicAPI provides it, so the !!!!GentleGC_Memory companion is only needed without it. /ggc also shows the client's scriptMemory limit and warns when scriptProfile is on. Adds tests/test_native_memory.lua. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019W3rwtugSE5NbbiWQCbbKX
147 lines
5.4 KiB
Lua
147 lines
5.4 KiB
Lua
-- Native per-addon memory (ClassicAPI), the companion fallback, and the client info lines.
|
|
-- Run: lua5.0 tests/test_native_memory.lua GentleGC.lua
|
|
|
|
format = string.format
|
|
local now = 1000
|
|
function GetTime() return now end
|
|
function UnitAffectingCombat() return false end
|
|
function UnitIsAFK() return false end
|
|
local log = {}
|
|
DEFAULT_CHAT_FRAME = { AddMessage = function(_, m) table.insert(log, m); print(" " .. m) end }
|
|
SlashCmdList = {}
|
|
FRAMES = {}
|
|
function CreateFrame()
|
|
local f = { scripts = {} }
|
|
function f:RegisterEvent() end
|
|
function f:UnregisterEvent() end
|
|
function f:SetScript(k, fn) self.scripts[k] = fn end
|
|
table.insert(FRAMES, f)
|
|
return f
|
|
end
|
|
local caller = nil
|
|
function debugstack()
|
|
local s = "Interface\\AddOns\\GentleGC\\GentleGC.lua:400: in function `WrappedCollect'\n"
|
|
if caller then s = s .. "Interface\\AddOns\\" .. caller .. "\\core.lua:12: in function `x'\n" end
|
|
return s
|
|
end
|
|
|
|
-- the client's collector: full collections leave the threshold at 1.25x live,
|
|
-- and take 10 ms + 8 ms per MB freed (simulated on the clock)
|
|
local origCG = collectgarbage
|
|
local function setThreshold() local u = gcinfo(); origCG(math.floor(u * 1.25)) end
|
|
local timed = false
|
|
collectgarbage = function(n)
|
|
if n == nil or n == 0 then
|
|
local before = gcinfo()
|
|
origCG(); setThreshold()
|
|
if timed then now = now + 0.010 + 0.008 * (before - gcinfo()) / 1024 end
|
|
else
|
|
origCG(n)
|
|
end
|
|
end
|
|
local unwrapped = collectgarbage -- what an addon holds if it loaded before us
|
|
|
|
LIVE = {}
|
|
for i = 1, 300000 do LIVE[i] = { i } end
|
|
origCG(); setThreshold()
|
|
|
|
dofile(arg[1])
|
|
local F = FRAMES[1]
|
|
event, arg1 = "ADDON_LOADED", "GentleGC"; this = F; F.scripts.OnEvent()
|
|
local SF = FRAMES[4]
|
|
local cmd = SlashCmdList.GENTLEGC
|
|
cmd("debug 2")
|
|
|
|
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
|
|
local function clear() log = {} end
|
|
local function has(pat) return count(pat) > 0 end
|
|
|
|
print("1. without ClassicAPI's functions: the companion is asked for")
|
|
clear()
|
|
cmd("addons")
|
|
check("explains the companion", has("per%-addon memory needs"))
|
|
cmd("addons measure")
|
|
check("measure falls through to the companion text", has("per%-addon memory needs"))
|
|
|
|
-- a stand-in for ClassicAPI's profiling functions
|
|
local names = {}
|
|
local kbs = {}
|
|
local ADDONS = 20
|
|
for i = 1, ADDONS do names[i] = "Addon" .. i; kbs[i] = i * 100 end -- 100 KB .. 2000 KB
|
|
names[21] = "Blizzard_Thing"; kbs[21] = 0 -- Blizzard / not loaded: 0
|
|
local updates = 0
|
|
function GetNumAddOns() return table.getn(names) end
|
|
function GetAddOnInfo(i) return names[i] end
|
|
function UpdateAddOnMemoryUsage() updates = updates + 1 end
|
|
function GetAddOnMemoryUsage(i)
|
|
if i < 1 or i > table.getn(names) then error("bad index") end
|
|
return kbs[i]
|
|
end
|
|
|
|
print("2. with them: a live list, biggest first, top 15")
|
|
clear()
|
|
cmd("addons")
|
|
check("one update per listing", updates == 1)
|
|
check("header says where it comes from", has("addon memory now %(from ClassicAPI%)"))
|
|
check("total counts the 20 loaded addons", has("in 20 addons"))
|
|
check("zero rows (not loaded) are left out", not has("Blizzard_Thing"))
|
|
check("biggest is first", string.find(log[2], "Addon20") ~= nil)
|
|
check("15 rows shown", count("Addon%d+") == 15)
|
|
check("says how many more", has("and 5 more"))
|
|
check("no growth on the first check", not has("since the last check"))
|
|
|
|
print("3. 'all' lists every addon")
|
|
clear()
|
|
cmd("addons all")
|
|
check("20 rows", count("Addon%d+") == 20)
|
|
|
|
print("4. growth since the last check is shown")
|
|
kbs[3] = kbs[3] + 3000
|
|
kbs[4] = kbs[4] + 50 -- under 100 KB: not worth a note
|
|
clear()
|
|
cmd("addons all")
|
|
check("Addon3 grew", string.find(lastMatch("Addon3 "), "%+2%.9 MB since the last check") ~= nil or string.find(lastMatch("Addon3"), "since the last check") ~= nil)
|
|
check("Addon4's small change is not flagged", string.find(lastMatch("Addon4 "), "since the last check") == nil)
|
|
|
|
print("5. measure is not needed")
|
|
clear()
|
|
cmd("addons measure")
|
|
check("says so", has("no measuring needed"))
|
|
|
|
print("6. a client where the update call errors falls back")
|
|
local good = UpdateAddOnMemoryUsage
|
|
function UpdateAddOnMemoryUsage() error("nope") end
|
|
clear()
|
|
cmd("addons")
|
|
check("falls back to the companion text", has("per%-addon memory needs"))
|
|
UpdateAddOnMemoryUsage = good
|
|
|
|
print("7. everything reporting 0 falls back")
|
|
local saved = {}
|
|
for i = 1, table.getn(kbs) do saved[i] = kbs[i]; kbs[i] = 0 end
|
|
clear()
|
|
cmd("addons")
|
|
check("falls back", has("per%-addon memory needs"))
|
|
for i = 1, table.getn(kbs) do kbs[i] = saved[i] end
|
|
|
|
print("8. client info lines in /ggc")
|
|
clear()
|
|
cmd("status")
|
|
check("no cvar function: no client lines", not has("client:"))
|
|
local cvars = { scriptMemory = "0", scriptProfile = "0" }
|
|
function GetCVar(k) return cvars[k] end
|
|
-- the slash command prints them after the status block
|
|
clear()
|
|
cmd("status")
|
|
check("limit off", has("client: interface memory limit off"))
|
|
check("profiling off prints no warning", not has("CPU profiling is ON"))
|
|
cvars.scriptMemory = "65536"; cvars.scriptProfile = "1"
|
|
clear()
|
|
cmd("status")
|
|
check("limit shown", has("interface memory limit 64.0 MB"))
|
|
check("profiling warning", has("CPU profiling is ON"))
|
|
|
|
print(FAILED and "SOME TESTS FAILED" or "ALL TESTS PASSED")
|