summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/phase16.lua163
1 files changed, 163 insertions, 0 deletions
diff --git a/tests/phase16.lua b/tests/phase16.lua
index d285d28..ef7d197 100644
--- a/tests/phase16.lua
+++ b/tests/phase16.lua
@@ -16,6 +16,7 @@ local MemoryHost = require("clever_f.testing.memory_host")
local assertions = dofile(root .. "/tests/assertions.lua")
local same = assertions.same
local truthy = assertions.truthy
+local falsy = assertions.falsy
local tests = {}
local passed = 0
@@ -196,6 +197,168 @@ test("Public actions keep byte columns valid in every supported encoding", funct
end
end)
+test("Input and matching faults release temporary presentation resources", function()
+ local cases = {
+ {
+ message = "input failed",
+ packets = { { kind = "error", message = "input failed" } },
+ },
+ {
+ message = "matching failed",
+ packets = { { kind = "text", text = "x" } },
+ target_factory = {
+ build = function()
+ error("matching failed", 0)
+ end,
+ },
+ },
+ }
+
+ for _, case in ipairs(cases) do
+ local state = fresh_state()
+ local host = MemoryHost.new({
+ buffer_lines = { "ax" },
+ cursor = { line = 1, byte_column = 1 },
+ configuration = {
+ mark_char = false,
+ mark_cursor = true,
+ mark_direct = true,
+ show_prompt = true,
+ },
+ input_packets = case.packets,
+ emit_movement_events = false,
+ })
+ local facade = action_facade.new({
+ host = host,
+ target_factory = case.target_factory,
+ })
+
+ local outcome = facade:start_find_forward()
+ same(domain.ActionKind.ERROR, outcome.kind, case.message)
+ same(case.message, outcome.diagnostic, case.message)
+ same(0, #state.temporary_overlays, case.message)
+ same(nil, next(host:highlights()), case.message)
+ falsy(host:cursor_presentation().hidden, case.message)
+ end
+end)
+
+test("Movement faults leave acquisition temporaries released", function()
+ local state = fresh_state()
+ local host, facade = motion_host({
+ buffer_lines = { "ax" },
+ cursor = { line = 1, byte_column = 1 },
+ configuration = {
+ mark_cursor = true,
+ mark_direct = true,
+ },
+ input_packets = { { kind = "text", text = "x" } },
+ })
+ host.apply_cursor = function()
+ error("movement failed", 0)
+ end
+
+ local ok, failure = pcall(function()
+ facade:start_find_forward()
+ end)
+ falsy(ok)
+ truthy(tostring(failure):find("movement failed", 1, true) ~= nil)
+ same(0, #state.temporary_overlays)
+ same(nil, next(host:highlights()))
+ falsy(host:cursor_presentation().hidden)
+end)
+
+test("Overlay creation faults release earlier temporary overlays", function()
+ local state = fresh_state()
+ local host = MemoryHost.new({
+ buffer_lines = { "ax" },
+ cursor = { line = 1, byte_column = 1 },
+ configuration = {
+ mark_char = false,
+ mark_cursor = true,
+ mark_direct = true,
+ show_prompt = true,
+ },
+ input_packets = { { kind = "text", text = "x" } },
+ emit_movement_events = false,
+ })
+ local create = host.create_highlight
+ local calls = 0
+ host.create_highlight = function(self, specification)
+ calls = calls + 1
+ if calls == 2 then
+ error("overlay failed", 0)
+ end
+ return create(self, specification)
+ end
+ local facade = action_facade.new({ host = host })
+
+ local outcome = facade:start_find_forward()
+ same(domain.ActionKind.ERROR, outcome.kind)
+ same("overlay failed", outcome.diagnostic)
+ same(0, #state.temporary_overlays)
+ same(nil, next(host:highlights()))
+ falsy(host:cursor_presentation().hidden)
+end)
+
+test("Timer faults retain owned feedback and release acquisition temporaries", function()
+ local state = fresh_state()
+ local host = MemoryHost.new({
+ buffer_lines = { "ax" },
+ cursor = { line = 1, byte_column = 1 },
+ configuration = {
+ mark_char = true,
+ mark_cursor = true,
+ mark_direct = true,
+ highlight_timeout_ms = 10,
+ },
+ input_packets = { { kind = "text", text = "x" } },
+ emit_movement_events = false,
+ })
+ host.start_timer = function()
+ error("timer failed", 0)
+ end
+ local facade = action_facade.new({ host = host })
+
+ local ok, failure = pcall(function()
+ facade:start_find_forward()
+ end)
+ falsy(ok)
+ truthy(tostring(failure):find("timer failed", 1, true) ~= nil)
+ same(0, #state.temporary_overlays)
+ same(nil, state.highlight_timer)
+ same(1, #state.target_overlays)
+ same(1, #state.finalizers)
+ falsy(host:cursor_presentation().hidden)
+end)
+
+test("Event-registration faults roll back unowned persistent feedback", function()
+ local state = fresh_state()
+ local host = MemoryHost.new({
+ buffer_lines = { "ax" },
+ cursor = { line = 1, byte_column = 1 },
+ configuration = {
+ mark_char = true,
+ mark_cursor = true,
+ mark_direct = true,
+ },
+ input_packets = { { kind = "text", text = "x" } },
+ emit_movement_events = false,
+ })
+ host.register_events = function()
+ error("event registration failed", 0)
+ end
+ local facade = action_facade.new({ host = host })
+
+ local outcome = facade:start_find_forward()
+ same(domain.ActionKind.ERROR, outcome.kind)
+ same("event registration failed", outcome.diagnostic)
+ same(0, #state.temporary_overlays)
+ same(0, #state.target_overlays)
+ same(0, #state.finalizers)
+ same(nil, next(host:highlights()))
+ falsy(host:cursor_presentation().hidden)
+end)
+
test("Public actions share one record across operator-pending variants", function()
local variants = {
"no",