-- Test for ACE-94: memory reclaiming of table indices

dofile("wow_api.lua")
dofile("LibStub.lua")

local MAJOR = "AceTimer-3.0"

dofile("../"..MAJOR.."/"..MAJOR..".lua")

local AceTimer,minor = LibStub:GetLibrary(MAJOR)

local function dummy() end

local VERBOSE = strmatch(arg[1] or "", "v")

-- Initial memory that we have to include...

local objs={}
for n=1,3 do
	objs[n] = {}
end

WoWAPI_FireUpdate(GetTime()+100)

-----------------------------------------------------------------------
-- Get "empty" memory count

collectgarbage("collect")
local mem = collectgarbage("count")



-----------------------------------------------------------------------
-- Run the whole test five times to make sure the cleaner keeps working after a loop

for LOOP=1,5 do

	if VERBOSE then print("\n------------- Loop "..LOOP) end

	-----------------------------------------------------------------------
	-- Schedule a boatload of timers

	for _,obj in pairs(objs) do
		for t=1,1000 do
			AceTimer.ScheduleTimer(obj, dummy, 1)
		end
	end

	collectgarbage("collect")
	if VERBOSE then print(collectgarbage("count") - mem.."KB used - everything live") end
	-- About 688K in original test



	-----------------------------------------------------------------------
	-- Cancel them all again

	for _,obj in pairs(objs) do
		AceTimer.CancelAllTimers(obj)
	end

	-- .. and let them get removed from hash buckets over time
	WoWAPI_FireUpdate(GetTime()+100)

	collectgarbage("collect")
	local idxwaste = collectgarbage("count") - mem
	if VERBOSE then print(idxwaste.."KB used - this is table index waste. Expecting ~110 KB.") end

	-- We should be wasting about 110K here (warnmsg in original test)
	-- 1000 timers * 3 objects * ~40 bytes per index = ~120KB. Sounds about right.
	assert(idxwaste>80 and idxwaste<160, dump(idxwaste))



	-----------------------------------------------------------------------
	-- Now start firing PLAYER_REGEN_ENABLED and watch memory decrease

	-- Clean up 2 thirds
	WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")
	WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")

	-- See if 1 third remains
	collectgarbage("collect")
	local thirdwaste = collectgarbage("count") - mem

	assert(thirdwaste > idxwaste/3*0.9 and thirdwaste < idxwaste/3*1.1, dump(thirdwaste, idxwaste))

	-- Clean up last third
	WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")


	-----------------------------------------------------------------------
	-- We should now be back to near 0 mem consumption

	collectgarbage("collect")
	local endresult = collectgarbage("count") - mem
	if VERBOSE then print(endresult.."KB used - should be nearly clean") end
	-- I'm getting ~2.5K waste from system total, probably something eating a bit more after being run once, not going to go hunting for it
	-- Loops 2..n keep using the same total.
	assert(endresult>0 and endresult<10)


end -- for LOOP=1,5


-----------------------------------------------------------------------
-- Test the excessive timer count warning system
-- Also make sure the cleaner obeys our minimum operation criteria and doesn't run too often
-- (Cleaner and warning runs at the same time)

if VERBOSE then print("\n------------- Testing excessive timer count warnings") end

local obj = setmetatable({}, {
	__tostring = function(self)
		return "MyTestObj"
	end
})

assert(AceTimer.debug.BUCKETS < 1000)	-- .BUCKETS is the warning limit
for t=1,1000 do
	AceTimer.ScheduleTimer(obj, dummy, 1)
end

local warnmsg
local numwarnmsgs=0
function ChatFrame1:AddMessage(msg)
	if VERBOSE then print("Warning message emitted: <"..msg..">") end
	warnmsg = msg
	numwarnmsgs=numwarnmsgs+1
end

for _ in pairs(AceTimer.selfs) do
	WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")
end
assert(numwarnmsgs==1, dump(numwarnmsgs))
assert(strmatch(warnmsg, "MyTestObj.*has 100[1-9] live timers"), dump(warnmsg, msg))	-- it won't be 1000 since __ops etc gets counted. it'll be a little more.

-- Now it shouldn't clean&warn again since there are no operations on the table
numwarnmsgs=0
for _ in pairs(AceTimer.selfs) do
	WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")
end
assert(numwarnmsgs==0)

-- Artificially inflate __ops somewhat, still shouldn't warn
AceTimer.selfs[obj].__ops = 10
numwarnmsgs=0
for _ in pairs(AceTimer.selfs) do
	WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")
end
assert(numwarnmsgs==0)

-- Artificially inflate __ops A LOT, now it should warn again. Once.
AceTimer.selfs[obj].__ops = 1000000
numwarnmsgs=0
for LOOP=1,20 do
	for _ in pairs(AceTimer.selfs) do
		WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")
	end
end
assert(numwarnmsgs==1)


-----------------------------------------------------------------------
-- Test a fencepost case: only one addon. That one addon should be 
-- checked for every PLAYER_REGEN_ENABLED.

LibStub.libs[MAJOR] = nil
LibStub.minors[MAJOR] = nil
dofile("../"..MAJOR.."/"..MAJOR..".lua")

local AceTimer,minor = LibStub:GetLibrary(MAJOR)

assert(AceTimer.debug.BUCKETS < 1000)	-- .BUCKETS is the warning limit
for t=1,1000 do
	AceTimer.ScheduleTimer(obj, dummy, 1)
end

for LOOP=1,3 do
	numwarnmsgs=0
	AceTimer.selfs[obj].__ops = 1000000
	WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")
	assert(numwarnmsgs==1)
end

for LOOP=1,3 do
	numwarnmsgs=0
	-- no __ops increase, nothing should be checked
	WoWAPI_FireEvent("PLAYER_REGEN_ENABLED")
	assert(numwarnmsgs==0)
end




-----------------------------------------------------------------------
print "OK"
