defmodule Bloccs.Web.Panels.Coverage do @moduledoc """ Panel 4 — structural coverage. Records a run (or loads a `.bloccs-trace`), derives the reached obligations with `Bloccs.Web.Coverage`, and overlays them on the topology graph (reached nodes/edges lit) alongside a summary bar and the list of unreached obligations. Trace export sits behind the `:trace_export` feature (the Pro seam) — enabled in the open build. """ use Bloccs.Web, :html import Bloccs.Web.Components.Graph alias Bloccs.Web.{Access, Coverage} attr :network, :any, required: true attr :base_path, :string, required: true attr :features, :any, required: true attr :coverage, :any, default: nil attr :recording, :boolean, default: false attr :upload, :any, default: nil def render(assigns) do ~H"""

Coverage

{source_label(@coverage.source)}
<.controls recording={@recording} upload={@upload} coverage={@coverage} features={@features} /> <%= if @coverage do %> <.summary report={@coverage.report} /> <.graph network={@network} states={Coverage.node_states(@network, @coverage.report)} reached_edges={Coverage.reached_edges(@coverage.report)} /> <.unreached report={@coverage.report} /> <% else %>

No coverage yet.

Record a run while messages flow through the network, or load a .bloccs-trace file, to see which ports and edges were exercised.

<% end %>
""" end attr :recording, :boolean, required: true attr :upload, :any, required: true attr :coverage, :any, required: true attr :features, :any, required: true defp controls(assigns) do ~H"""
● recording…
<.live_file_input upload={@upload} />
<.export coverage={@coverage} features={@features} />
""" end attr :coverage, :any, required: true attr :features, :any, required: true defp export(assigns) do ~H""" <%= cond do %> <% is_nil(@coverage) or is_nil(@coverage[:json]) -> %> <% Access.enabled?(:trace_export, @features) -> %> Export .bloccs-trace <% true -> %> <.pro_lock feature={:trace_export} /> <% end %> """ end attr :report, :map, required: true defp summary(assigns) do ~H"""
{@report.percent}% {@report.reached_count} / {@report.total} obligations reached
""" end attr :report, :map, required: true defp unreached(assigns) do ~H"""

Unreached ({length(@report.unreached)})

✓ Full structural coverage — every port and edge was exercised.

""" end defp label({:port_in, node, port}), do: "in · #{node}.#{port}" defp label({:port_out, node, port}), do: "out · #{node}.#{port}" defp label({:edge, {fn_, fp}, {tn, tp}}), do: "edge · #{fn_}.#{fp} → #{tn}.#{tp}" defp source_label(:recording), do: "from a recorded run" defp source_label(:trace), do: "from a loaded .bloccs-trace" defp source_label(_), do: "" end