mirror of
https://github.com/littensy/charm.git
synced 2026-10-20 17:51:37 +00:00
test: simplify gc testing
This commit is contained in:
parent
8ca299f671
commit
0c68ff760f
6 changed files with 111 additions and 49 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
local atom = require(script.Parent.Parent.atom)
|
local atom = require(script.Parent.Parent.atom)
|
||||||
|
local derive = require(script.Parent.Parent.derive)
|
||||||
local effect = require(script.Parent.Parent.effect)
|
local effect = require(script.Parent.Parent.effect)
|
||||||
local store = require(script.Parent.Parent.store)
|
local store = require(script.Parent.Parent.store)
|
||||||
local subscribe = require(script.Parent.Parent.subscribe)
|
local subscribe = require(script.Parent.Parent.subscribe)
|
||||||
|
|
@ -35,4 +36,26 @@ return function()
|
||||||
end)
|
end)
|
||||||
expect(value).to.equal(1)
|
expect(value).to.equal(1)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("batches derived atoms", function()
|
||||||
|
local source = atom(1)
|
||||||
|
local updates = 0
|
||||||
|
local computed = 0
|
||||||
|
local double = derive(function()
|
||||||
|
computed += 1
|
||||||
|
return source() * 2
|
||||||
|
end)
|
||||||
|
computed = 0 -- derived atoms will run once initially
|
||||||
|
subscribe(double, function()
|
||||||
|
updates += 1
|
||||||
|
end)
|
||||||
|
store.batch(function()
|
||||||
|
source(2)
|
||||||
|
source(3)
|
||||||
|
source(4)
|
||||||
|
end)
|
||||||
|
expect(updates).to.equal(1)
|
||||||
|
expect(computed).to.equal(1)
|
||||||
|
expect(double()).to.equal(8)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ local Package = game:GetService("ReplicatedStorage").Packages.charm
|
||||||
|
|
||||||
local atom = require(Package.atom)
|
local atom = require(Package.atom)
|
||||||
local derive = require(Package.derive)
|
local derive = require(Package.derive)
|
||||||
|
local effect = require(Package.effect)
|
||||||
|
local subscribe = require(Package.subscribe)
|
||||||
|
|
||||||
local function parameterGenerator()
|
local function parameterGenerator()
|
||||||
return atom({ count = 0 })
|
return atom({ count = 0 })
|
||||||
|
|
@ -26,14 +28,13 @@ local benchmarks = {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
derived_memo = function(_, source: any)
|
derived_nested = function(_, source: any)
|
||||||
derive(function()
|
local double = derive(function()
|
||||||
return { count = 2 * source().count }
|
return { count = 2 * source().count }
|
||||||
end, {
|
end)
|
||||||
equals = function(previous, current)
|
derive(function()
|
||||||
return previous.count == current.count
|
return { count = 2 * double().count }
|
||||||
end,
|
end)
|
||||||
})
|
|
||||||
for _ = 1, 500 do
|
for _ = 1, 500 do
|
||||||
source({ count = math.random(1, 2) })
|
source({ count = math.random(1, 2) })
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
local atom = require(script.Parent.Parent.atom)
|
local atom = require(script.Parent.Parent.atom)
|
||||||
local derive = require(script.Parent.Parent.derive)
|
local derive = require(script.Parent.Parent.derive)
|
||||||
local store = require(script.Parent.Parent.store)
|
local store = require(script.Parent.Parent.store)
|
||||||
|
local collect = require(script.Parent.Parent.utils.collect)
|
||||||
|
local count = require(script.Parent.Parent.utils.count)
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
it("memoizes the result", function()
|
it("memoizes the result", function()
|
||||||
|
|
@ -35,34 +37,34 @@ return function()
|
||||||
expect(quadruple()).to.equal(8)
|
expect(quadruple()).to.equal(8)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("garbage collects", function()
|
it("collects derived atoms", function()
|
||||||
-- large values coerce the garbage collector to run
|
local source = atom(0)
|
||||||
local source = atom(string.rep("x", 1e5))
|
|
||||||
local max = 0
|
|
||||||
|
|
||||||
local function listenersDecreased()
|
derive(function()
|
||||||
local count = 0
|
return source()
|
||||||
for _ in next, store.listeners[source] do
|
end)
|
||||||
count += 1
|
|
||||||
end
|
|
||||||
max = math.max(max, count)
|
|
||||||
return count < max
|
|
||||||
end
|
|
||||||
|
|
||||||
for index = 1, 500 do
|
collect()
|
||||||
derive(function()
|
|
||||||
return string.upper(source())
|
assert(count(store.listeners[source]) == 0, "derived atom did not gc")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("collects nested derived atoms", function()
|
||||||
|
local source = atom(0)
|
||||||
|
|
||||||
|
do
|
||||||
|
-- Declare this in another scope to ensure no references are kept
|
||||||
|
local derived = derive(function()
|
||||||
|
return source()
|
||||||
|
end)
|
||||||
|
|
||||||
|
derive(function()
|
||||||
|
return derived() * 2
|
||||||
end)
|
end)
|
||||||
if index % 50 == 0 then
|
|
||||||
-- derived atoms should be collected when memory is stressed
|
|
||||||
source(source() .. "x")
|
|
||||||
task.wait()
|
|
||||||
end
|
|
||||||
if listenersDecreased() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
error("None of the derived atoms were collected")
|
collect()
|
||||||
|
|
||||||
|
assert(count(store.listeners[source]) == 0, "nested derived atom did not gc")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
local atom = require(script.Parent.Parent.atom)
|
local atom = require(script.Parent.Parent.atom)
|
||||||
local derive = require(script.Parent.Parent.derive)
|
local derive = require(script.Parent.Parent.derive)
|
||||||
|
local store = require(script.Parent.Parent.store)
|
||||||
local subscribe = require(script.Parent.Parent.subscribe)
|
local subscribe = require(script.Parent.Parent.subscribe)
|
||||||
|
local collect = require(script.Parent.Parent.utils.collect)
|
||||||
|
local count = require(script.Parent.Parent.utils.count)
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
it("runs on state change", function()
|
it("runs on state change", function()
|
||||||
|
|
@ -27,25 +30,41 @@ return function()
|
||||||
local source = atom(1)
|
local source = atom(1)
|
||||||
local updates = 0
|
local updates = 0
|
||||||
|
|
||||||
for _ = 1, 1000 do
|
-- Create a new scope to encapsulate variables with a limited lifetime, like
|
||||||
-- The idea here is to test whether subscribe() prevents every derived
|
-- 'unsubscribe' and 'derived', to ensure they are garbage collected.
|
||||||
-- atom in a chain from being garbage collected. If any are GCed, then
|
--
|
||||||
-- the subscription will stop updating and the test will fail.
|
-- The idea here is to test whether subscribe() prevents every derived
|
||||||
local derived = derive(function()
|
-- atom in a chain from being garbage collected. If any are GCed, then
|
||||||
return source()
|
-- the subscription will stop updating and the test will fail.
|
||||||
end)
|
do
|
||||||
subscribe(
|
local unsubscribe
|
||||||
derive(function()
|
|
||||||
return derived() * 2
|
do
|
||||||
end),
|
local derived = derive(function()
|
||||||
function()
|
return source()
|
||||||
updates += 1
|
end)
|
||||||
end
|
|
||||||
)
|
unsubscribe = subscribe(
|
||||||
|
derive(function()
|
||||||
|
return derived() * 2
|
||||||
|
end),
|
||||||
|
function()
|
||||||
|
updates += 1
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
collect()
|
||||||
|
source(source() + 1)
|
||||||
|
|
||||||
|
assert(updates == 1, "subscription stopped updating")
|
||||||
|
assert(count(store.listeners[source]) == 1, "derived atom was garbage collected")
|
||||||
|
|
||||||
|
unsubscribe()
|
||||||
end
|
end
|
||||||
|
|
||||||
source(source() + 1)
|
collect()
|
||||||
task.wait()
|
|
||||||
expect(updates).to.equal(1000)
|
assert(count(store.listeners[source]) == 0, "derived atom did not collect")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
8
src/utils/collect.luau
Normal file
8
src/utils/collect.luau
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
local function collect()
|
||||||
|
for _ = 1, 1e4 do
|
||||||
|
local _ = table.create(1e3)
|
||||||
|
end
|
||||||
|
task.wait()
|
||||||
|
end
|
||||||
|
|
||||||
|
return collect
|
||||||
9
src/utils/count.luau
Normal file
9
src/utils/count.luau
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
local function count(object: any): number
|
||||||
|
local count = 0
|
||||||
|
for _ in next, object do
|
||||||
|
count += 1
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
|
return count
|
||||||
Loading…
Add table
Add a link
Reference in a new issue