Skip to content

Instantly share code, notes, and snippets.

@puzza007
Created March 22, 2026 04:50
Show Gist options
  • Select an option

  • Save puzza007/c588127fd36997ddd165d5f98c3e5c55 to your computer and use it in GitHub Desktop.

Select an option

Save puzza007/c588127fd36997ddd165d5f98c3e5c55 to your computer and use it in GitHub Desktop.
proper_viz: Gantt + Sequence diagrams from PropEr statem tests

proper_viz: Gantt + Sequence diagrams from PropEr statem tests

Output from proper_viz_gantt:render/2 and proper_viz_mermaid:render/2 applied to a katipo streaming body statem test. Each Upload/Get handle gets its own section (Gantt) or column (Sequence).

Gantt timeline

Each section is a handle. Time flows left-to-right. Shows concurrent uploads and gets, an await_uploading timeout on Upload-5, and the recovery (finish after timeout).

gantt
    dateFormat X
    axisFormat %s
    section Get-1
        start_get() → ok :1, 2
        await_response() → ok :2, 3
    section Get-2
        start_get() → ok :3, 4
        await_response() → ok :4, 5
    section Upload-3
        start_upload() → ok :5, 6
        finish_upload() → ok :6, 7
        await_response() → ok :7, 8
    section Get-4
        start_get() → ok :8, 9
        send_chunk("iw") → ok :9, 10
        await_response() → ok :10, 11
    section Upload-5
        start_upload() → ok :11, 12
        await_uploading() → timeout :crit, 12, 13
        finish_upload() → ok :13, 14
    section Get-6
        start_get() → ok :14, 15
        await_response() → ok :15, 16
    section Upload-7
        start_upload() → ok :16, 17
        finish_upload() → ok :17, 18
        await_response() → ok :18, 19
Loading

Sequence diagram

Same test run. Shows the call/return flow between the test harness and each handle.

sequenceDiagram
    participant Test
    participant Get-1
    participant Get-2
    participant Upload-3
    participant Get-4
    participant Upload-5
    participant Get-6
    participant Upload-7
    Test->>Get-1: start_get()
    Get-1-->>Test: {ok, Handle}
    Test->>Get-1: await_response({var,1})
    Get-1-->>Test: {ok, #{status => 200, ...}}
    Test->>Get-2: start_get()
    Get-2-->>Test: {ok, Handle}
    Test->>Upload-3: start_upload()
    Upload-3-->>Test: {ok, Handle}
    Test->>Get-2: await_response({var,3})
    Get-2-->>Test: {ok, #{status => 200, ...}}
    Test->>Get-4: start_get()
    Get-4-->>Test: {ok, Handle}
    Test->>Upload-5: start_upload()
    Upload-5-->>Test: {ok, Handle}
    Test->>Upload-3: finish_upload({var,4})
    Upload-3-->>Test: ok
    Test->>Get-4: send_chunk({var,6}, <<"iw">>)
    Get-4-->>Test: ok
    Test->>Upload-5: await_uploading({var,7})
    Upload-5--x Test: {error, #{code => await_timeout}}
    Test->>Upload-3: await_response({var,4})
    Upload-3-->>Test: {ok, #{status => 200, ...}}
    Test->>Get-4: await_response({var,6})
    Get-4-->>Test: {ok, #{status => 200, ...}}
    Test->>Upload-5: finish_upload({var,7})
    Upload-5-->>Test: ok
    Test->>Get-6: start_get()
    Get-6-->>Test: {ok, Handle}
    Test->>Get-6: await_response({var,12})
    Get-6-->>Test: {ok, #{status => 200, ...}}
    Test->>Upload-7: start_upload()
    Upload-7-->>Test: {ok, Handle}
    Test->>Upload-7: finish_upload({var,13})
    Upload-7-->>Test: ok
    Test->>Upload-7: await_response({var,13})
    Upload-7-->>Test: {ok, #{status => 200, ...}}
Loading

Usage

-include_lib("proper_viz/src/proper_viz.hrl").

prop_streaming(BaseUrl, Opts) ->
    ?FORALL(Cmds, commands(?MODULE),
        begin
            {History, S, Res} = run_commands(?MODULE, Cmds),
            cleanup(S),
            Trace = proper_viz:from_run(?MODULE, Cmds, History, S),
            ActorOpts = #{actors => fun actor/1},
            Gantt = proper_viz_gantt:render(Trace, ActorOpts),
            Mermaid = proper_viz_mermaid:render(Trace, ActorOpts),
            ?WHENFAIL(
                ct:pal("Gantt:~n~s~nSequence:~n~s~nResult: ~p",
                       [Gantt, Mermaid, Res]),
                aggregate(command_names(Cmds), Res =:= ok))
        end).

actor(#viz_event{symbolic = {_, start_upload, _}}) -> {new, <<"Upload">>};
actor(#viz_event{symbolic = {_, start_get, _}})    -> {new, <<"Get">>};
actor(#viz_event{symbolic = {_, _, [Handle | _]}}) -> {handle, Handle};
actor(#viz_event{})                                -> {static, <<"SUT">>}.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment