mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-11 23:54:52 +09:00
- The instance sends every value of a feature, defaults included, so update_config is one loop which sets each field and compares. The stored table is the last set of values, and _apply is gone. - values_equal compares table sizes before the contents. - Protected entities are registered with register_on_object_destroyed and forgotten in on_object_destroyed, which covers every way an entity can go. The four removal handlers and the validity checks are gone. Entities destroyed while the feature is disabled are swept when it is enabled again. Saves from before the registrations table get it through the Storage on_init callback. - Spawn area applied the water offset twice and the pattern offset never. The pattern tiles now move with the entities above them. - pruneFeatureValues reads a validation result instead of catching an error, and FeaturesPage hands its pending state and unsaved bar to helpers. - Shorter comments throughout. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
153 lines
7.7 KiB
Lua
153 lines
7.7 KiB
Lua
local Suite = ... --- @type Suite<ExpScenario.TestEnv>
|
|
|
|
--- The raised on_config_updated events as { path, old value, new value }, sorted by path
|
|
local function updates(env)
|
|
local rtn = {}
|
|
for _, event in ipairs(env.events) do
|
|
rtn[#rtn + 1] = { event.path, event.old_value, event.new_value }
|
|
end
|
|
table.sort(rtn, function(a, b) return a[1] < b[1] end)
|
|
return rtn
|
|
end
|
|
|
|
Suite.test("register() enables features unless the config says otherwise", function(env)
|
|
Suite.eq(env.Feature.register("on", { value = 1 }).config.enabled, true, "enabled by default")
|
|
Suite.eq(env.Feature.register("off", { enabled = false }):is_enabled(), false, "the config can disable it")
|
|
Suite.eq(env.Feature.register("empty").config, { enabled = true }, "config is optional")
|
|
Suite.throws(function() env.Feature.register("on", {}) end, "Feature already registered: on", "names are unique")
|
|
end)
|
|
|
|
Suite.test("register() returns the config as well", function(env)
|
|
local feature, config = env.Feature.register("test", { flag = true })
|
|
Suite.eq(config == feature.config, true, "the same table")
|
|
end)
|
|
|
|
Suite.test("get() finds registered features", function(env)
|
|
local feature = env.Feature.register("test")
|
|
Suite.eq(env.Feature.get("test") == feature, true, "by name")
|
|
Suite.throws(function() env.Feature.get("unknown") end, "Unknown feature: unknown", "unknown names error")
|
|
end)
|
|
|
|
Suite.test("update_config() sets every value, missing ones to their default", function(env)
|
|
local feature, config = env.Feature.register("test", { flag = true, count = 5, text = "a" })
|
|
feature:update_config{ enabled = false, flag = false, count = 7 }
|
|
Suite.eq(config, { enabled = false, flag = false, count = 7, text = "a" }, "given values are set")
|
|
|
|
feature:update_config{ enabled = true, text = "b" }
|
|
Suite.eq(config, { enabled = true, flag = true, count = 5, text = "b" }, "values left out go back to their default")
|
|
Suite.eq(env.storage, { test = { enabled = true, text = "b" } }, "the values are kept in storage")
|
|
end)
|
|
|
|
Suite.test("update_config() raises on_config_updated for each changed value", function(env)
|
|
local feature = env.Feature.register("test", { flag = true, names = env.Feature.set{ "a" }, section = { count = 1 } })
|
|
feature:update_config{ enabled = false, flag = true, names = { "a" }, ["section.count"] = 2 }
|
|
Suite.eq(env.Feature.events.on_config_updated, assert(env.events[1]).name, "the event is on_config_updated")
|
|
Suite.eq(updates(env), {
|
|
{ "enabled", true, false },
|
|
{ "section.count", 1, 2 },
|
|
}, "with the path, old value, and new value, unchanged values raise nothing")
|
|
end)
|
|
|
|
Suite.test("update_config() raises when a value goes back to its default", function(env)
|
|
local feature = env.Feature.register("test", { flag = true })
|
|
feature:update_config{ enabled = true, flag = false }
|
|
env.events = {}
|
|
feature:update_config{ enabled = true }
|
|
Suite.eq(updates(env), { { "flag", false, true } }, "the value reverts")
|
|
end)
|
|
|
|
Suite.test("update_config() ignores and logs values of the wrong type", function(env)
|
|
local function check() return true end
|
|
local feature, config = env.Feature.register("test", { flag = true, check = check })
|
|
feature:update_config{ enabled = true, flag = "yes", check = false }
|
|
Suite.eq(config, { enabled = true, flag = true, check = check }, "wrong types and functions keep their default")
|
|
Suite.eq(#env.logged, 2, "each one is logged")
|
|
end)
|
|
|
|
Suite.test("receive_update() updates known features", function(env)
|
|
local feature = env.Feature.register("test", { flag = true })
|
|
env.Feature.receive_update{
|
|
{ name = "test", values = { enabled = true, flag = false } },
|
|
{ name = "unknown", values = { enabled = false } },
|
|
}
|
|
Suite.eq(feature.config.flag, false, "the known feature changed")
|
|
Suite.eq(env.storage.unknown, nil, "unknown features are not stored")
|
|
end)
|
|
|
|
Suite.test("on_load applies the stored values without raising events", function(env)
|
|
local feature, config = env.Feature.register("test", { flag = true })
|
|
env.on_load{ test = { enabled = false, flag = false, gone = 1 } }
|
|
Suite.eq(config, { enabled = false, flag = false }, "the saved values apply, unknown ones are skipped")
|
|
Suite.empty(env.events, "nothing is raised")
|
|
|
|
feature:update_config{ enabled = true }
|
|
Suite.eq(env.storage, { test = { enabled = true } }, "later updates replace the stored values")
|
|
end)
|
|
|
|
Suite.test("guard() skips handlers while the feature is disabled", function(env)
|
|
local feature = env.Feature.register("test")
|
|
local calls = {}
|
|
local lib = feature:guard{
|
|
events = { on_event = function(event) calls[#calls + 1] = event end },
|
|
on_nth_tick = { [60] = function(event) calls[#calls + 1] = event end },
|
|
on_init = function() end,
|
|
}
|
|
|
|
lib.events.on_event("a")
|
|
lib.on_nth_tick[60]("b")
|
|
feature:update_config{ enabled = false }
|
|
lib.events.on_event("c")
|
|
lib.on_nth_tick[60]("d")
|
|
Suite.eq(calls, { "a", "b" }, "only called while enabled")
|
|
end)
|
|
|
|
Suite.test("register() addresses nested values by path", function(env)
|
|
local offset = { 1, 2 }
|
|
local feature, config = env.Feature.register("test", { section = { flag = true, count = 1 }, offset = offset })
|
|
feature:update_config{ enabled = true, ["section.flag"] = false, offset = { "x" } }
|
|
Suite.eq(config.section, { flag = false, count = 1 }, "the nested value changes in place")
|
|
Suite.eq(config.offset, offset, "tables which are not sections can not be set")
|
|
|
|
feature:update_config{ enabled = true }
|
|
Suite.eq(config.section.flag, true, "and goes back to its default")
|
|
end)
|
|
|
|
Suite.test("list() declares a list of strings", function(env)
|
|
local feature, config = env.Feature.register("test", { names = env.Feature.list{ "a", "b" }, empty = env.Feature.list{}, plain = { "x" } })
|
|
Suite.eq(config.names, { "a", "b" }, "the default is the list")
|
|
feature:update_config{ enabled = true, names = { "c" }, empty = { "d" }, plain = { "y" } }
|
|
Suite.eq(config.names, { "c" }, "a list is replaced")
|
|
Suite.eq(config.empty, { "d" }, "an empty list can be set")
|
|
Suite.eq(config.plain, { "x" }, "a plain table can not be set")
|
|
feature:update_config{ enabled = true, names = { 1 } }
|
|
Suite.eq(config.names, { "a", "b" }, "a list with other types is invalid")
|
|
end)
|
|
|
|
Suite.test("set() declares a set of strings which is sent as a list", function(env)
|
|
local feature, config = env.Feature.register("test", { names = env.Feature.set{ "a" }, empty = env.Feature.set{} })
|
|
Suite.eq(config.names, { a = true }, "the default is a set")
|
|
Suite.eq(config.empty, {}, "and can be empty")
|
|
feature:update_config{ enabled = true, names = { "b", "c" } }
|
|
Suite.eq(config.names, { b = true, c = true }, "the list is converted to a set")
|
|
env.on_load{ test = { enabled = true, names = { "d" } } }
|
|
Suite.eq(config.names, { d = true }, "also when the values are loaded")
|
|
end)
|
|
|
|
Suite.test("optional() declares a value without a default", function(env)
|
|
local feature, config = env.Feature.register("test", { role_id = env.Feature.optional("number") })
|
|
Suite.eq(config.role_id, nil, "nil until set")
|
|
feature:update_config{ enabled = true, role_id = 3 }
|
|
Suite.eq(config.role_id, 3, "set with the given type")
|
|
feature:update_config{ enabled = true }
|
|
Suite.eq(config.role_id, nil, "and back to nil")
|
|
end)
|
|
|
|
Suite.test("config files register their feature", function(env)
|
|
local feature = env.load_config("spawn_area")
|
|
feature:update_config{ enabled = true, ["turrets.enabled"] = false }
|
|
Suite.eq(feature.config.turrets.enabled, false, "spawn_area can be changed")
|
|
Suite.eq(feature.config.turrets.ammo_type, "uranium-rounds-magazine", "other values keep their default")
|
|
end)
|
|
|
|
return Suite.run()
|