Apply Patch
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
-- Raid-scale behaviour: careful mode when collections are slow, collections
|
||||
-- run by other addons, and the garbage creation rate.
|
||||
-- Run: lua5.0 tests/test_raid.lua GentleGC.lua
|
||||
format = string.format
|
||||
local now = 1000
|
||||
function GetTime() return now end
|
||||
local combat, speed = false, 7
|
||||
function UnitAffectingCombat() return combat end
|
||||
function UnitIsAFK() return false end
|
||||
function GetUnitSpeed() return speed, 7, 0, 4.7 end -- ClassicAPI: enables stand-still
|
||||
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
|
||||
|
||||
-- the call stack another addon's collectgarbage() call is made from
|
||||
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 `FreeMemory'\n" end
|
||||
return s .. "[C]: ?\n"
|
||||
end
|
||||
|
||||
-- a slow collector: every full collection advances the clock by `gcCost`
|
||||
local gcCost = 0.3
|
||||
local origCG = collectgarbage
|
||||
collectgarbage = function(n)
|
||||
if n == nil or n == 0 then now = now + gcCost end
|
||||
return origCG(n)
|
||||
end
|
||||
local unwrappedCG = collectgarbage -- what an addon that localized it before us holds
|
||||
|
||||
LIVE = {}
|
||||
for i = 1, 300000 do LIVE[i] = { i } end
|
||||
origCG()
|
||||
|
||||
dofile(arg[1])
|
||||
local F = FRAMES[1]
|
||||
local function fire(e, a1) event, arg1 = e, a1; this = F; F.scripts.OnEvent() end
|
||||
fire("ADDON_LOADED", "GentleGC")
|
||||
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
|
||||
-- one rendered frame: per-frame sampler, then the main frame's OnUpdate
|
||||
local HF = FRAMES[2]
|
||||
local function frame(dt)
|
||||
now = now + dt
|
||||
SF.scripts.OnUpdate()
|
||||
arg1 = dt; this = F
|
||||
F.scripts.OnUpdate()
|
||||
if HF.scripts.OnUpdate then HF.scripts.OnUpdate() end
|
||||
end
|
||||
local function seconds(n) for i = 1, n * 10 do frame(0.1) end end
|
||||
local function garbageMB(mb)
|
||||
local t = {}
|
||||
for i = 1, mb * 7000 do t[i] = { i, i } end
|
||||
end
|
||||
local function liveMB() local _, _, v = string.find(lastMatch("heap .* live ~"), "live ~([%d%.]+)mb"); return tonumber(v) end
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
print("1. slow collections switch to careful mode")
|
||||
garbageMB(8)
|
||||
fire("PLAYER_LEAVING_WORLD")
|
||||
fire("PLAYER_ENTERING_WORLD")
|
||||
check("loading-screen collection ran", count("loading screen:") >= 1)
|
||||
cmd("status")
|
||||
check("status shows careful mode", string.find(lastMatch("collections take"), "~[23]%d%dms: careful mode") ~= nil)
|
||||
|
||||
print("2. careful mode: no stand-still or idle collections")
|
||||
garbageMB(8)
|
||||
speed = 0
|
||||
seconds(10)
|
||||
check("no stand-still collection", count("standing still:") == 0)
|
||||
now = now + 400
|
||||
seconds(3)
|
||||
check("no idle collection", count("idle:") == 0)
|
||||
speed = 7
|
||||
|
||||
print("3. careful mode: NPC windows and pressure still collect")
|
||||
fire("MERCHANT_SHOW")
|
||||
check("merchant collection ran", count("merchant:") == 1)
|
||||
local _, thr = gcinfo()
|
||||
do
|
||||
local junk = {}
|
||||
local i = 0
|
||||
while gcinfo() < thr * 0.9 do
|
||||
for k = 1, 500 do i = i + 1; junk[i] = { i } end
|
||||
if i > 30000 then junk = {}; i = 0 end
|
||||
SF.scripts.OnUpdate()
|
||||
end
|
||||
end
|
||||
seconds(6)
|
||||
check("pressure collection ran", count("pressure:") == 1)
|
||||
|
||||
print("4. fast collections switch back to normal")
|
||||
gcCost = 0.01
|
||||
for k = 1, 6 do now = now + 40; garbageMB(8); fire("MAIL_SHOW") end
|
||||
cmd("status")
|
||||
check("back to normal mode", string.find(lastMatch("collections take"), "normal mode") ~= nil)
|
||||
garbageMB(8)
|
||||
now = now + 40
|
||||
speed = 0
|
||||
seconds(5)
|
||||
check("stand-still collection runs again", count("standing still:") == 1)
|
||||
speed = 7
|
||||
|
||||
print("5. /ggc slow 0 turns careful mode off")
|
||||
gcCost = 0.3
|
||||
cmd("slow 0")
|
||||
for k = 1, 3 do now = now + 40; garbageMB(8); fire("BANKFRAME_OPENED") end
|
||||
cmd("status")
|
||||
check("normal mode despite slow collections", string.find(lastMatch("collections take"), "normal mode") ~= nil)
|
||||
cmd("slow 100")
|
||||
gcCost = 0.01
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
print("6. another addon's collectgarbage() is attributed, not 'automatic'")
|
||||
local autoBefore = count("automatic GC detected")
|
||||
garbageMB(10)
|
||||
seconds(1)
|
||||
caller = "Wrekkit"
|
||||
collectgarbage()
|
||||
caller = nil
|
||||
seconds(2)
|
||||
check("logged as Wrekkit's collection", string.find(lastMatch("collection by"), "collection by Wrekkit: freed") ~= nil)
|
||||
check("not counted as automatic", count("automatic GC detected") == autoBefore)
|
||||
cmd("status")
|
||||
check("status lists it by name", string.find(lastMatch("by other addons"), "Wrekkit 1") ~= nil)
|
||||
local real = gcinfo() / 1024
|
||||
check("live estimate matches real live memory", math.abs(liveMB() - real) < 1)
|
||||
|
||||
print("7. an addon setting the threshold (no collection) changes nothing")
|
||||
local liveBefore = liveMB()
|
||||
caller = "FpsWatch"
|
||||
local u = gcinfo()
|
||||
collectgarbage(u * 3)
|
||||
caller = nil
|
||||
seconds(2)
|
||||
cmd("status")
|
||||
check("no collection counted", string.find(lastMatch("by other addons"), "FpsWatch") == nil)
|
||||
check("no automatic GC counted", count("automatic GC detected") == autoBefore)
|
||||
check("live estimate unchanged", math.abs(liveMB() - liveBefore) < 0.5)
|
||||
origCG(math.floor(u * 1.5) + 2048) -- (back to a normal threshold for the next steps)
|
||||
seconds(1)
|
||||
|
||||
print("8. an addon we couldn't wrap: detected from the heap dropping below the threshold")
|
||||
garbageMB(10)
|
||||
seconds(1)
|
||||
unwrappedCG()
|
||||
seconds(2)
|
||||
check("counted as another addon's (unknown)", string.find(lastMatch("collection by"), "collection by unknown addon") ~= nil)
|
||||
check("still not counted as automatic", count("automatic GC detected") == autoBefore)
|
||||
|
||||
print("9. the game's collector firing at the threshold is still 'automatic'")
|
||||
do
|
||||
local _, t = gcinfo()
|
||||
local junk = {}
|
||||
local i = 0
|
||||
local _, cur = gcinfo()
|
||||
while cur == t do
|
||||
for k = 1, 500 do i = i + 1; junk[i] = { i } end
|
||||
if i > 30000 then junk = {}; i = 0 end
|
||||
SF.scripts.OnUpdate()
|
||||
_, cur = gcinfo()
|
||||
end
|
||||
end
|
||||
seconds(1)
|
||||
check("counted as automatic", count("automatic GC detected") == autoBefore + 1)
|
||||
|
||||
print("10. another addon collecting in combat: combat headroom is restored")
|
||||
combat = true; fire("PLAYER_REGEN_DISABLED")
|
||||
garbageMB(6)
|
||||
caller = "Wrekkit"
|
||||
collectgarbage()
|
||||
caller = nil
|
||||
local used, t = gcinfo()
|
||||
check("threshold raised again after it", t >= used + 32 * 1024 - 64)
|
||||
combat = false; fire("PLAYER_REGEN_ENABLED")
|
||||
seconds(1)
|
||||
|
||||
print("11. freeze detector names the addon")
|
||||
cmd("hitch")
|
||||
seconds(1)
|
||||
garbageMB(10)
|
||||
caller = "Wrekkit"
|
||||
now = now + 0.4
|
||||
collectgarbage()
|
||||
caller = nil
|
||||
frame(0.05)
|
||||
check("freeze blamed on Wrekkit", string.find(lastMatch("freeze"), "run by Wrekkit") ~= nil)
|
||||
cmd("hitch 0")
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
print("12. garbage creation rate")
|
||||
cmd("allocwarn 1")
|
||||
now = now + 120
|
||||
for s = 1, 40 do
|
||||
for f = 1, 10 do
|
||||
garbageMB(0.2) -- ~2 MB/s
|
||||
frame(0.1)
|
||||
end
|
||||
end
|
||||
cmd("status")
|
||||
local _, _, rate = string.find(lastMatch("garbage created"), "([%d%.]+) MB/s now")
|
||||
rate = tonumber(rate)
|
||||
check("rate is measured (about 2 MB/s)", rate and rate > 1.2 and rate < 3.5)
|
||||
check("warning shown above the limit", count("garbage is being created at") == 1)
|
||||
for s = 1, 20 do for f = 1, 10 do garbageMB(0.2); frame(0.1) end end
|
||||
check("warning not repeated within 10 minutes", count("garbage is being created at") == 1)
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
print("13. a wrapper that blocks collectgarbage is reported, not counted")
|
||||
FRAMES = {}
|
||||
log = {}
|
||||
DEFAULT_CHAT_FRAME = { AddMessage = function(_, m) table.insert(log, m); print(" " .. m) end }
|
||||
GentleGCDB = nil
|
||||
SlashCmdList = {}
|
||||
collectgarbage = function(n) end -- an addon loaded before us that swallows every call
|
||||
dofile(arg[1])
|
||||
F = FRAMES[1]
|
||||
fire("ADDON_LOADED", "GentleGC")
|
||||
SlashCmdList.GENTLEGC("debug 1")
|
||||
garbageMB(8)
|
||||
SlashCmdList.GENTLEGC("now")
|
||||
check("reported as blocked", count("did not run") == 1)
|
||||
SlashCmdList.GENTLEGC("status")
|
||||
check("not counted as a collection", string.find(lastMatch("session:"), "0 collections") ~= nil)
|
||||
check("ratio not learned from it", count("not measured yet") == 1)
|
||||
|
||||
print(FAILED and "SOME TESTS FAILED" or "ALL TESTS PASSED")
|
||||
Reference in New Issue
Block a user