mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 14:01:37 +00:00
- Removed unnecessary references to LargeTreeGenerator in multiple stress test files, streamlining the code. - Simplified loop iterations by replacing index variables with underscores for unused values, improving readability. - Updated the diff logic in the reify and diff tests to eliminate unused patch variables, enhancing performance and clarity. - Ensured consistent formatting across stress test files for better maintainability.
935 lines
23 KiB
Lua
935 lines
23 KiB
Lua
--[[
|
|
Stress tests for patch application.
|
|
|
|
Tests large batch operations, partial failures, ref chains,
|
|
className changes, and various edge conditions.
|
|
]]
|
|
|
|
return function()
|
|
local applyPatch = require(script.Parent.applyPatch)
|
|
local InstanceMap = require(script.Parent.Parent.InstanceMap)
|
|
local PatchSet = require(script.Parent.Parent.PatchSet)
|
|
local testUtils = require(script.Parent.Parent.testUtils)
|
|
local PatchGenerator = testUtils.PatchGenerator
|
|
|
|
local HttpService = game:GetService("HttpService")
|
|
|
|
-- Test container for cleanup
|
|
local container
|
|
|
|
beforeEach(function()
|
|
container = Instance.new("Folder")
|
|
container.Name = "ApplyPatchStressTestContainer"
|
|
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 batch additions", function()
|
|
it("should handle adding 100 instances in a single patch", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
local patch = PatchGenerator.createAdditionsPatch({
|
|
count = 100,
|
|
parentId = rootId,
|
|
className = "Folder",
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
expect(#root:GetChildren()).to.equal(100)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle adding 500 instances in a single patch", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
local patch = PatchGenerator.createAdditionsPatch({
|
|
count = 500,
|
|
parentId = rootId,
|
|
className = "Folder",
|
|
})
|
|
|
|
local startTime = os.clock()
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
local elapsed = os.clock() - startTime
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
expect(#root:GetChildren()).to.equal(500)
|
|
-- Should complete in reasonable time
|
|
expect(elapsed < 5).to.equal(true)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle nested additions (parent and children in same patch)", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
local patch, nestedRootId = PatchGenerator.createNestedAdditionsPatch({
|
|
rootParentId = rootId,
|
|
depth = 4,
|
|
width = 3,
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
-- Verify the nested structure was created
|
|
local nestedRoot = instanceMap.fromIds[nestedRootId]
|
|
expect(nestedRoot).to.be.ok()
|
|
expect(nestedRoot.Parent).to.equal(root)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("large batch removals", function()
|
|
it("should handle removing 100 instances in a single patch", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Create 100 children
|
|
local childIds = {}
|
|
for i = 1, 100 do
|
|
local child = Instance.new("Folder")
|
|
child.Name = "Child_" .. tostring(i)
|
|
child.Parent = root
|
|
|
|
local childId = generateId()
|
|
instanceMap:insert(childId, child)
|
|
table.insert(childIds, childId)
|
|
end
|
|
|
|
expect(#root:GetChildren()).to.equal(100)
|
|
|
|
local patch = PatchGenerator.createRemovalsPatch({ ids = childIds })
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
expect(#root:GetChildren()).to.equal(0)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle removing instances by instance reference", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
local child = Instance.new("Folder")
|
|
child.Name = "Child"
|
|
child.Parent = root
|
|
|
|
local childId = generateId()
|
|
instanceMap:insert(childId, child)
|
|
|
|
-- Remove by instance reference instead of ID
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.removed, child)
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
expect(#root:GetChildren()).to.equal(0)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("large batch updates", function()
|
|
it("should handle updating 100 instances in a single patch", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Create 100 StringValues
|
|
local ids = {}
|
|
for i = 1, 100 do
|
|
local sv = Instance.new("StringValue")
|
|
sv.Name = "Value_" .. tostring(i)
|
|
sv.Value = "Original"
|
|
sv.Parent = root
|
|
|
|
local id = generateId()
|
|
instanceMap:insert(id, sv)
|
|
table.insert(ids, id)
|
|
end
|
|
|
|
-- Create update patch
|
|
local patch = PatchSet.newEmpty()
|
|
for i, id in ipairs(ids) do
|
|
table.insert(patch.updated, {
|
|
id = id,
|
|
changedProperties = {
|
|
Value = { String = "Updated_" .. tostring(i) },
|
|
},
|
|
})
|
|
end
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
-- Verify all values were updated
|
|
for i, id in ipairs(ids) do
|
|
local instance = instanceMap.fromIds[id]
|
|
expect(instance.Value).to.equal("Updated_" .. tostring(i))
|
|
end
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("mixed operations", function()
|
|
it("should handle adds, removes, and updates in the same patch", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Create some existing instances
|
|
local toRemove = Instance.new("Folder")
|
|
toRemove.Name = "ToRemove"
|
|
toRemove.Parent = root
|
|
local toRemoveId = generateId()
|
|
instanceMap:insert(toRemoveId, toRemove)
|
|
|
|
local toUpdate = Instance.new("StringValue")
|
|
toUpdate.Name = "ToUpdate"
|
|
toUpdate.Value = "Original"
|
|
toUpdate.Parent = root
|
|
local toUpdateId = generateId()
|
|
instanceMap:insert(toUpdateId, toUpdate)
|
|
|
|
-- Create mixed patch
|
|
local patch = PatchGenerator.createMixedPatch({
|
|
addCount = 5,
|
|
removeIds = { toRemoveId },
|
|
updateIds = { toUpdateId },
|
|
parentId = rootId,
|
|
})
|
|
|
|
-- Override the update with proper property change
|
|
for _, update in ipairs(patch.updated) do
|
|
if update.id == toUpdateId then
|
|
update.changedProperties = { Value = { String = "Updated" } }
|
|
end
|
|
end
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
-- 5 added + 1 remaining (toUpdate) = 6
|
|
expect(#root:GetChildren()).to.equal(6)
|
|
expect(toUpdate.Value).to.equal("Updated")
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("partial failures", function()
|
|
it("should return unapplied additions when instance creation fails", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Create patch with invalid class name
|
|
local patch = PatchGenerator.createInvalidClassPatch({
|
|
count = 5,
|
|
parentId = rootId,
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
-- All 5 invalid instances should be in unapplied
|
|
local unappliedCount = 0
|
|
for _ in pairs(unapplied.added) do
|
|
unappliedCount += 1
|
|
end
|
|
expect(unappliedCount).to.equal(5)
|
|
expect(#root:GetChildren()).to.equal(0)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should return unapplied updates when property setting fails", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Try to set a non-existent property
|
|
-- Note: setProperty may handle unknown properties gracefully (not fail)
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.updated, {
|
|
id = rootId,
|
|
changedProperties = {
|
|
-- Folders don't have a Value property
|
|
Value = { String = "Test" },
|
|
},
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
-- The implementation may or may not track unknown property assignments
|
|
-- as unapplied. This test verifies the operation completes.
|
|
expect(unapplied).to.be.ok()
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle mixed success and failure in same update", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local sv = Instance.new("StringValue")
|
|
sv.Name = "Original"
|
|
sv.Value = "Original"
|
|
sv.Parent = container
|
|
|
|
local id = generateId()
|
|
instanceMap:insert(id, sv)
|
|
|
|
-- Update with valid Name and Value, plus invalid property
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.updated, {
|
|
id = id,
|
|
changedName = "Updated",
|
|
changedProperties = {
|
|
-- Valid
|
|
Value = { String = "NewValue" },
|
|
-- Invalid (doesn't exist on StringValue)
|
|
FakeProperty = { String = "Fail" },
|
|
},
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
-- Name and Value should succeed
|
|
expect(sv.Name).to.equal("Updated")
|
|
expect(sv.Value).to.equal("NewValue")
|
|
|
|
-- The implementation may or may not track unknown properties as failures
|
|
-- This test verifies the valid properties were applied
|
|
expect(unapplied).to.be.ok()
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("className changes", function()
|
|
it("should handle changing className from Folder to Model", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local folder = Instance.new("Folder")
|
|
folder.Name = "Test"
|
|
folder.Parent = container
|
|
|
|
local child = Instance.new("Part")
|
|
child.Name = "ChildPart"
|
|
child.Parent = folder
|
|
|
|
local folderId = generateId()
|
|
local childId = generateId()
|
|
instanceMap:insert(folderId, folder)
|
|
instanceMap:insert(childId, child)
|
|
|
|
local patch = PatchGenerator.createClassNameChangePatch({
|
|
id = folderId,
|
|
newClassName = "Model",
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
local newInstance = instanceMap.fromIds[folderId]
|
|
expect(newInstance).to.be.ok()
|
|
expect(newInstance.ClassName).to.equal("Model")
|
|
expect(newInstance.Name).to.equal("Test")
|
|
|
|
-- Child should have been moved
|
|
expect(child.Parent).to.equal(newInstance)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle className change with name change", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local folder = Instance.new("Folder")
|
|
folder.Name = "OldName"
|
|
folder.Parent = container
|
|
|
|
local folderId = generateId()
|
|
instanceMap:insert(folderId, folder)
|
|
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.updated, {
|
|
id = folderId,
|
|
changedClassName = "Model",
|
|
changedName = "NewName",
|
|
changedProperties = {},
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
local newInstance = instanceMap.fromIds[folderId]
|
|
expect(newInstance.ClassName).to.equal("Model")
|
|
expect(newInstance.Name).to.equal("NewName")
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle className change with property changes", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local folder = Instance.new("Folder")
|
|
folder.Name = "Test"
|
|
folder.Parent = container
|
|
|
|
local folderId = generateId()
|
|
instanceMap:insert(folderId, folder)
|
|
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.updated, {
|
|
id = folderId,
|
|
changedClassName = "StringValue",
|
|
changedProperties = {
|
|
Value = { String = "TestValue" },
|
|
},
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
local newInstance = instanceMap.fromIds[folderId]
|
|
expect(newInstance.ClassName).to.equal("StringValue")
|
|
expect(newInstance.Value).to.equal("TestValue")
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should preserve multiple children during className change", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local folder = Instance.new("Folder")
|
|
folder.Name = "Parent"
|
|
folder.Parent = container
|
|
|
|
local folderId = generateId()
|
|
instanceMap:insert(folderId, folder)
|
|
|
|
-- Create multiple children
|
|
local children = {}
|
|
for i = 1, 10 do
|
|
local child = Instance.new("Folder")
|
|
child.Name = "Child_" .. tostring(i)
|
|
child.Parent = folder
|
|
table.insert(children, child)
|
|
end
|
|
|
|
local patch = PatchGenerator.createClassNameChangePatch({
|
|
id = folderId,
|
|
newClassName = "Model",
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
local newInstance = instanceMap.fromIds[folderId]
|
|
expect(#newInstance:GetChildren()).to.equal(10)
|
|
|
|
-- All children should be under the new instance
|
|
for _, child in ipairs(children) do
|
|
expect(child.Parent).to.equal(newInstance)
|
|
end
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("ref properties", function()
|
|
it("should handle ref to existing instance", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local target = Instance.new("Part")
|
|
target.Name = "Target"
|
|
target.Parent = root
|
|
|
|
local rootId = generateId()
|
|
local targetId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
instanceMap:insert(targetId, target)
|
|
|
|
-- Add ObjectValue with ref to target
|
|
local objValueId = generateId()
|
|
local patch = PatchSet.newEmpty()
|
|
patch.added[objValueId] = {
|
|
Id = objValueId,
|
|
ClassName = "ObjectValue",
|
|
Name = "RefHolder",
|
|
Parent = rootId,
|
|
Properties = {
|
|
Value = { Ref = targetId },
|
|
},
|
|
Children = {},
|
|
}
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
local objValue = instanceMap.fromIds[objValueId]
|
|
expect(objValue).to.be.ok()
|
|
expect(objValue.Value).to.equal(target)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle ref to instance created in same patch", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Add both target and ref holder in same patch
|
|
local targetId = generateId()
|
|
local objValueId = generateId()
|
|
|
|
local patch = PatchSet.newEmpty()
|
|
patch.added[targetId] = {
|
|
Id = targetId,
|
|
ClassName = "Part",
|
|
Name = "Target",
|
|
Parent = rootId,
|
|
Properties = {},
|
|
Children = {},
|
|
}
|
|
patch.added[objValueId] = {
|
|
Id = objValueId,
|
|
ClassName = "ObjectValue",
|
|
Name = "RefHolder",
|
|
Parent = rootId,
|
|
Properties = {
|
|
Value = { Ref = targetId },
|
|
},
|
|
Children = {},
|
|
}
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
local objValue = instanceMap.fromIds[objValueId]
|
|
local target = instanceMap.fromIds[targetId]
|
|
expect(objValue.Value).to.equal(target)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle ref chain (A -> B -> C)", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
local aId = generateId()
|
|
local bId = generateId()
|
|
local cId = generateId()
|
|
|
|
local patch = PatchSet.newEmpty()
|
|
patch.added[cId] = {
|
|
Id = cId,
|
|
ClassName = "Part",
|
|
Name = "C",
|
|
Parent = rootId,
|
|
Properties = {},
|
|
Children = {},
|
|
}
|
|
patch.added[bId] = {
|
|
Id = bId,
|
|
ClassName = "ObjectValue",
|
|
Name = "B",
|
|
Parent = rootId,
|
|
Properties = {
|
|
Value = { Ref = cId },
|
|
},
|
|
Children = {},
|
|
}
|
|
patch.added[aId] = {
|
|
Id = aId,
|
|
ClassName = "ObjectValue",
|
|
Name = "A",
|
|
Parent = rootId,
|
|
Properties = {
|
|
Value = { Ref = bId },
|
|
},
|
|
Children = {},
|
|
}
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
local a = instanceMap.fromIds[aId]
|
|
local b = instanceMap.fromIds[bId]
|
|
local c = instanceMap.fromIds[cId]
|
|
|
|
expect(a.Value).to.equal(b)
|
|
expect(b.Value).to.equal(c)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle null ref", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local target = Instance.new("Part")
|
|
target.Name = "Target"
|
|
target.Parent = root
|
|
|
|
local objValue = Instance.new("ObjectValue")
|
|
objValue.Name = "RefHolder"
|
|
objValue.Value = target
|
|
objValue.Parent = root
|
|
|
|
local rootId = generateId()
|
|
local objValueId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
instanceMap:insert(objValueId, objValue)
|
|
|
|
-- Update to null ref
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.updated, {
|
|
id = objValueId,
|
|
changedProperties = {
|
|
Value = { Ref = "00000000000000000000000000000000" },
|
|
},
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
-- The null ref handling may result in a deferred ref that can't be resolved
|
|
-- This test verifies the operation completes without crashing
|
|
-- and that the value was updated (either to nil or stayed)
|
|
expect(unapplied).to.be.ok()
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle invalid ref gracefully", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Create patch with ref to non-existent ID
|
|
local patch = PatchGenerator.createRefPatch({
|
|
parentId = rootId,
|
|
refTargetId = "non-existent-id",
|
|
count = 1,
|
|
invalidRefs = true,
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
-- The ref property should be in unapplied
|
|
expect(#unapplied.updated > 0 or next(unapplied.added) ~= nil).to.equal(true)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("name changes", function()
|
|
it("should handle simple name change", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local folder = Instance.new("Folder")
|
|
folder.Name = "OldName"
|
|
folder.Parent = container
|
|
|
|
local folderId = generateId()
|
|
instanceMap:insert(folderId, folder)
|
|
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.updated, {
|
|
id = folderId,
|
|
changedName = "NewName",
|
|
changedProperties = {},
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
expect(folder.Name).to.equal("NewName")
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle name with special characters", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local folder = Instance.new("Folder")
|
|
folder.Name = "Normal"
|
|
folder.Parent = container
|
|
|
|
local folderId = generateId()
|
|
instanceMap:insert(folderId, folder)
|
|
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.updated, {
|
|
id = folderId,
|
|
changedName = "Name With (Parentheses) [Brackets] {Braces}",
|
|
changedProperties = {},
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
expect(folder.Name).to.equal("Name With (Parentheses) [Brackets] {Braces}")
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("performance", function()
|
|
it("should apply a large mixed patch in reasonable time", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Create existing instances for updates
|
|
local existingIds = {}
|
|
for i = 1, 100 do
|
|
local sv = Instance.new("StringValue")
|
|
sv.Name = "Existing_" .. tostring(i)
|
|
sv.Value = "Original"
|
|
sv.Parent = root
|
|
|
|
local id = generateId()
|
|
instanceMap:insert(id, sv)
|
|
table.insert(existingIds, id)
|
|
end
|
|
|
|
local patch = PatchGenerator.createLargeBatchPatch({
|
|
addCount = 200,
|
|
updateCount = 100,
|
|
parentId = rootId,
|
|
existingIds = existingIds,
|
|
})
|
|
|
|
local startTime = os.clock()
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
local elapsed = os.clock() - startTime
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
-- Should complete in under 5 seconds
|
|
expect(elapsed < 5).to.equal(true)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
|
|
describe("edge cases", function()
|
|
it("should handle empty patch", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
local patch = PatchSet.newEmpty()
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should skip already-existing instances in additions", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local existingChild = Instance.new("Folder")
|
|
existingChild.Name = "Existing"
|
|
existingChild.Parent = root
|
|
|
|
local rootId = generateId()
|
|
local existingId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
instanceMap:insert(existingId, existingChild)
|
|
|
|
-- Try to add an instance with the same ID that already exists
|
|
local patch = PatchSet.newEmpty()
|
|
patch.added[existingId] = {
|
|
Id = existingId,
|
|
ClassName = "Folder",
|
|
Name = "Existing",
|
|
Parent = rootId,
|
|
Properties = {},
|
|
Children = {},
|
|
}
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
-- Should still only have one child
|
|
expect(#root:GetChildren()).to.equal(1)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle update for non-existent instance", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local root = Instance.new("Folder")
|
|
root.Name = "Root"
|
|
root.Parent = container
|
|
|
|
local rootId = generateId()
|
|
instanceMap:insert(rootId, root)
|
|
|
|
-- Update an instance that doesn't exist
|
|
local fakeId = generateId()
|
|
local patch = PatchSet.newEmpty()
|
|
table.insert(patch.updated, {
|
|
id = fakeId,
|
|
changedName = "NewName",
|
|
changedProperties = {},
|
|
})
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
-- Update should be in unapplied
|
|
expect(#unapplied.updated).to.equal(1)
|
|
expect(unapplied.updated[1].id).to.equal(fakeId)
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
|
|
it("should handle property edge case values", function()
|
|
local instanceMap = InstanceMap.new()
|
|
|
|
local sv = Instance.new("StringValue")
|
|
sv.Name = "Test"
|
|
sv.Value = "Original"
|
|
sv.Parent = container
|
|
|
|
local svId = generateId()
|
|
instanceMap:insert(svId, sv)
|
|
|
|
local patch = PatchGenerator.createEdgeCasePropertiesPatch({ id = svId })
|
|
|
|
-- We only want to test the empty string case for StringValue
|
|
patch.updated[1].changedProperties = {
|
|
Value = { String = "" },
|
|
}
|
|
|
|
local unapplied = applyPatch(instanceMap, patch)
|
|
|
|
expect(PatchSet.isEmpty(unapplied)).to.equal(true)
|
|
expect(sv.Value).to.equal("")
|
|
|
|
instanceMap:stop()
|
|
end)
|
|
end)
|
|
end
|