Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog.d/sui-25h.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Changed

- The live inspector's datamodel pane (`StatifierUI.Kino.inspect/3`) follows
its scrubber: on a selected macrostep it shows the values as they stood at
the end of that macrostep, and **Live** returns it to the current values.
13 changes: 7 additions & 6 deletions lib/statifier_ui/kino.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ if Code.ensure_loaded?(Kino) do
## The scrubber (sui-3gg)

Four buttons above the diagram - **First**, **Prev**, **Next**,
**Live** - move the diagram from the live tip to any macrostep in the
log and back. Selecting a macrostep draws the configuration that
macrostep settled in, opens its entry in the event log, and marks that
entry `- selected`; the note above the diagram says which
point is on screen. `StatifierUI.Inspector` decides all of it, so the
**Live** - move the diagram and the datamodel explorer from the live
tip to any macrostep in the log and back. Selecting a macrostep draws
the configuration that macrostep settled in, shows the datamodel as it
stood at the end of that macrostep, opens its entry in the event log,
and marks that entry `- selected`; the note above the diagram says
which point is on screen. `StatifierUI.Inspector` decides all of it, so the
whole behaviour is testable without Kino - this module only wires
buttons to `Updater.scrub/2`.

Expand Down Expand Up @@ -512,7 +513,7 @@ if Code.ensure_loaded?(Kino) do

Kino.Frame.render(
state.frames.datamodel,
Kino.Markdown.new(Inspector.datamodel(messages))
Kino.Markdown.new(Inspector.datamodel(messages, opts))
)

Kino.Frame.render(state.frames.log, Kino.Markdown.new(Inspector.event_log(messages, opts)))
Expand Down
15 changes: 7 additions & 8 deletions notebooks/inspector.livemd
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,12 @@ caught-up stream got there through statifier ADR-0034 replay.
(`sui-dc7`), and the note keeps the two readings apart: a configuration
the chart *exited* in is never worded as one it *settled* in.

The datamodel explorer in **this** widget does not move with the
scrubber - it always shows the current values. The per-macrostep fold
exists (`StatifierUI.Inspector.datamodel/2` takes the same `:selection`
every other read here does, `sui-2uz`), and the persisted stepper in step
10 is built on it; wiring it into the live inspector's own pane is a
separate change. Here the diagram and the log are the pair this walk
exercises.
The datamodel explorer follows the scrubber too. On a selected macrostep
it shows the values as they stood at the end of that macrostep - the fold
`StatifierUI.Inspector.datamodel/2` takes under the same `:selection`
every other read here does, and the one the persisted stepper in step 10
shows. Back at **|< First**, expect `authorization_attempts` and
`captured_cents` at `0`; **Live** returns the pane to the current values.

## 7. The form, including its error path

Expand Down Expand Up @@ -243,7 +242,7 @@ number and event.

1. Pick a macrostep from **Jump to**. Expect: the note, the diagram, the
event log's marked entry, **and the datamodel pane** all move to that
point together - unlike step 6, the values shown are the ones that
point together - as in step 6, the values shown are the ones that
macrostep ended with, not the ones the run finished with.
2. Read the **Datamodel changes in macrostep N** table below the panes.
Expect: one row per slot the step moved, with the value before and
Expand Down
71 changes: 71 additions & 0 deletions test/statifier_ui/kino_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,57 @@ defmodule StatifierUI.KinoTest do
end
end

describe "the scrubber's updater over a datamodel that moves" do
setup do
machine = SessionCase.compile!(@counter)
{sub, session} = SessionCase.start_early!(machine, "sess_kino_scrub_datamodel")
Session.send_event(session, "bump")
SessionCase.wait_for_macrostep(session, 2)
Session.send_event(session, "bump")
SessionCase.wait_for_macrostep(session, 3)
wait_for_buffered_macrostep(sub, 3)

frames =
Map.new([:status, :note, :diagram, :datamodel, :log], fn key ->
{key, Kino.Frame.new(placeholder: false)}
end)

{:ok, updater} =
Updater.start_link(
sub: sub,
machine: machine,
frames: frames,
initial_configuration: [0, 1]
)

%{updater: updater, frames: frames, messages: Subscriber.messages(sub)}
end

# Sabotage: rendered the live datamodel pane with `Inspector.datamodel/1`
# (no selection) in `Updater` - this test went red on the scrubbed
# assert, showing the live tip's count where the first macrostep's was
# expected, and the other updater tests stayed green.
test "the datamodel pane follows the scrubber and returns to the tip on live",
%{updater: updater, frames: frames, messages: messages} do
at_first = Inspector.datamodel(messages, selection: {:macrostep, 1})
at_tip = Inspector.datamodel(messages)

# The fold differs between the two points, so the pane can only match
# one of them.
assert at_first =~ "42"
assert at_tip =~ "44"
refute at_first == at_tip

:ok = Updater.scrub(updater, :first)
_settled = :sys.get_state(updater)
assert rendered_text(frames.datamodel) == at_first

:ok = Updater.scrub(updater, :live)
_settled = :sys.get_state(updater)
assert rendered_text(frames.datamodel) == at_tip
end
end

describe "truth_table/2" do
setup do
{:ok, fixtures} =
Expand Down Expand Up @@ -383,4 +434,24 @@ defmodule StatifierUI.KinoTest do
assert Process.alive?(stepper)
end
end

# The Markdown text a frame holds now - what the widget shows - read
# through `Kino.Frame.get_items/1`, the frame's own record of its content.
defp rendered_text(frame) do
[%Kino.Markdown{text: text}] = Kino.Frame.get_items(frame)
text
end

# The session reports a macrostep before its subscriber has necessarily
# buffered every message of it; poll the buffer itself.
defp wait_for_buffered_macrostep(sub, target, timeout \\ 1000) do
buffered? = Enum.any?(Subscriber.messages(sub), &(&1.macrostep == target))

if buffered? or timeout <= 0 do
:ok
else
Process.sleep(10)
wait_for_buffered_macrostep(sub, target, timeout - 10)
end
end
end
Loading