mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Refactor
This commit is contained in:
parent
2050c2585f
commit
cc10c80a90
10 changed files with 174 additions and 220 deletions
|
|
@ -15,10 +15,10 @@ vide.strict = true
|
||||||
|
|
||||||
What strict mode will do:
|
What strict mode will do:
|
||||||
|
|
||||||
1. Run derived callbacks twice when re-evaluating.
|
1. Run derived sources twice a source updates.
|
||||||
2. Run watcher callbacks twice when a state changes.
|
2. Run watchers twice when a source updates.
|
||||||
3. Throw an error if yields occur where they are not allowed.
|
3. Throw an error if yields occur where they are not allowed.
|
||||||
4. Checks for `map()` returning primitive values.
|
4. Checks for `indexes()` and `values()` returning primitive values.
|
||||||
5. Better error reporting and stack traces.
|
5. Better error reporting and stack traces.
|
||||||
|
|
||||||
It is recommend to develop UI with strict mode and to disable it when pushing to
|
It is recommend to develop UI with strict mode and to disable it when pushing to
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ function setup(instance: Instance, setter: (Instance) -> ())
|
||||||
local trace = traceback()
|
local trace = traceback()
|
||||||
setter = function(instance)
|
setter = function(instance)
|
||||||
local ok, err: string? = pcall(fn, instance)
|
local ok, err: string? = pcall(fn, instance)
|
||||||
if not ok then warn(`error occured updating state binding:\n{err}\nset from:{trace}`) end
|
if not ok then warn(`error occured updating property:\n{err}\nset from:{trace}`) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -68,8 +68,6 @@ function setup(instance: Instance, setter: (Instance) -> ())
|
||||||
instance:GetPropertyChangedSignal("Parent"):Connect(ref)
|
instance:GetPropertyChangedSignal("Parent"):Connect(ref)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- todo: move `fn` as arg?
|
|
||||||
|
|
||||||
local function bind_property(instance: Instance, property: string, fn: () -> unknown)
|
local function bind_property(instance: Instance, property: string, fn: () -> unknown)
|
||||||
setup(instance, function(instance_weak: any)
|
setup(instance, function(instance_weak: any)
|
||||||
instance_weak[property] = fn()
|
instance_weak[property] = fn()
|
||||||
|
|
@ -80,13 +78,13 @@ local function bind_parent(instance: Instance, fn: () -> Instance?)
|
||||||
instance.Destroying:Connect(function()
|
instance.Destroying:Connect(function()
|
||||||
instance= nil :: any -- allow gc when destroyed
|
instance= nil :: any -- allow gc when destroyed
|
||||||
end)
|
end)
|
||||||
|
|
||||||
setup(instance, function(instance)
|
setup(instance, function(instance)
|
||||||
local _ = instance -- state will strongly reference instance when parent is bound
|
local _ = instance -- state will strongly reference instance when parent is bound
|
||||||
instance.Parent = fn()
|
instance.Parent = fn()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- todo: could optimize, see: maps.luau values()
|
|
||||||
local function bind_children(parent: Instance, fn: () -> { Instance })
|
local function bind_children(parent: Instance, fn: () -> { Instance })
|
||||||
local current_child_set: { [Instance]: true } = {} -- cache of all children parented before update
|
local current_child_set: { [Instance]: true } = {} -- cache of all children parented before update
|
||||||
local new_child_set: { [Instance]: true } = {} -- cache of all children parented after update
|
local new_child_set: { [Instance]: true } = {} -- cache of all children parented after update
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ local defaults = require(script.Parent.defaults)
|
||||||
local apply = require(script.Parent.apply)
|
local apply = require(script.Parent.apply)
|
||||||
local memoize = require(script.Parent.memoize)
|
local memoize = require(script.Parent.memoize)
|
||||||
|
|
||||||
local function createInstance(className: string)
|
local function create_instance(className: string)
|
||||||
local success, instance: Instance = pcall(Instance.new, className :: any)
|
local success, instance: Instance = pcall(Instance.new, className :: any)
|
||||||
if success == false then throw(`invalid class name, could not create instance of class { className }`) end
|
if success == false then throw(`invalid class name, could not create instance of class { className }`) end
|
||||||
|
|
||||||
|
|
@ -26,9 +26,9 @@ local function createInstance(className: string)
|
||||||
return function(properties: { [any]: unknown }): Instance
|
return function(properties: { [any]: unknown }): Instance
|
||||||
return apply(instance:Clone(), properties)
|
return apply(instance:Clone(), properties)
|
||||||
end
|
end
|
||||||
end; createInstance = memoize(createInstance)
|
end; create_instance = memoize(create_instance)
|
||||||
|
|
||||||
local function cloneInstance(instance: Instance)
|
local function clone_instance(instance: Instance)
|
||||||
return function(properties: { [any]: unknown }): Instance
|
return function(properties: { [any]: unknown }): Instance
|
||||||
local clone = instance:Clone()
|
local clone = instance:Clone()
|
||||||
if not clone then error("Attempt to clone a non-archivable instance", 3) end
|
if not clone then error("Attempt to clone a non-archivable instance", 3) end
|
||||||
|
|
@ -38,9 +38,9 @@ end
|
||||||
|
|
||||||
local function create(classNameOrInstance: string|Instance)
|
local function create(classNameOrInstance: string|Instance)
|
||||||
if type(classNameOrInstance) == "string" then
|
if type(classNameOrInstance) == "string" then
|
||||||
return createInstance(classNameOrInstance)
|
return create_instance(classNameOrInstance)
|
||||||
elseif typeof(classNameOrInstance) == "Instance" then
|
elseif typeof(classNameOrInstance) == "Instance" then
|
||||||
return cloneInstance(classNameOrInstance)
|
return clone_instance(classNameOrInstance)
|
||||||
else
|
else
|
||||||
error("Bad argument #1, expected string or instance, got "..typeof(classNameOrInstance), 2)
|
error("Bad argument #1, expected string or instance, got "..typeof(classNameOrInstance), 2)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
if not game then script = require "test/wrap-require" end
|
if not game then script = require "test/wrap-require" end
|
||||||
|
|
||||||
|
local throw = require(script.Parent.throw)
|
||||||
local flags = require(script.Parent.flags)
|
local flags = require(script.Parent.flags)
|
||||||
|
|
||||||
export type Node<T> = {
|
export type Node<T> = {
|
||||||
|
|
@ -13,28 +14,30 @@ local reff = false
|
||||||
local refs = {} :: { Node<unknown> }
|
local refs = {} :: { Node<unknown> }
|
||||||
|
|
||||||
local WEAK_VALUES_RESIZABLE = { __mode = "vs" }
|
local WEAK_VALUES_RESIZABLE = { __mode = "vs" }
|
||||||
local EVALUATION_ERR = "error while evaluating node:\n\n"
|
local EVALUATION_ERR = "error while evaluating source:\n\n"
|
||||||
|
|
||||||
setmetatable(refs :: any, WEAK_VALUES_RESIZABLE)
|
setmetatable(refs :: any, WEAK_VALUES_RESIZABLE)
|
||||||
|
|
||||||
local check_for_yield do
|
local check_for_yield: <T...>(fn: (T...) -> unknown, T...) -> () do
|
||||||
local t = { __mode = "kv" }
|
local t = { __mode = "kv" }
|
||||||
setmetatable(t, t)
|
setmetatable(t, t)
|
||||||
|
|
||||||
check_for_yield = function<T..., U...>(fn: (T...) -> (), ...: any)
|
check_for_yield = function(fn, ...: any)
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
t.__unm = function()
|
|
||||||
|
t.__unm = function(_)
|
||||||
fn(unpack(args))
|
fn(unpack(args))
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = pcall(function()
|
local ok, err = pcall(function()
|
||||||
return -t :: any
|
local _ = -t
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
if err == "attempt to yield across metamethod/C-call boundary" or err == "thread is not yieldable" then
|
if err == "attempt to yield across metamethod/C-call boundary" or err == "thread is not yieldable" then
|
||||||
error(EVALUATION_ERR .. "cannot yield when deriving node in watcher", 3)
|
throw(EVALUATION_ERR .. "cannot yield when deriving node in watcher")
|
||||||
else
|
else
|
||||||
error(EVALUATION_ERR..err, 3)
|
throw(EVALUATION_ERR .. err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -45,10 +48,16 @@ local function set_effect<T>(node: Node<unknown>, fn: (T) -> (), key: T)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function run_effects(node: Node<unknown>)
|
local function run_effects(node: Node<unknown>)
|
||||||
for effect, key in next, node.effects do
|
if flags.strict then
|
||||||
if flags.strict then effect(key) end
|
for effect, key in next, node.effects do
|
||||||
effect(key)
|
effect(key)
|
||||||
end
|
effect(key)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for effect, key in next, node.effects do
|
||||||
|
effect(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- retrieves a node's cached value
|
-- retrieves a node's cached value
|
||||||
|
|
@ -71,8 +80,10 @@ end
|
||||||
local function update(node: Node<unknown>)
|
local function update(node: Node<unknown>)
|
||||||
run_effects(node)
|
run_effects(node)
|
||||||
if node.children then
|
if node.children then
|
||||||
|
local strict = flags.strict
|
||||||
|
|
||||||
for _, child in node.children do
|
for _, child in node.children do
|
||||||
if flags.strict then check_for_yield(child.derive) end
|
if strict then check_for_yield(child.derive) end
|
||||||
child.cache = child.derive()
|
child.cache = child.derive()
|
||||||
update(child)
|
update(child)
|
||||||
end
|
end
|
||||||
|
|
@ -108,7 +119,7 @@ local function capture<T, U>(fn: (U?) -> T, arg: U?): ({ Node<unknown> }, T)
|
||||||
|
|
||||||
reff = false
|
reff = false
|
||||||
|
|
||||||
if not ok then error("error while detecting watcher: " .. result :: string, 0) end
|
if not ok then throw(EVALUATION_ERR .. result :: string) end
|
||||||
|
|
||||||
return refs, result :: T
|
return refs, result :: T
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ local indexes, values = require(script.maps)()
|
||||||
local spring, update_springs = require(script.spring)()
|
local spring, update_springs = require(script.spring)()
|
||||||
local action = require(script.action)()
|
local action = require(script.action)()
|
||||||
|
|
||||||
|
local throw = require(script.throw)
|
||||||
local flags = require(script.flags)
|
local flags = require(script.flags)
|
||||||
|
|
||||||
local vide = {
|
local vide = {
|
||||||
|
|
@ -49,15 +50,20 @@ local vide = {
|
||||||
}
|
}
|
||||||
|
|
||||||
setmetatable(vide :: any, {
|
setmetatable(vide :: any, {
|
||||||
__index = function(_, index: unknown)
|
__index = function(_, index: unknown): ()
|
||||||
error(string.format("\"%s\" is not a valid member of vide", tostring(index)), 2)
|
if index == "strict" then
|
||||||
|
return flags.strict
|
||||||
|
else
|
||||||
|
throw(`{tostring(index)} is not a valid member of vide`)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__newindex = function(_, index: unknown, value: unknown)
|
__newindex = function(_, index: unknown, value: unknown)
|
||||||
if index == "strict" then
|
if index == "strict" then
|
||||||
flags.strict = if type(value) == "boolean" then value else error("strict must be a boolean", 2)
|
if value ~= true then throw "strict mode can only be set to true" end
|
||||||
|
flags.strict = true
|
||||||
else
|
else
|
||||||
error(string.format("\"%s\" is not a valid member of vide", tostring(index)), 2)
|
throw(`{tostring(index)} is not a valid member of vide`)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
if not game then script = require "test/wrap-require" end
|
if not game then script = require "test/wrap-require" end
|
||||||
|
|
||||||
|
local throw = require(script.Parent.throw)
|
||||||
|
local flags = require(script.Parent.flags)
|
||||||
local graph = require(script.Parent.graph)
|
local graph = require(script.Parent.graph)
|
||||||
type Node<T> = graph.Node<T>
|
type Node<T> = graph.Node<T>
|
||||||
local create = graph.create
|
local create = graph.create
|
||||||
|
|
@ -9,6 +11,15 @@ local link = graph.link
|
||||||
|
|
||||||
type Map<K, V> = { [K]: V }
|
type Map<K, V> = { [K]: V }
|
||||||
|
|
||||||
|
local function check_primitives(t: {})
|
||||||
|
if not flags.strict then return end
|
||||||
|
|
||||||
|
for _, v in next, t do
|
||||||
|
if type(v) == "table" or type(v) == "userdata" then continue end
|
||||||
|
throw("table source map cannot return primitives")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- todo: optimize output array
|
-- todo: optimize output array
|
||||||
local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI, K) -> VO): () -> { VO }
|
local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI, K) -> VO): () -> { VO }
|
||||||
local input_cache = {} :: Map<K, VI>
|
local input_cache = {} :: Map<K, VI>
|
||||||
|
|
@ -54,7 +65,8 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
||||||
for _, v in next, output_cache do
|
for _, v in next, output_cache do
|
||||||
table.insert(output_array, v)
|
table.insert(output_array, v)
|
||||||
end
|
end
|
||||||
|
check_primitives(output_array)
|
||||||
|
|
||||||
return output_array
|
return output_array
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -86,6 +98,16 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
||||||
|
|
||||||
local function recompute(data: Map<K, VI>)
|
local function recompute(data: Map<K, VI>)
|
||||||
local cur_input_cache, new_input_cache = cur_input_cache_up, new_input_cache_up
|
local cur_input_cache, new_input_cache = cur_input_cache_up, new_input_cache_up
|
||||||
|
|
||||||
|
if flags.strict then
|
||||||
|
local cache = {}
|
||||||
|
for _, v in next, data do
|
||||||
|
if cache[v] ~= nil then
|
||||||
|
throw "duplicate table value detected"
|
||||||
|
end
|
||||||
|
cache[v] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- process data
|
-- process data
|
||||||
for i, v in next, data do
|
for i, v in next, data do
|
||||||
|
|
@ -136,6 +158,7 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
||||||
for _, node in next, nodes do
|
for _, node in next, nodes do
|
||||||
link(node, output, derive)
|
link(node, output, derive)
|
||||||
end
|
end
|
||||||
|
check_primitives(output_array)
|
||||||
|
|
||||||
output.cache = recompute(value)
|
output.cache = recompute(value)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ local function update_springs(dt: number)
|
||||||
typeof(initial_position),
|
typeof(initial_position),
|
||||||
target_type
|
target_type
|
||||||
))
|
))
|
||||||
throw(`Cannot tween type { typeof(initial_position) } and { target_type }`)
|
throw(`cannot tween type { typeof(initial_position) } and { target_type }`)
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -165,14 +165,13 @@ local function update_springs(dt: number)
|
||||||
|
|
||||||
if lerp == nil then
|
if lerp == nil then
|
||||||
springs[data] = nil
|
springs[data] = nil
|
||||||
throw(`Cannot animate type { target_type }`)
|
throw(`cannot animate type { target_type }`)
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
local new_time = data.duration + dt
|
local new_time = data.duration + dt
|
||||||
local new_alpha = solve(data.period, data.damping_ratio, data.initial_velocity, new_time)
|
local new_alpha = solve(data.period, data.damping_ratio, data.initial_velocity, new_time)
|
||||||
local new_velocity = -(new_alpha - data.alpha)/dt
|
local new_velocity = -(new_alpha - data.alpha)/dt
|
||||||
|
|
||||||
local acceleration = (new_velocity - data.velocity)/dt
|
local acceleration = (new_velocity - data.velocity)/dt
|
||||||
|
|
||||||
data.velocity = new_velocity
|
data.velocity = new_velocity
|
||||||
|
|
@ -181,7 +180,8 @@ local function update_springs(dt: number)
|
||||||
|
|
||||||
local value = lerp(initial_position, target_position, new_alpha)
|
local value = lerp(initial_position, target_position, new_alpha)
|
||||||
|
|
||||||
if math.abs(acceleration) < 0.01 then
|
if math.abs(acceleration) < (0.01 / data.period) then
|
||||||
|
-- close enough to target, remove and set value to target
|
||||||
table.insert(remove_queue, data)
|
table.insert(remove_queue, data)
|
||||||
set(output, target_position)
|
set(output, target_position)
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
------------------------------------------------------------------------------------------
|
|
||||||
-- benchmark.lua
|
|
||||||
------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local BENCH, START = require("test/testkit").benchmark()
|
local BENCH, START = require("test/testkit").benchmark()
|
||||||
|
|
||||||
local vide = require "src/init"
|
local vide = require "src/init"
|
||||||
|
|
||||||
local N = 2^18 -- 262144
|
local N = 2^18 -- 262144
|
||||||
|
|
||||||
BENCH("Create state", function()
|
BENCH("create state", function()
|
||||||
local cache = table.create(N)
|
local cache = table.create(N)
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
|
|
||||||
|
|
@ -17,7 +13,7 @@ BENCH("Create state", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Get value", function()
|
BENCH("get value", function()
|
||||||
local state = vide.source(1)
|
local state = vide.source(1)
|
||||||
|
|
||||||
for i = 1, START(N) do
|
for i = 1, START(N) do
|
||||||
|
|
@ -25,7 +21,7 @@ BENCH("Get value", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Set value", function()
|
BENCH("set value", function()
|
||||||
local state = vide.source(1)
|
local state = vide.source(1)
|
||||||
|
|
||||||
for i = 1, START(N) do
|
for i = 1, START(N) do
|
||||||
|
|
@ -33,7 +29,7 @@ BENCH("Set value", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Derive 1 state", function()
|
BENCH("derive 1 state", function()
|
||||||
local cache = table.create(N)
|
local cache = table.create(N)
|
||||||
local state = vide.source(1)
|
local state = vide.source(1)
|
||||||
local derive = vide.derive
|
local derive = vide.derive
|
||||||
|
|
@ -45,7 +41,7 @@ BENCH("Derive 1 state", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Derive 4 states", function()
|
BENCH("derive 4 states", function()
|
||||||
local cache = table.create(N)
|
local cache = table.create(N)
|
||||||
local state = vide.source(1)
|
local state = vide.source(1)
|
||||||
local state2 = vide.source(2)
|
local state2 = vide.source(2)
|
||||||
|
|
@ -60,7 +56,7 @@ BENCH("Derive 4 states", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Set derived value", function()
|
BENCH("set derived value", function()
|
||||||
local state = vide.source(1)
|
local state = vide.source(1)
|
||||||
local _derived = vide.derive(state)
|
local _derived = vide.derive(state)
|
||||||
|
|
||||||
|
|
@ -69,7 +65,7 @@ BENCH("Set derived value", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Apply 0 properties", function()
|
BENCH("apply 0 properties", function()
|
||||||
local apply = require "src/apply"
|
local apply = require "src/apply"
|
||||||
local instance = vide.create("Frame") {}
|
local instance = vide.create("Frame") {}
|
||||||
|
|
||||||
|
|
@ -78,7 +74,7 @@ BENCH("Apply 0 properties", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Apply 8 properties", function()
|
BENCH("apply 8 properties", function()
|
||||||
local apply = require "src/apply"
|
local apply = require "src/apply"
|
||||||
local instance = vide.create("Frame") {}
|
local instance = vide.create("Frame") {}
|
||||||
|
|
||||||
|
|
@ -96,7 +92,7 @@ BENCH("Apply 8 properties", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Bind state", function()
|
BENCH("bind source", function()
|
||||||
local apply = require "src/apply"
|
local apply = require "src/apply"
|
||||||
local instance = vide.create("Frame") {}
|
local instance = vide.create("Frame") {}
|
||||||
local state = vide.source(1)
|
local state = vide.source(1)
|
||||||
|
|
@ -108,7 +104,7 @@ BENCH("Bind state", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
BENCH("Update binding", function()
|
BENCH("update binding", function()
|
||||||
local apply = require "src/apply"
|
local apply = require "src/apply"
|
||||||
local instance = vide.create("Frame") {}
|
local instance = vide.create("Frame") {}
|
||||||
local state = vide.source(1)
|
local state = vide.source(1)
|
||||||
|
|
|
||||||
261
test/tests.luau
261
test/tests.luau
|
|
@ -1,13 +1,11 @@
|
||||||
local testkit = require("test/testkit")
|
local testkit = require("test/testkit")
|
||||||
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
local TEST, CASE, CHECK, FINISH = testkit.test()
|
||||||
|
|
||||||
local mock = require "test/mock"
|
local mock = require "test/mock"
|
||||||
|
|
||||||
local Instance, Signal = mock.Instance, mock.Signal
|
local Instance, Signal = mock.Instance, mock.Signal
|
||||||
|
|
||||||
local vide = require "src/init"
|
local vide = require "src/init"
|
||||||
|
|
||||||
-- force run garbage collector cycles
|
|
||||||
local function gc(n: number?)
|
local function gc(n: number?)
|
||||||
for i = 1, n or 3 do
|
for i = 1, n or 3 do
|
||||||
(collectgarbage :: any)("collect")
|
(collectgarbage :: any)("collect")
|
||||||
|
|
@ -29,12 +27,12 @@ TEST("graph", function()
|
||||||
local link = graph.link
|
local link = graph.link
|
||||||
local set_effect = graph.set_effect
|
local set_effect = graph.set_effect
|
||||||
|
|
||||||
do CASE "Node creation"
|
do CASE "node creation"
|
||||||
local node = create(1)
|
local node = create(1)
|
||||||
CHECK(get(node) == 1)
|
CHECK(get(node) == 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Node value"
|
do CASE "node value"
|
||||||
local node = create(0)
|
local node = create(0)
|
||||||
set(node, 1)
|
set(node, 1)
|
||||||
CHECK(get(node) == 1)
|
CHECK(get(node) == 1)
|
||||||
|
|
@ -42,7 +40,7 @@ TEST("graph", function()
|
||||||
CHECK(get(node) == 2)
|
CHECK(get(node) == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Capture nodes"
|
do CASE "capture nodes"
|
||||||
local node1 = create(nil)
|
local node1 = create(nil)
|
||||||
local node2 = create(nil)
|
local node2 = create(nil)
|
||||||
local nodes = capture(function()
|
local nodes = capture(function()
|
||||||
|
|
@ -52,7 +50,7 @@ TEST("graph", function()
|
||||||
CHECK(nodes[2] == node2)
|
CHECK(nodes[2] == node2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Linking nodes"
|
do CASE "linking nodes"
|
||||||
local parent = create(1)
|
local parent = create(1)
|
||||||
local child = create(0)
|
local child = create(0)
|
||||||
|
|
||||||
|
|
@ -64,7 +62,7 @@ TEST("graph", function()
|
||||||
CHECK(get(child) == 2) -- child should automatically update
|
CHECK(get(child) == 2) -- child should automatically update
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Capture and link nodes"
|
do CASE "capture and link nodes"
|
||||||
local parent = create(1)
|
local parent = create(1)
|
||||||
local child = create()
|
local child = create()
|
||||||
|
|
||||||
|
|
@ -76,38 +74,13 @@ TEST("graph", function()
|
||||||
CHECK(get(child) == "2")
|
CHECK(get(child) == "2")
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
do CASE "nodes garbage collection"
|
||||||
do CASE "Scoped captures"
|
|
||||||
local a = create(0)
|
|
||||||
local b = create(nil :: any)
|
|
||||||
|
|
||||||
local count = 0
|
|
||||||
|
|
||||||
b.cache = capture_and_link(b, function()
|
|
||||||
count += 1
|
|
||||||
local data = get(a)
|
|
||||||
local c = create(data)
|
|
||||||
get(c)
|
|
||||||
return c
|
|
||||||
end)
|
|
||||||
|
|
||||||
local c = get(b)
|
|
||||||
|
|
||||||
CHECK(count == 1)
|
|
||||||
set(c, 1)
|
|
||||||
CHECK(count == 1)
|
|
||||||
set(a, 1)
|
|
||||||
CHECK(count == 2)
|
|
||||||
end
|
|
||||||
]]
|
|
||||||
|
|
||||||
do CASE "Nodes garbage collection"
|
|
||||||
local wref = weak { create(1) }
|
local wref = weak { create(1) }
|
||||||
gc()
|
gc()
|
||||||
CHECK(not wref[1])
|
CHECK(not wref[1])
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Node effect garbage collection"
|
do CASE "node effect garbage collection"
|
||||||
do
|
do
|
||||||
local wref
|
local wref
|
||||||
|
|
||||||
|
|
@ -179,18 +152,18 @@ TEST("source()", function()
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local watch = vide.watch
|
local watch = vide.watch
|
||||||
|
|
||||||
do CASE "Create source"
|
do CASE "create source"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
CHECK(state() == 1)
|
CHECK(state() == 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Set and get source value"
|
do CASE "set and get source value"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
state(2)
|
state(2)
|
||||||
CHECK(state() == 2)
|
CHECK(state() == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Does not update if same value"
|
do CASE "does not update if same value"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
|
|
||||||
local updates = -1
|
local updates = -1
|
||||||
|
|
@ -206,7 +179,7 @@ TEST("source()", function()
|
||||||
CHECK(updates == 1)
|
CHECK(updates == 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Does update if same value is table"
|
do CASE "does update if same value is table"
|
||||||
local state = source {}
|
local state = source {}
|
||||||
|
|
||||||
local updates = -1
|
local updates = -1
|
||||||
|
|
@ -225,7 +198,7 @@ TEST("derive()", function()
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local derive = vide.derive
|
local derive = vide.derive
|
||||||
|
|
||||||
do CASE "Derive new value on source change"
|
do CASE "derive new value on source change"
|
||||||
local inputA = source(1)
|
local inputA = source(1)
|
||||||
local inputB = source(2)
|
local inputB = source(2)
|
||||||
|
|
||||||
|
|
@ -238,7 +211,7 @@ TEST("derive()", function()
|
||||||
CHECK(output() == "4")
|
CHECK(output() == "4")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Derive transformed source"
|
do CASE "derive wrapped source"
|
||||||
local input = source(1)
|
local input = source(1)
|
||||||
|
|
||||||
local transform = function()
|
local transform = function()
|
||||||
|
|
@ -254,25 +227,7 @@ TEST("derive()", function()
|
||||||
CHECK(output() == 2)
|
CHECK(output() == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
do CASE "garbage collection"
|
||||||
do CASE "Cleanup"
|
|
||||||
local count, set = source(1)
|
|
||||||
|
|
||||||
local derived = derive(function(from)
|
|
||||||
return { Value = from(count), Destroyed = false }
|
|
||||||
end, function(v)
|
|
||||||
v.Destroyed = true
|
|
||||||
end)
|
|
||||||
|
|
||||||
local first = derived()
|
|
||||||
CHECK(first.Destroyed == false)
|
|
||||||
set(2)
|
|
||||||
local _ = derived() -- trigger recalc
|
|
||||||
CHECK(first.Destroyed == true)
|
|
||||||
end
|
|
||||||
]]
|
|
||||||
|
|
||||||
do CASE "Garbage collection"
|
|
||||||
do -- check that `b` does not allow gc of `a`
|
do -- check that `b` does not allow gc of `a`
|
||||||
local wref, b
|
local wref, b
|
||||||
|
|
||||||
|
|
@ -309,7 +264,7 @@ TEST("derive()", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Garbage collection 2"
|
do CASE "garbage collection 2"
|
||||||
-- creats a chain `a -> b -> c` where `a` is the source
|
-- creats a chain `a -> b -> c` where `a` is the source
|
||||||
local function setup()
|
local function setup()
|
||||||
local a = source(0)
|
local a = source(0)
|
||||||
|
|
@ -362,7 +317,7 @@ TEST("watch()", function()
|
||||||
local watch = vide.watch
|
local watch = vide.watch
|
||||||
local cleanup = vide.cleanup
|
local cleanup = vide.cleanup
|
||||||
|
|
||||||
do CASE "Capture states"
|
do CASE "capture sourcess"
|
||||||
local a = source(1)
|
local a = source(1)
|
||||||
local b = source(1)
|
local b = source(1)
|
||||||
|
|
||||||
|
|
@ -380,7 +335,7 @@ TEST("watch()", function()
|
||||||
CHECK(runcount == 2)
|
CHECK(runcount == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Stop watch"
|
do CASE "stop watch"
|
||||||
local a = source(1)
|
local a = source(1)
|
||||||
|
|
||||||
local runcount = -1
|
local runcount = -1
|
||||||
|
|
@ -394,7 +349,7 @@ TEST("watch()", function()
|
||||||
CHECK(runcount == 0)
|
CHECK(runcount == 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Side-effect cleanup"
|
do CASE "side-effect cleanup"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
|
|
||||||
local effect_runcount = 0
|
local effect_runcount = 0
|
||||||
|
|
@ -421,7 +376,7 @@ TEST("watch()", function()
|
||||||
CHECK(cleanup_runcount == 2)
|
CHECK(cleanup_runcount == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Garbage collection"
|
do CASE "garbage collection"
|
||||||
local function factory(p)
|
local function factory(p)
|
||||||
return function()
|
return function()
|
||||||
p()
|
p()
|
||||||
|
|
@ -486,7 +441,7 @@ TEST("cleanup()", function()
|
||||||
local watch = vide.watch
|
local watch = vide.watch
|
||||||
local cleanup = vide.cleanup
|
local cleanup = vide.cleanup
|
||||||
|
|
||||||
do CASE "Cleanup runs for watcher"
|
do CASE "cleanup runs for watcher"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
|
|
||||||
local watched = 0
|
local watched = 0
|
||||||
|
|
@ -520,7 +475,7 @@ TEST("cleanup()", function()
|
||||||
CHECK(cleaned == 2)
|
CHECK(cleaned == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Scoped"
|
do CASE "scoped"
|
||||||
local function setup()
|
local function setup()
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
local obj = { cleaned = 0 }
|
local obj = { cleaned = 0 }
|
||||||
|
|
@ -562,7 +517,7 @@ TEST("cleanup()", function()
|
||||||
CHECK(objB.cleaned == 2)
|
CHECK(objB.cleaned == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Multiple cleanup"
|
do CASE "multiple cleanup"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
|
|
||||||
local queue = {}
|
local queue = {}
|
||||||
|
|
@ -595,14 +550,14 @@ TEST("create()", function()
|
||||||
local create = vide.create
|
local create = vide.create
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
|
|
||||||
do CASE "Apply default properties"
|
do CASE "apply default properties"
|
||||||
local defaults = require("src/defaults")
|
local defaults = require("src/defaults")
|
||||||
local frame = create "Frame" {} :: Instance & { BorderSizePixel: any, BorderColor3: any }
|
local frame = create "Frame" {} :: Instance & { BorderSizePixel: any, BorderColor3: any }
|
||||||
CHECK(frame.BorderSizePixel == defaults.Frame.BorderSizePixel)
|
CHECK(frame.BorderSizePixel == defaults.Frame.BorderSizePixel)
|
||||||
CHECK(frame.BorderColor3 == defaults.Frame.BorderColor3)
|
CHECK(frame.BorderColor3 == defaults.Frame.BorderColor3)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Set properties"
|
do CASE "set properties"
|
||||||
local text = create "TextLabel" {
|
local text = create "TextLabel" {
|
||||||
Name = "Label",
|
Name = "Label",
|
||||||
Text = "test"
|
Text = "test"
|
||||||
|
|
@ -611,7 +566,7 @@ TEST("create()", function()
|
||||||
CHECK(text.Text == "test")
|
CHECK(text.Text == "test")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Set nested properties"
|
do CASE "set nested properties"
|
||||||
local text = create "TextLabel" {
|
local text = create "TextLabel" {
|
||||||
{ Name = "Label" },
|
{ Name = "Label" },
|
||||||
Group = { Text = "test" }
|
Group = { Text = "test" }
|
||||||
|
|
@ -620,12 +575,12 @@ TEST("create()", function()
|
||||||
CHECK(text.Text == "test")
|
CHECK(text.Text == "test")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Independent"
|
do CASE "independent"
|
||||||
local frame = create "Frame"
|
local frame = create "Frame"
|
||||||
CHECK(frame {} ~= frame {})
|
CHECK(frame {} ~= frame {})
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Set children"
|
do CASE "set children"
|
||||||
local frame = create "Frame" {
|
local frame = create "Frame" {
|
||||||
create "TextLabel" { Name = "A" },
|
create "TextLabel" { Name = "A" },
|
||||||
create "TextLabel" { Name = "B" },
|
create "TextLabel" { Name = "B" },
|
||||||
|
|
@ -652,7 +607,7 @@ TEST("create()", function()
|
||||||
CHECK(frame:FindFirstChild "G")
|
CHECK(frame:FindFirstChild "G")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Binding properties to state"
|
do CASE "binding properties to source"
|
||||||
local name = source("Hi")
|
local name = source("Hi")
|
||||||
local text = source("Bye")
|
local text = source("Bye")
|
||||||
|
|
||||||
|
|
@ -671,7 +626,7 @@ TEST("create()", function()
|
||||||
CHECK(label.Text == "Bar")
|
CHECK(label.Text == "Bar")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Binding garbage collection"
|
do CASE "binding garbage collection"
|
||||||
do -- instance should gc when unparented
|
do -- instance should gc when unparented
|
||||||
local state = source("Hi")
|
local state = source("Hi")
|
||||||
|
|
||||||
|
|
@ -774,7 +729,7 @@ TEST("create()", function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Bind same state to multiple instance properties"
|
do CASE "bind same state to multiple instance properties"
|
||||||
local state = source "1"
|
local state = source "1"
|
||||||
|
|
||||||
local text = create "TextBox" {
|
local text = create "TextBox" {
|
||||||
|
|
@ -790,7 +745,7 @@ TEST("create()", function()
|
||||||
CHECK(text.PlaceholderText == "2")
|
CHECK(text.PlaceholderText == "2")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Bind children"
|
do CASE "bind children"
|
||||||
local state = source()
|
local state = source()
|
||||||
|
|
||||||
local a, b, c =
|
local a, b, c =
|
||||||
|
|
@ -844,7 +799,7 @@ TEST("create()", function()
|
||||||
end
|
end
|
||||||
]]
|
]]
|
||||||
|
|
||||||
do CASE "GC test"
|
do CASE "garbage collection test"
|
||||||
local wref
|
local wref
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|
@ -871,13 +826,11 @@ TEST("create()", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- todo: more comprehensive tests for maps
|
|
||||||
|
|
||||||
TEST("indexes()", function()
|
TEST("indexes()", function()
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local indexes = vide.indexes
|
local indexes = vide.indexes
|
||||||
|
|
||||||
do CASE "Use state"
|
do CASE "use source"
|
||||||
local input = source { 1, 2, 3 }
|
local input = source { 1, 2, 3 }
|
||||||
|
|
||||||
local output = indexes(input, function(v, k)
|
local output = indexes(input, function(v, k)
|
||||||
|
|
@ -889,7 +842,7 @@ TEST("indexes()", function()
|
||||||
CHECK("" .. input()[3] == output()[3])
|
CHECK("" .. input()[3] == output()[3])
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Cache result"
|
do CASE "cache result"
|
||||||
local input = source { 1, 2, 3 }
|
local input = source { 1, 2, 3 }
|
||||||
|
|
||||||
local runcount = table.create(3, 0)
|
local runcount = table.create(3, 0)
|
||||||
|
|
@ -910,7 +863,7 @@ TEST("indexes()", function()
|
||||||
CHECK(runcount[3] == 1)
|
CHECK(runcount[3] == 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Removal reflected"
|
do CASE "removal reflected"
|
||||||
local input = source { 1, 2, 3 }
|
local input = source { 1, 2, 3 }
|
||||||
|
|
||||||
local output = indexes(input, function(v, i)
|
local output = indexes(input, function(v, i)
|
||||||
|
|
@ -926,95 +879,44 @@ TEST("indexes()", function()
|
||||||
CHECK(t[3] == nil)
|
CHECK(t[3] == nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
do CASE "garbage collection"
|
||||||
do CASE "Bind children"
|
do -- check that `output` does not allow gc of `input`
|
||||||
local state, set = source { "A", "B", "C" }
|
local input = source {}
|
||||||
|
|
||||||
local derived = map(state, function(i, v)
|
local _derived = indexes(input, function(i, v)
|
||||||
return create "TextLabel" {
|
|
||||||
Name = v,
|
|
||||||
Text = tostring(i)
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
|
|
||||||
local frame = create "Frame" {
|
|
||||||
Name = "21",
|
|
||||||
[Children] = derived
|
|
||||||
}
|
|
||||||
|
|
||||||
local function find(childname: string): TextLabel
|
|
||||||
return frame:FindFirstChild(childname) :: TextLabel
|
|
||||||
end
|
|
||||||
|
|
||||||
CHECK(find "A".Text == "1")
|
|
||||||
CHECK(find "B".Text == "2")
|
|
||||||
CHECK(find "C".Text == "3")
|
|
||||||
|
|
||||||
set { "A", "C", "D" }
|
|
||||||
|
|
||||||
CHECK(find "A".Text == "1")
|
|
||||||
CHECK(not find "B")
|
|
||||||
CHECK(find "C".Text == "2")
|
|
||||||
CHECK(find "D".Text == "3")
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Use optional destructor"
|
|
||||||
local state, set = source { 1, 2, 3 }
|
|
||||||
local derived = map(state, function(i, v)
|
|
||||||
return { Value = v, Destroyed = false }
|
|
||||||
end, function(v)
|
|
||||||
v.Destroyed = true
|
|
||||||
end)
|
|
||||||
|
|
||||||
local first = derived()
|
|
||||||
|
|
||||||
CHECK(first[1].Destroyed == false)
|
|
||||||
|
|
||||||
set { 1, 2, 4 }
|
|
||||||
|
|
||||||
local _ = derived()
|
|
||||||
|
|
||||||
CHECK(first[1].Destroyed == false)
|
|
||||||
CHECK(first[2].Destroyed == false)
|
|
||||||
CHECK(first[3].Destroyed == true)
|
|
||||||
end
|
|
||||||
|
|
||||||
do CASE "Garbage collection"
|
|
||||||
do -- check that `derived` does not allow gc of `state`
|
|
||||||
local state = source {}
|
|
||||||
|
|
||||||
local derived = map(state, function(i, v)
|
|
||||||
return v
|
return v
|
||||||
end)
|
end)
|
||||||
|
|
||||||
wref.state, state = state, nil :: any
|
local wref = weak { input }
|
||||||
wref.derived = derived
|
|
||||||
|
input = nil :: any
|
||||||
|
|
||||||
gc()
|
gc()
|
||||||
CHECK(wref.state)
|
CHECK(wref[1])
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- check that `state` allows gc of `derived`
|
do -- check that `input` allows gc of `output`
|
||||||
local state = source {}
|
local input = source {}
|
||||||
|
|
||||||
local derived = map(state, function(i, v)
|
local output = indexes(input, function(i, v)
|
||||||
return i, v
|
return i, v
|
||||||
end) :: State?
|
end)
|
||||||
|
|
||||||
wref.state = state
|
local wref = weak { output }
|
||||||
wref.derived, derived = derived, nil
|
|
||||||
|
output = nil :: any
|
||||||
|
|
||||||
gc()
|
gc()
|
||||||
CHECK(not wref.derived)
|
CHECK(not wref[1])
|
||||||
end
|
end
|
||||||
end]]
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("values()", function()
|
TEST("values()", function()
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local values = vide.values
|
local values = vide.values
|
||||||
|
|
||||||
do CASE "Use state"
|
do CASE "use source"
|
||||||
local input = source { 1, 2, 3 }
|
local input = source { 1, 2, 3 }
|
||||||
|
|
||||||
local output = values(input, function(v, k)
|
local output = values(input, function(v, k)
|
||||||
|
|
@ -1026,7 +928,7 @@ TEST("values()", function()
|
||||||
CHECK("" .. input()[3] == output()[3])
|
CHECK("" .. input()[3] == output()[3])
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Cache result"
|
do CASE "cache result"
|
||||||
local input = source { 1, 2, 3 }
|
local input = source { 1, 2, 3 }
|
||||||
|
|
||||||
local runcount = table.create(3, 0)
|
local runcount = table.create(3, 0)
|
||||||
|
|
@ -1047,7 +949,7 @@ TEST("values()", function()
|
||||||
CHECK(runcount[3] == 1)
|
CHECK(runcount[3] == 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Removal reflected"
|
do CASE "removal reflected"
|
||||||
local input = source { 1, 2, 3 }
|
local input = source { 1, 2, 3 }
|
||||||
|
|
||||||
local output = values(input, function(v, i)
|
local output = values(input, function(v, i)
|
||||||
|
|
@ -1063,7 +965,7 @@ TEST("values()", function()
|
||||||
CHECK(t[3] == nil)
|
CHECK(t[3] == nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Removal reflected 2"
|
do CASE "removal reflected 2"
|
||||||
local input = source { 1 }
|
local input = source { 1 }
|
||||||
|
|
||||||
local output = values(input, function(v, i)
|
local output = values(input, function(v, i)
|
||||||
|
|
@ -1088,7 +990,7 @@ TEST("spring()", function()
|
||||||
local spring = vide.spring
|
local spring = vide.spring
|
||||||
local watch = vide.watch
|
local watch = vide.watch
|
||||||
|
|
||||||
do CASE "Update state (on next hearbeat resumption cycle)"
|
do CASE "update source (on next step)"
|
||||||
local value = source(10)
|
local value = source(10)
|
||||||
local springed = spring(value, 1, 1)
|
local springed = spring(value, 1, 1)
|
||||||
|
|
||||||
|
|
@ -1099,7 +1001,7 @@ TEST("spring()", function()
|
||||||
CHECK(springed() > 10)
|
CHECK(springed() > 10)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Garbage collection"
|
do CASE "garbage collection"
|
||||||
do -- `output` should not allow gc of `input`
|
do -- `output` should not allow gc of `input`
|
||||||
local input = source(10)
|
local input = source(10)
|
||||||
local _output = spring(input)
|
local _output = spring(input)
|
||||||
|
|
@ -1124,7 +1026,7 @@ TEST("spring()", function()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do CASE "Garbage collection (binded)"
|
do CASE "garbage collection (binded)"
|
||||||
local input = source(10)
|
local input = source(10)
|
||||||
local output = spring(input, 1, 1)
|
local output = spring(input, 1, 1)
|
||||||
|
|
||||||
|
|
@ -1139,7 +1041,7 @@ TEST("spring()", function()
|
||||||
CHECK(wref[1]) -- `output` should not gc
|
CHECK(wref[1]) -- `output` should not gc
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Spring finished"
|
do CASE "spring finished"
|
||||||
local input = source(0)
|
local input = source(0)
|
||||||
local output = spring(input)
|
local output = spring(input)
|
||||||
|
|
||||||
|
|
@ -1166,11 +1068,11 @@ TEST("spring()", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TEST("Events", function()
|
TEST("events", function()
|
||||||
local create = vide.create
|
local create = vide.create
|
||||||
|
|
||||||
local function Thing(props)
|
local function Thing(props)
|
||||||
local instance = Instance.new("Thing") :: any
|
local instance = Instance.new("Thing")
|
||||||
instance.Signal = Signal.new()
|
instance.Signal = Signal.new()
|
||||||
|
|
||||||
local clone = create(instance)(props)
|
local clone = create(instance)(props)
|
||||||
|
|
@ -1178,7 +1080,7 @@ TEST("Events", function()
|
||||||
return clone
|
return clone
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Connect event"
|
do CASE "connect event"
|
||||||
local connected = false
|
local connected = false
|
||||||
|
|
||||||
local val = Thing {
|
local val = Thing {
|
||||||
|
|
@ -1188,8 +1090,6 @@ TEST("Events", function()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
-- testkit.print2(getmetatable(val))
|
|
||||||
|
|
||||||
CHECK(not connected)
|
CHECK(not connected)
|
||||||
val.Value = 1; Signal.fire(val.Signal, val.Value)
|
val.Value = 1; Signal.fire(val.Signal, val.Value)
|
||||||
CHECK(connected)
|
CHECK(connected)
|
||||||
|
|
@ -1200,7 +1100,7 @@ TEST("actions", function()
|
||||||
local create = vide.create
|
local create = vide.create
|
||||||
local action = vide.action
|
local action = vide.action
|
||||||
|
|
||||||
do CASE "Run action"
|
do CASE "run action"
|
||||||
local ran = false
|
local ran = false
|
||||||
|
|
||||||
create "Frame" {
|
create "Frame" {
|
||||||
|
|
@ -1212,7 +1112,7 @@ TEST("actions", function()
|
||||||
CHECK(ran)
|
CHECK(ran)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Priorities"
|
do CASE "priorities"
|
||||||
local queue = {}
|
local queue = {}
|
||||||
|
|
||||||
create "Frame" {
|
create "Frame" {
|
||||||
|
|
@ -1235,8 +1135,9 @@ TEST("strict", function()
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local derive = vide.derive
|
local derive = vide.derive
|
||||||
local watch = vide.watch
|
local watch = vide.watch
|
||||||
|
local indexes, values = vide.indexes, vide.values
|
||||||
|
|
||||||
do CASE "Error on derived callback yield"
|
do CASE "error on derived callback yield"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
|
|
||||||
local ok = pcall(function()
|
local ok = pcall(function()
|
||||||
|
|
@ -1249,7 +1150,7 @@ TEST("strict", function()
|
||||||
CHECK(not ok)
|
CHECK(not ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Error on watcher callback yield"
|
do CASE "error on watcher callback yield"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
|
|
||||||
local ok = pcall(function()
|
local ok = pcall(function()
|
||||||
|
|
@ -1262,7 +1163,7 @@ TEST("strict", function()
|
||||||
CHECK(not ok)
|
CHECK(not ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Run derived callback twice"
|
do CASE "run derived callback twice"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
local runcount = 0
|
local runcount = 0
|
||||||
|
|
||||||
|
|
@ -1276,7 +1177,7 @@ TEST("strict", function()
|
||||||
CHECK(runcount == 4)
|
CHECK(runcount == 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "Run watcher callback twice"
|
do CASE "run watcher callback twice"
|
||||||
local state = source(1)
|
local state = source(1)
|
||||||
local runcount = 0
|
local runcount = 0
|
||||||
|
|
||||||
|
|
@ -1290,7 +1191,25 @@ TEST("strict", function()
|
||||||
CHECK(runcount == 4)
|
CHECK(runcount == 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- todo: add case for strict mode bindings
|
do CASE "indexes() error if primitive"
|
||||||
|
local state = source { 1 }
|
||||||
|
|
||||||
|
local ok = pcall(function()
|
||||||
|
indexes(state, function() return 1 end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
CHECK(not ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
do CASE "values() error if duplicate"
|
||||||
|
local state = source { 1, 2, 1 }
|
||||||
|
|
||||||
|
local ok = pcall(function()
|
||||||
|
values(state, function() return {} end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
CHECK(not ok)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local ok = FINISH()
|
local ok = FINISH()
|
||||||
|
|
|
||||||
3
todo.md
3
todo.md
|
|
@ -3,9 +3,10 @@
|
||||||
- cleanup codebase
|
- cleanup codebase
|
||||||
- setup site, improve docs
|
- setup site, improve docs
|
||||||
- cleanup within `values()` and `indexes()`
|
- cleanup within `values()` and `indexes()`
|
||||||
|
- behavior when a table source value is set to the same table
|
||||||
- address behavior of binding property to multiples states
|
- address behavior of binding property to multiples states
|
||||||
- strict mode
|
- strict mode
|
||||||
- better error reporting
|
- better error reporting and stack traces
|
||||||
- warn when `values()` returns primitive
|
- warn when `values()` returns primitive
|
||||||
- warn when `values()` returns duplicate object
|
- warn when `values()` returns duplicate object
|
||||||
- implement from solid
|
- implement from solid
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue