diff options
Diffstat (limited to 'tests/phase16.lua')
| -rw-r--r-- | tests/phase16.lua | 163 |
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", |
