Skip to content

Commit c7a6661

Browse files
committed
test(replay): refactor to use event_manager
Using state.event_manager also lets us use (and test) the throttling emitter
1 parent 17e8e91 commit c7a6661

File tree

3 files changed

+79
-91
lines changed

3 files changed

+79
-91
lines changed

AGENTS.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,10 @@
1111
`nvim --headless -u tests/manual/init.lua -c "lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})"`
1212
- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing
1313
- **Debug rendering in headless mode:**
14-
`nvim --headless -u tests/manual/init_replay.lua "+ReplayHeadless" "+ReplayLoad tests/data/FILE.json" "+ReplayAll 1" "+sleep 500m | qa!"`
14+
`nvim --headless -u tests/manual/init_replay.lua "+ReplayHeadless" "+ReplayLoad tests/data/FILE.json" "+ReplayAll 0" "+qa"`
1515
This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.
16-
You can run to just a specific message # with (e.g. message # 12):
17-
`nvim --headless -u tests/manual/init_replay.lua "+ReplayHeadless" "+ReplayLoad tests/data/message-removal.json" "+ReplayNext 12" "+sleep 500m | qa"`
18-
```
19-
20-
```
16+
You can also run to just a specific message # with (e.g. message # 12):
17+
`nvim --headless -u tests/manual/init_replay.lua "+ReplayHeadless" "+ReplayLoad tests/data/message-removal.json" "+ReplayNext 12" "+qa"`
2118
- **Lint:** No explicit lint command; follow Lua best practices.
2219

2320
## Code Style Guidelines

tests/helpers.lua

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,15 @@ function M.replay_setup()
2222

2323
state.windows = ui.create_windows()
2424

25+
-- we use the event manager to dispatch events
26+
require('opencode.event_manager').setup()
27+
2528
-- we don't change any changes on session
2629
renderer._cleanup_subscriptions()
30+
31+
-- but we do want event_manager subscriptions so set those back up
32+
renderer._setup_event_subscriptions()
33+
2734
renderer.reset()
2835

2936
M.mock_time_ago()
@@ -228,24 +235,8 @@ end
228235

229236
function M.replay_event(event)
230237
event = vim.deepcopy(event)
231-
local renderer = require('opencode.ui.renderer')
232-
if event.type == 'message.updated' then
233-
renderer.on_message_updated(event.properties)
234-
elseif event.type == 'message.part.updated' then
235-
renderer.on_part_updated(event.properties)
236-
elseif event.type == 'message.removed' then
237-
renderer.on_message_removed(event.properties)
238-
elseif event.type == 'message.part.removed' then
239-
renderer.on_part_removed(event.properties)
240-
elseif event.type == 'session.compacted' then
241-
renderer.on_session_compacted(event.properties)
242-
elseif event.type == 'session.updated' then
243-
renderer.on_session_updated(event.properties)
244-
elseif event.type == 'permission.updated' then
245-
renderer.on_permission_updated(event.properties)
246-
elseif event.type == 'permission.replied' then
247-
renderer.on_permission_replied(event.properties)
248-
end
238+
-- synthetic "emit" by adding the event to the throttling emitter's queue
239+
require('opencode.state').event_manager.throttling_emitter:enqueue(event)
249240
end
250241

251242
function M.replay_events(events)

tests/manual/renderer_replay.lua

Lines changed: 67 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ local helpers = require('tests.helpers')
44
local output_window = require('opencode.ui.output_window')
55
local config = require('opencode.config')
66

7-
local topbar = require('opencode.ui.topbar')
8-
local footer = require('opencode.ui.footer')
9-
local loading_animation = require('opencode.ui.loading_animation')
10-
117
local M = {}
128

139
M.events = {}
@@ -64,50 +60,13 @@ function M.setup_windows(opts)
6460
return true
6561
end
6662

67-
function M.emit_event(event)
68-
if not event or not event.type then
69-
return
70-
end
71-
72-
local index = M.current_index
73-
local count = #M.events
74-
local id = event.properties.info and event.properties.info.id
75-
or event.properties.part and event.properties.part.id
76-
or event.properties.id
77-
or event.properties.permissionID
78-
or event.properties.partID
79-
or event.properties.messageID
80-
or ''
81-
vim.notify(
82-
'Event '
83-
.. index
84-
.. '/'
85-
.. count
86-
.. ': '
87-
.. event.type
88-
.. ' '
89-
.. id
90-
.. ' lines_set: '
91-
.. output_window._lines_set
92-
.. ' set_calls: '
93-
.. output_window._set_calls,
94-
vim.log.levels.INFO
95-
)
96-
helpers.replay_event(event)
97-
end
98-
9963
function M.replay_next(steps)
10064
steps = tonumber(steps) or 1
10165

