mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 22:11:38 +00:00
Complete comprehensive plugin tests and add new stress test files
- Marked all todos in the comprehensive_plugin_tests plan as completed, indicating that all planned tests have been implemented. - Introduced new stress test files for InstanceMap, PatchSet, and encoding functionalities, covering various scenarios including large batch operations, conflict resolution, and deep/wide hierarchies. - Enhanced the testing framework to ensure thorough validation of plugin functionalities and edge cases.
This commit is contained in:
parent
180e1f9078
commit
01110fc916
20 changed files with 8211 additions and 15 deletions
420
plugin/src/InstanceMap.stress.spec.lua
Normal file
420
plugin/src/InstanceMap.stress.spec.lua
Normal file
|
|
@ -0,0 +1,420 @@
|
|||
--[[
|
||||
Stress tests for InstanceMap.
|
||||
|
||||
Tests scale, rapid operations, and concurrency.
|
||||
]]
|
||||
|
||||
return function()
|
||||
local InstanceMap = require(script.Parent.InstanceMap)
|
||||
local testUtils = require(script.Parent.testUtils)
|
||||
local LargeTreeGenerator = testUtils.LargeTreeGenerator
|
||||
|
||||
local HttpService = game:GetService("HttpService")
|
||||
|
||||
-- Test container for cleanup
|
||||
local container
|
||||
|
||||
beforeEach(function()
|
||||
container = Instance.new("Folder")
|
||||
container.Name = "InstanceMapStressTestContainer"
|
||||
container.Parent = game:GetService("Workspace")
|
||||
end)
|
||||
|
||||
afterEach(function()
|
||||
if container then
|
||||
container:Destroy()
|
||||
container = nil
|
||||
end
|
||||
end)
|
||||
|
||||
local function generateId()
|
||||
return HttpService:GenerateGUID(false)
|
||||
end
|
||||
|
||||
describe("large scale operations", function()
|
||||
it("should handle inserting 100 instances", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local instances = {}
|
||||
for i = 1, 100 do
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Instance_" .. tostring(i)
|
||||
folder.Parent = container
|
||||
table.insert(instances, folder)
|
||||
end
|
||||
|
||||
local startTime = os.clock()
|
||||
for _, instance in ipairs(instances) do
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, instance)
|
||||
end
|
||||
local elapsed = os.clock() - startTime
|
||||
|
||||
expect(instanceMap:size()).to.equal(100)
|
||||
expect(elapsed < 1).to.equal(true)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should handle inserting 500 instances", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local instances = {}
|
||||
for i = 1, 500 do
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Instance_" .. tostring(i)
|
||||
folder.Parent = container
|
||||
table.insert(instances, folder)
|
||||
end
|
||||
|
||||
local startTime = os.clock()
|
||||
for _, instance in ipairs(instances) do
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, instance)
|
||||
end
|
||||
local elapsed = os.clock() - startTime
|
||||
|
||||
expect(instanceMap:size()).to.equal(500)
|
||||
expect(elapsed < 3).to.equal(true)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should handle removing 100 instances", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local ids = {}
|
||||
for i = 1, 100 do
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Instance_" .. tostring(i)
|
||||
folder.Parent = container
|
||||
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
table.insert(ids, id)
|
||||
end
|
||||
|
||||
expect(instanceMap:size()).to.equal(100)
|
||||
|
||||
local startTime = os.clock()
|
||||
for _, id in ipairs(ids) do
|
||||
instanceMap:removeId(id)
|
||||
end
|
||||
local elapsed = os.clock() - startTime
|
||||
|
||||
expect(instanceMap:size()).to.equal(0)
|
||||
expect(elapsed < 1).to.equal(true)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("rapid insert/remove cycles", function()
|
||||
it("should handle rapid insert-remove cycles", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
for _ = 1, 50 do
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Temporary"
|
||||
folder.Parent = container
|
||||
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
expect(instanceMap.fromIds[id]).to.equal(folder)
|
||||
|
||||
instanceMap:removeId(id)
|
||||
expect(instanceMap.fromIds[id]).to.equal(nil)
|
||||
|
||||
folder:Destroy()
|
||||
end
|
||||
|
||||
expect(instanceMap:size()).to.equal(0)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should handle alternating insert/remove on same instance", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Persistent"
|
||||
folder.Parent = container
|
||||
|
||||
for _ = 1, 20 do
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
instanceMap:removeInstance(folder)
|
||||
end
|
||||
|
||||
expect(instanceMap:size()).to.equal(0)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("bidirectional lookup", function()
|
||||
it("should maintain consistent bidirectional mapping", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local instances = {}
|
||||
local ids = {}
|
||||
|
||||
for i = 1, 100 do
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Instance_" .. tostring(i)
|
||||
folder.Parent = container
|
||||
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
|
||||
instances[i] = folder
|
||||
ids[i] = id
|
||||
end
|
||||
|
||||
-- Verify bidirectional lookup
|
||||
for i = 1, 100 do
|
||||
expect(instanceMap.fromIds[ids[i]]).to.equal(instances[i])
|
||||
expect(instanceMap.fromInstances[instances[i]]).to.equal(ids[i])
|
||||
end
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should update mapping when re-inserting with different ID", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Test"
|
||||
folder.Parent = container
|
||||
|
||||
local id1 = generateId()
|
||||
local id2 = generateId()
|
||||
|
||||
instanceMap:insert(id1, folder)
|
||||
expect(instanceMap.fromIds[id1]).to.equal(folder)
|
||||
|
||||
instanceMap:insert(id2, folder)
|
||||
expect(instanceMap.fromIds[id1]).to.equal(nil)
|
||||
expect(instanceMap.fromIds[id2]).to.equal(folder)
|
||||
expect(instanceMap.fromInstances[folder]).to.equal(id2)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("destroy operations", function()
|
||||
it("should destroy instance and remove from map", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "ToDestroy"
|
||||
folder.Parent = container
|
||||
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
|
||||
instanceMap:destroyId(id)
|
||||
|
||||
expect(instanceMap.fromIds[id]).to.equal(nil)
|
||||
expect(folder.Parent).to.equal(nil)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should destroy instance by instance reference", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "ToDestroy"
|
||||
folder.Parent = container
|
||||
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
|
||||
instanceMap:destroyInstance(folder)
|
||||
|
||||
expect(instanceMap.fromIds[id]).to.equal(nil)
|
||||
expect(instanceMap.fromInstances[folder]).to.equal(nil)
|
||||
expect(folder.Parent).to.equal(nil)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should remove descendant mappings when destroying parent", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local parent = Instance.new("Folder")
|
||||
parent.Name = "Parent"
|
||||
parent.Parent = container
|
||||
|
||||
local child = Instance.new("Folder")
|
||||
child.Name = "Child"
|
||||
child.Parent = parent
|
||||
|
||||
local grandchild = Instance.new("Folder")
|
||||
grandchild.Name = "Grandchild"
|
||||
grandchild.Parent = child
|
||||
|
||||
local parentId = generateId()
|
||||
local childId = generateId()
|
||||
local grandchildId = generateId()
|
||||
|
||||
instanceMap:insert(parentId, parent)
|
||||
instanceMap:insert(childId, child)
|
||||
instanceMap:insert(grandchildId, grandchild)
|
||||
|
||||
expect(instanceMap:size()).to.equal(3)
|
||||
|
||||
instanceMap:destroyInstance(parent)
|
||||
|
||||
expect(instanceMap:size()).to.equal(0)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("pause/unpause", function()
|
||||
it("should pause updates for instance", function()
|
||||
local changeCount = 0
|
||||
local instanceMap = InstanceMap.new(function()
|
||||
changeCount = changeCount + 1
|
||||
end)
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Test"
|
||||
folder.Parent = container
|
||||
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
|
||||
instanceMap:pauseInstance(folder)
|
||||
|
||||
-- Changes while paused should not trigger callback
|
||||
-- (Actual triggering depends on the RunService check)
|
||||
|
||||
instanceMap:unpauseInstance(folder)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should unpause all instances", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local folders = {}
|
||||
for i = 1, 10 do
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Test_" .. tostring(i)
|
||||
folder.Parent = container
|
||||
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
instanceMap:pauseInstance(folder)
|
||||
table.insert(folders, folder)
|
||||
end
|
||||
|
||||
-- All should be paused
|
||||
for _, folder in ipairs(folders) do
|
||||
expect(instanceMap.pausedUpdateInstances[folder]).to.equal(true)
|
||||
end
|
||||
|
||||
instanceMap:unpauseAllInstances()
|
||||
|
||||
-- All should be unpaused
|
||||
for _, folder in ipairs(folders) do
|
||||
expect(instanceMap.pausedUpdateInstances[folder]).to.equal(nil)
|
||||
end
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("stop cleanup", function()
|
||||
it("should clean up all mappings on stop", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
for i = 1, 50 do
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Name = "Test_" .. tostring(i)
|
||||
folder.Parent = container
|
||||
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, folder)
|
||||
end
|
||||
|
||||
expect(instanceMap:size()).to.equal(50)
|
||||
|
||||
instanceMap:stop()
|
||||
|
||||
expect(instanceMap:size()).to.equal(0)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("edge cases", function()
|
||||
it("should handle removing non-existent ID", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
-- Should not throw
|
||||
instanceMap:removeId("non-existent-id")
|
||||
|
||||
expect(instanceMap:size()).to.equal(0)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should handle removing non-tracked instance", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local folder = Instance.new("Folder")
|
||||
folder.Parent = container
|
||||
|
||||
-- Should not throw
|
||||
instanceMap:removeInstance(folder)
|
||||
|
||||
expect(instanceMap:size()).to.equal(0)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
|
||||
it("should handle destroying non-existent ID", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
-- Should not throw
|
||||
instanceMap:destroyId("non-existent-id")
|
||||
|
||||
expect(instanceMap:size()).to.equal(0)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("performance", function()
|
||||
it("should handle tree with 200+ instances", function()
|
||||
local instanceMap = InstanceMap.new()
|
||||
|
||||
local root = LargeTreeGenerator.createInstanceTree({
|
||||
depth = 3,
|
||||
width = 6,
|
||||
instanceType = "Folder",
|
||||
})
|
||||
root.Parent = container
|
||||
|
||||
local function insertRecursive(instance)
|
||||
local id = generateId()
|
||||
instanceMap:insert(id, instance)
|
||||
|
||||
for _, child in ipairs(instance:GetChildren()) do
|
||||
insertRecursive(child)
|
||||
end
|
||||
end
|
||||
|
||||
local startTime = os.clock()
|
||||
insertRecursive(root)
|
||||
local elapsed = os.clock() - startTime
|
||||
|
||||
expect(instanceMap:size() > 200).to.equal(true)
|
||||
expect(elapsed < 2).to.equal(true)
|
||||
|
||||
instanceMap:stop()
|
||||
end)
|
||||
end)
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue