-module(metamon@internal@report). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/metamon/internal/report.gleam"). -export([render_json/1, render/1]). -export_type([input_source/0, morph_mode/0, failure_report/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(false). -type input_source() :: {edge_source, integer()} | {random_source, integer(), integer()}. -type morph_mode() :: {plain, binary()} | {equivariant, binary(), binary()}. -type failure_report() :: {failure_report, binary(), binary(), integer(), integer(), integer(), integer(), boolean(), gleam@option:option(morph_mode()), binary(), binary(), binary(), binary(), binary(), input_source(), boolean(), list(binary()), list(binary()), gleam@option:option(metamon@coverage:snapshot())}. -file("src/metamon/internal/report.gleam", 111). ?DOC(false). -spec coverage_to_json(metamon@coverage:snapshot()) -> gleam@json:json(). coverage_to_json(Snap) -> gleam@json:object( [{<<"total"/utf8>>, gleam@json:int(erlang:element(2, Snap))}, {<<"requirements"/utf8>>, gleam@json:array( metamon@coverage:requirements_of(Snap), fun(Req) -> gleam@json:object( [{<<"label"/utf8>>, gleam@json:string(erlang:element(2, Req))}, {<<"target_pct"/utf8>>, gleam@json:float( metamon@coverage:target_pct_of( Req, erlang:element(2, Snap) ) )}, {<<"hits"/utf8>>, gleam@json:int(erlang:element(4, Req))}, {<<"actual_pct"/utf8>>, gleam@json:float( metamon@coverage:actual_pct( erlang:element(4, Req), erlang:element(2, Snap) ) )}] ) end )}, {<<"collected"/utf8>>, gleam@json:array( maps:to_list(metamon@coverage:collected_of(Snap)), fun(Pair) -> gleam@json:object( [{<<"label"/utf8>>, gleam@json:string(erlang:element(1, Pair))}, {<<"count"/utf8>>, gleam@json:int(erlang:element(2, Pair))}] ) end )}] ). -file("src/metamon/internal/report.gleam", 57). ?DOC(false). -spec render_json(failure_report()) -> binary(). render_json(Report) -> Mode = case erlang:element(9, Report) of none -> gleam@json:string(<<"plain"/utf8>>); {some, {plain, T}} -> gleam@json:object( [{<<"kind"/utf8>>, gleam@json:string(<<"plain"/utf8>>)}, {<<"transform"/utf8>>, gleam@json:string(T)}] ); {some, {equivariant, Input_t, Output_t}} -> gleam@json:object( [{<<"kind"/utf8>>, gleam@json:string(<<"equivariant"/utf8>>)}, {<<"input_transform"/utf8>>, gleam@json:string(Input_t)}, {<<"output_transform"/utf8>>, gleam@json:string(Output_t)}] ) end, Source = case erlang:element(15, Report) of {edge_source, I} -> gleam@json:object( [{<<"kind"/utf8>>, gleam@json:string(<<"edge"/utf8>>)}, {<<"index"/utf8>>, gleam@json:int(I)}] ); {random_source, Seed_value, Size} -> gleam@json:object( [{<<"kind"/utf8>>, gleam@json:string(<<"random"/utf8>>)}, {<<"seed"/utf8>>, gleam@json:int(Seed_value)}, {<<"size"/utf8>>, gleam@json:int(Size)}] ) end, Coverage_field = case erlang:element(19, Report) of none -> gleam@json:null(); {some, Snap} -> coverage_to_json(Snap) end, _pipe = gleam@json:object( [{<<"mr_name"/utf8>>, gleam@json:string(erlang:element(2, Report))}, {<<"test_name"/utf8>>, gleam@json:string(erlang:element(3, Report))}, {<<"config_seed"/utf8>>, gleam@json:int(erlang:element(4, Report))}, {<<"runs_done"/utf8>>, gleam@json:int(erlang:element(5, Report))}, {<<"runs_total"/utf8>>, gleam@json:int(erlang:element(6, Report))}, {<<"shrinks_done"/utf8>>, gleam@json:int(erlang:element(7, Report))}, {<<"shrink_capped"/utf8>>, gleam@json:bool(erlang:element(8, Report))}, {<<"source"/utf8>>, Source}, {<<"morph_mode"/utf8>>, Mode}, {<<"relation"/utf8>>, gleam@json:string(erlang:element(10, Report))}, {<<"source_input"/utf8>>, gleam@json:string(erlang:element(11, Report))}, {<<"followup_input"/utf8>>, gleam@json:string(erlang:element(12, Report))}, {<<"source_output"/utf8>>, gleam@json:string(erlang:element(13, Report))}, {<<"followup_output"/utf8>>, gleam@json:string(erlang:element(14, Report))}, {<<"annotations"/utf8>>, gleam@json:array( erlang:element(17, Report), fun gleam@json:string/1 )}, {<<"footnotes"/utf8>>, gleam@json:array( erlang:element(18, Report), fun gleam@json:string/1 )}, {<<"coverage"/utf8>>, Coverage_field}] ), gleam@json:to_string(_pipe). -file("src/metamon/internal/report.gleam", 150). ?DOC(false). -spec header_lines(failure_report()) -> list(binary()). header_lines(Report) -> Title = case erlang:element(9, Report) of none -> <<"× property failed"/utf8>>; {some, _} -> <<<<"× metamorphic relation `"/utf8, (erlang:element(2, Report))/binary>>/binary, "` failed"/utf8>> end, Source = case erlang:element(15, Report) of {edge_source, Idx} -> <<<<"edge("/utf8, (erlang:integer_to_binary(Idx))/binary>>/binary, ")"/utf8>>; {random_source, Seed_value, Size} -> <<<<<<<<"random(seed="/utf8, (erlang:integer_to_binary(Seed_value))/binary>>/binary, ", size="/utf8>>/binary, (erlang:integer_to_binary(Size))/binary>>/binary, ")"/utf8>> end, Shrinks_label = case erlang:element(8, Report) of true -> <<(erlang:integer_to_binary(erlang:element(7, Report)))/binary, "+ (limit reached)"/utf8>>; false -> erlang:integer_to_binary(erlang:element(7, Report)) end, [Title, <<" test: "/utf8, (erlang:element(3, Report))/binary>>, <<" source: "/utf8, Source/binary>>, <<" config seed: "/utf8, (erlang:integer_to_binary(erlang:element(4, Report)))/binary>>, <<<<<<" runs: "/utf8, (erlang:integer_to_binary(erlang:element(5, Report)))/binary>>/binary, " / "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(6, Report)))/binary>>, <<" shrinks: "/utf8, Shrinks_label/binary>>]. -file("src/metamon/internal/report.gleam", 220). ?DOC(false). -spec list_join_block(list(binary())) -> list(binary()). list_join_block(Lines) -> case Lines of [] -> []; Items -> [gleam@string:join(Items, <<"\n"/utf8>>)] end. -file("src/metamon/internal/report.gleam", 253). ?DOC(false). -spec coverage_lines(metamon@coverage:snapshot()) -> list(binary()). coverage_lines(Snap) -> Total = erlang:element(2, Snap), Req_lines = gleam@list:map( metamon@coverage:requirements_of(Snap), fun(Req) -> Pct = metamon@coverage:actual_pct(erlang:element(4, Req), Total), <<<<<<<<<<<<<<<<<<<<" "/utf8, (erlang:element(2, Req))/binary>>/binary, ": "/utf8>>/binary, (erlang:integer_to_binary( erlang:element(4, Req) ))/binary>>/binary, "/"/utf8>>/binary, (erlang:integer_to_binary(Total))/binary>>/binary, " ("/utf8>>/binary, (gleam_stdlib:float_to_string(Pct))/binary>>/binary, "%) target≥"/utf8>>/binary, (gleam_stdlib:float_to_string( metamon@coverage:target_pct_of(Req, Total) ))/binary>>/binary, "%"/utf8>> end ), Collected = metamon@coverage:collected_of(Snap), Collected_lines = begin _pipe = maps:to_list(Collected), gleam@list:map( _pipe, fun(Pair) -> <<<<<<" "/utf8, (erlang:element(1, Pair))/binary>>/binary, ": "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Pair)))/binary>> end ) end, lists:append(Req_lines, Collected_lines). -file("src/metamon/internal/report.gleam", 227). ?DOC(false). -spec extras_lines(failure_report()) -> list(binary()). extras_lines(Report) -> Annotations_block = case erlang:element(17, Report) of [] -> []; Items -> [<<" annotations:\n"/utf8, (gleam@string:join( gleam@list:map( Items, fun(A) -> <<" - "/utf8, A/binary>> end ), <<"\n"/utf8>> ))/binary>>] end, Footnotes_block = case erlang:element(18, Report) of [] -> []; Items@1 -> [<<" footnotes:\n"/utf8, (gleam@string:join( gleam@list:map( Items@1, fun(F) -> <<" - "/utf8, F/binary>> end ), <<"\n"/utf8>> ))/binary>>] end, Coverage_block = case erlang:element(19, Report) of none -> []; {some, Snap} -> case coverage_lines(Snap) of [] -> []; Lines -> [<<" coverage:\n"/utf8, (gleam@string:join(Lines, <<"\n"/utf8>>))/binary>>] end end, lists:append([Annotations_block, Footnotes_block, Coverage_block]). -file("src/metamon/internal/report.gleam", 277). ?DOC(false). -spec reproduce_lines(failure_report()) -> list(binary()). reproduce_lines(Report) -> case erlang:element(9, Report) of none -> [<<" reproduce (paste into a test):"/utf8>>, <<" // The property failed for this input. To pin it as a"/utf8>>, <<" // regression, store it explicitly and assert directly."/utf8>>, <<" let input = "/utf8, (erlang:element(11, Report))/binary>>, <<" should.be_true(property(input))"/utf8>>]; {some, _} -> [<<" reproduce (paste into a test):"/utf8>>, <<" // The MR failed for this input. To pin it as a regression,"/utf8>>, <<" // call assert_morph with the shrunk input and the same MR."/utf8>>, <<" let input = "/utf8, (erlang:element(11, Report))/binary>>, <<" metamon.assert_morph(input, mr, f)"/utf8>>] end. -file("src/metamon/internal/report.gleam", 300). ?DOC(false). -spec indent(binary(), binary()) -> binary(). indent(Block, Prefix) -> _pipe = gleam@string:split(Block, <<"\n"/utf8>>), _pipe@1 = gleam@list:map( _pipe, fun(Line) -> <> end ), gleam@string:join(_pipe@1, <<"\n"/utf8>>). -file("src/metamon/internal/report.gleam", 181). ?DOC(false). -spec body_lines(failure_report()) -> list(binary()). body_lines(Report) -> Transform_block = case erlang:element(9, Report) of none -> []; {some, {plain, T}} -> [<<<<" transform: `"/utf8, T/binary>>/binary, "`"/utf8>>]; {some, {equivariant, Input_t, Output_t}} -> [<<<<" input: `"/utf8, Input_t/binary>>/binary, "`"/utf8>>, <<<<" output: `"/utf8, Output_t/binary>>/binary, "`"/utf8>>] end, Relation_block = case erlang:element(9, Report) of none -> []; {some, _} -> [<<<<" relation: `"/utf8, (erlang:element(10, Report))/binary>>/binary, "`"/utf8>>] end, Inputs = case erlang:element(9, Report) of none -> [<<" source input (shrunk):\n "/utf8, (erlang:element(11, Report))/binary>>]; {some, _} -> [<<" source input (shrunk):\n "/utf8, (erlang:element(11, Report))/binary>>, <<" follow-up input (= transform(source)):\n "/utf8, (erlang:element(12, Report))/binary>>, <<" source output:\n "/utf8, (erlang:element(13, Report))/binary>>, <<" follow-up output:\n "/utf8, (erlang:element(14, Report))/binary>>] end, Diff_block = case {erlang:element(16, Report), erlang:element(9, Report)} of {true, {some, _}} -> [<<" diff (source_output vs follow-up_output):\n"/utf8, (indent( metamon@diff:render( metamon@diff:diff( erlang:element(13, Report), erlang:element(14, Report) ) ), <<" "/utf8>> ))/binary>>]; {_, _} -> [] end, lists:append( [list_join_block(lists:append(Transform_block, Relation_block)), Inputs, Diff_block] ). -file("src/metamon/internal/report.gleam", 139). ?DOC(false). -spec render(failure_report()) -> binary(). render(Report) -> Header_block = begin _pipe = header_lines(Report), gleam@string:join(_pipe, <<"\n"/utf8>>) end, Body_block = begin _pipe@1 = body_lines(Report), gleam@string:join(_pipe@1, <<"\n\n"/utf8>>) end, Extras_block = begin _pipe@2 = extras_lines(Report), gleam@string:join(_pipe@2, <<"\n\n"/utf8>>) end, Reproduce_block = begin _pipe@3 = reproduce_lines(Report), gleam@string:join(_pipe@3, <<"\n"/utf8>>) end, Blocks = begin _pipe@4 = [Header_block, Body_block, Extras_block, Reproduce_block], gleam@list:filter(_pipe@4, fun(B) -> B /= <<""/utf8>> end) end, gleam@string:join(Blocks, <<"\n\n"/utf8>>).