102-
if M.current_index >= #M.events then
103-
vim.notify('No more events to replay', vim.log.levels.WARN)
104-
return
105-
end
106-
10766
for _ = 1, steps do
10867
if M.current_index < #M.events then
10968
M.current_index = M.current_index + 1
110-
M.emit_event(M.events[M.current_index])
69+
helpers.replay_event(M.events[M.current_index])
11170
else
11271
vim.notify('No more events to replay', vim.log.levels.WARN)
11372
return
@@ -131,49 +90,30 @@ function M.replay_all(delay_ms)
13190

13291
if delay_ms == 0 then
13392
M.replay_next(#M.events)
134-
vim.notify(
135-
'Renders: footer: '
136-
.. footer.render_calls
137-
.. ' topbar: '
138-
.. topbar.render_calls
139-
.. ' animation:'
140-
.. loading_animation.render_calls
141-
)
14293
return
14394
end
14495

14596
state.job_count = 1
14697

98+
-- This defer loop will fill the event manager throttling emitter and that
99+
-- emitter will drain the events through event manager, which
100+
-- will call renderer
147101
local function tick()
148-
if M.current_index >= #M.events then
102+
M.replay_next()
103+
if M.current_index >= #M.events or M.stop then
149104
state.job_count = 0
150-
vim.notify(
151-
('Renders: footer: %d topbar: %d animation: %d'):format(
152-
footer.render_calls,
153-
topbar.render_calls,
154-
loading_animation.render_calls
155-
)
156-
)
157105

158106
if M.headless_mode then
159107
M.dump_buffer_and_quit()
160108
end
161-
return
162-
end
163109

164-
if M.stop then
165-
M.stop = false
166-
state.job_count = 0
167-
vim.notify('Replay stopped at event ' .. M.current_index .. '/' .. #M.events, vim.log.levels.INFO)
168110
return
169111
end
170112

171-
M.replay_next()
172-
173113
vim.defer_fn(tick, delay_ms)
174114
end
175115

176-
vim.defer_fn(tick, delay_ms)
116+
tick()
177117
end
178118

179119
function M.replay_stop()
@@ -264,6 +204,11 @@ end
264204

265205
function M.dump_buffer_and_quit()
266206
vim.schedule(function()
207+
-- wait until the emitter queue is empty
208+
vim.wait(5000, function()
209+
return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)
210+
end)
211+
267212
if not state.windows or not state.windows.output_buf then
268213
print('ERROR: No output buffer available')
269214
vim.cmd('qall!')
@@ -397,6 +342,61 @@ function M.start(opts)
397342
vim.keymap.set('n', '<leader>r', ':ReplayReset<CR>')
398343

399344
M.setup_windows(opts)
345+
346+
local log_event = function(type, event)
347+
local index = M.current_index
348+
local count = #M.events
349+
local id = event.info and event.info.id
350+
or event.part and event.part.id
351+
or event.id
352+
or event.permissionID
353+
or event.partID
354+
or event.messageID
355+
or ''
356+
vim.notify(
357+
'Event '
358+
.. index
359+
.. '/'
360+
.. count
361+
.. ': '
362+
.. type
363+
.. ' '
364+
.. id
365+
.. ' lines_set: '
366+
.. output_window._lines_set
367+
.. ' set_calls: '
368+
.. output_window._set_calls,
369+
vim.log.levels.INFO
370+
)
371+
end
372+
373+
state.event_manager:subscribe('session.updated', function(event)
374+
log_event('session.updated', event)
375+
end)
376+
state.event_manager:subscribe('session.compacted', function(event)
377+
log_event('session.compacted', event)
378+
end)
379+
state.event_manager:subscribe('session.error', function(event)
380+
log_event('session.error', event)
381+
end)
382+
state.event_manager:subscribe('message.updated', function(event)
383+
log_event('message.updated', event)
384+
end)
385+
state.event_manager:subscribe('message.removed', function(event)
386+
log_event('message.removed', event)
387+
end)
388+
state.event_manager:subscribe('message.part.updated', function(event)
389+
log_event('message.part.updated', event)
390+
end)
391+
state.event_manager:subscribe('message.removed', function(event)
392+
log_event('message.removed', event)
393+
end)
394+
state.event_manager:subscribe('permission.updated', function(event)
395+
log_event('permission.updated', event)
396+
end)
397+
state.event_manager:subscribe('permission.replied', function(event)
398+
log_event('permission.replied', event)
399+
end)
400400
end
401401

402402
return M

0 commit comments

Comments
 (0)