-- Guda ClassicAPI runtime self-test -- Read-only diagnostics for the test branch. Run /gudatest in game. -- Lua 5.0 compatible. local addon = Guda local SelfTest = {} addon.Modules.ClassicAPISelfTest = SelfTest local function Bool(v) return v and "yes" or "no" end local function ExtractItemID(link) if not link then return nil end local _, _, id = string.find(link, "item:(%d+)") return id and tonumber(id) or nil end local function IsKnownQuality(value) value = tonumber(value) return value ~= nil and value >= 0 end local function AddMismatch(state, text) state.mismatches = state.mismatches + 1 if state.mismatches <= 12 then addon:Print("|cffff6060Mismatch:|r %s", text) end end local function CompareContainer(state, bagID, label) local api = addon.Modules.ClassicAPI local numSlots = GetContainerNumSlots(bagID) or 0 for slotID = 1, numSlots do local texture, count, locked, quality, readable = GetContainerItemInfo(bagID, slotID) local link = GetContainerItemLink(bagID, slotID) local vanillaOccupied = texture ~= nil or link ~= nil local ok, info = pcall(api.GetContainerItemInfo, api, bagID, slotID) if not ok then AddMismatch(state, format("%s %d:%d ClassicAPI call errored", label, bagID, slotID)) else local classicOccupied = info ~= nil state.checked = state.checked + 1 if vanillaOccupied ~= classicOccupied then AddMismatch(state, format("%s %d:%d occupancy Vanilla=%s ClassicAPI=%s", label, bagID, slotID, Bool(vanillaOccupied), Bool(classicOccupied))) elseif vanillaOccupied and info then local vanillaID = ExtractItemID(link) local classicID = info.itemID and tonumber(info.itemID) or nil if vanillaID and classicID and vanillaID ~= classicID then AddMismatch(state, format("%s %d:%d itemID %s ~= %s", label, bagID, slotID, tostring(vanillaID), tostring(classicID))) end -- An occupied slot cannot have a real stack size <= 0. The -- legacy 1.12/Turtle GetContainerItemInfo path can expose -- negative sentinel-like values for some special bank items. -- Treat only that invalid Vanilla value as non-comparable; -- any disagreement between two positive counts still fails. local vanillaCount = tonumber(count) local classicCount = tonumber(info.stackCount) if vanillaCount and vanillaCount <= 0 then state.countUnknown = state.countUnknown + 1 else vanillaCount = vanillaCount or 1 classicCount = classicCount or 1 if vanillaCount ~= classicCount then AddMismatch(state, format("%s %d:%d count %s ~= %s", label, bagID, slotID, tostring(count), tostring(info.stackCount))) end end if (locked and true or false) ~= (info.isLocked and true or false) then AddMismatch(state, format("%s %d:%d locked state differs", label, bagID, slotID)) end if link and info.hyperlink and link ~= info.hyperlink then AddMismatch(state, format("%s %d:%d hyperlink differs", label, bagID, slotID)) end -- ClassicAPI deliberately leaves cache-dependent fields nil -- when static item data is cold. Vanilla 1.12 can also return -- quality=-1 as a legacy special/unknown value. Treat nil or -- negative quality as non-comparable, but still fail on any -- disagreement between two known quality values. if texture and info.iconFileID and texture ~= info.iconFileID then AddMismatch(state, format("%s %d:%d texture differs", label, bagID, slotID)) end if IsKnownQuality(quality) and IsKnownQuality(info.quality) then if tonumber(quality) ~= tonumber(info.quality) then AddMismatch(state, format("%s %d:%d quality %s ~= %s", label, bagID, slotID, tostring(quality), tostring(info.quality))) end elseif vanillaOccupied then state.qualityUnknown = state.qualityUnknown + 1 end if readable ~= nil and info.isReadable ~= nil and (readable and true or false) ~= (info.isReadable and true or false) then AddMismatch(state, format("%s %d:%d readable state differs", label, bagID, slotID)) end end end end end function SelfTest:Run() local api = addon.Modules.ClassicAPI addon:Print("|cffffd200=== Guda ClassicAPI self-test ===|r") if not api then addon:Print("|cffff6060FAIL: ClassicAPI integration module is missing.|r") return false end local status = api:GetStatus() addon:Print("ClassicAPI fast path: %s", status.available and "|cff60ff60ACTIVE|r" or "|cffff6060MISSING|r") addon:Print("C_Container info=%s itemID=%s charges=%s", Bool(status.containerInfo), Bool(status.containerItemID), Bool(status.containerCharges)) addon:Print("Frame helpers: EnumerateFrames=%s GetFramesRegisteredForEvent=%s", Bool(type(EnumerateFrames) == "function"), Bool(type(GetFramesRegisteredForEvent) == "function")) if not status.available then addon:Print("|cffff6060FAIL: required C_Container fast-path functions are unavailable.|r") return false end local state = { checked = 0, mismatches = 0, qualityUnknown = 0, countUnknown = 0, } for bagID = 0, 4 do CompareContainer(state, bagID, "bag") end -- Keyring exists on the target 1.12/Turtle client. pcall is inside the -- comparator, so a ClassicAPI incompatibility is reported instead of -- aborting the test. if (GetContainerNumSlots(-2) or 0) > 0 then CompareContainer(state, -2, "keyring") end if addon.Modules.BankScanner and addon.Modules.BankScanner.IsBankOpen and addon.Modules.BankScanner:IsBankOpen() then for _, bagID in ipairs(addon.Constants.BANK_BAGS or {}) do CompareContainer(state, bagID, "bank") end else addon:Print("Bank comparison skipped (open a bank and run /gudatest again).") end local budget = addon.Modules.Utils and addon.Modules.Utils.GetFrameBudget and addon.Modules.Utils:GetFrameBudget() or nil if budget then addon:Print("Queue frame budget: %.1f ms", budget * 1000) end if addon.Modules.InventoryIndex and addon.Modules.InventoryIndex.GetStats then local s = addon.Modules.InventoryIndex:GetStats() addon:Print("Inventory index: current=%d static=%d dirty(current/static)=%s/%s", s.currentItems or 0, s.staticItems or 0, Bool(s.currentDirty), Bool(s.staticDirty)) end if addon.Modules.ItemButtonPerformance and addon.Modules.ItemButtonPerformance.GetStats then local s = addon.Modules.ItemButtonPerformance:GetStats() addon:Print("ItemButton perf: watcher=%s polling=%s sharedGlows=%d active=%s", Bool(s.cursorWatcherFound), Bool(s.cursorPollingEnabled), s.sharedGlowDrivers or 0, Bool(s.sharedGlowActive)) end if state.qualityUnknown > 0 then addon:Print("Quality comparison skipped for %d slot(s) with legacy/unknown quality.", state.qualityUnknown) end if state.countUnknown > 0 then addon:Print("Count comparison skipped for %d slot(s) with invalid legacy count.", state.countUnknown) end if state.mismatches == 0 then addon:Print("|cff60ff60PASS: %d container slots checked, no API mismatches.|r", state.checked) return true end if state.mismatches > 12 then addon:Print("... %d additional mismatch(es) hidden.", state.mismatches - 12) end addon:Print("|cffff6060FAIL: %d mismatch(es) across %d checked slots.|r", state.mismatches, state.checked) return false end SLASH_GUDACLASSICAPITEST1 = "/gudatest" SlashCmdList["GUDACLASSICAPITEST"] = function() SelfTest:Run() end addon:Debug("ClassicAPI /gudatest runtime diagnostics loaded")