ExQuality.Report (ExQuality v0.11.0)

View Source

Builds the machine-readable form of a run.

mix quality exits 0 or 1 for the whole run, which tells a caller that something failed but not what. A script that wants to route on the result - hand the credo findings to one fixer, the test failures to another - would otherwise have to scrape the console or re-run the tools.

A report answers "which stages ran, which failed, and what did they find" from the same results the human output is rendered from, so the two streams can never disagree.

Shape

{
  "version": "0.6.0",
  "status": "error",
  "duration_ms": 48213,
  "stages": [
    {
      "name": "Credo",
      "status": "error",
      "summary": "5 issues (2 readability, 3 design)",
      "duration_ms": 412,
      "stats": {"issue_count": 5},
      "findings": [
        {
          "file": "lib/user.ex", "line": 42, "column": 3,
          "app": "web", "severity": "info",
          "check": "Credo.Check.Readability.ModuleDoc",
          "message": "Modules should have a @moduledoc tag."
        }
      ]
    },
    {
      "name": "Dialyzer", "status": "skipped",
      "summary": "--quick", "duration_ms": 0,
      "stats": {}, "findings": []
    }
  ]
}

Every stage carries the same keys whatever its status, so a consumer reads one field for the explanation rather than branching: a skipped stage puts its reason in summary, exactly as ExQuality.Stage.skipped/2 records it.

A stage that failed without producing findings carries its tool's full output under output instead, mirroring the human renderer. Findings are the parsed form; output is the fallback, and one of the two is always present for a failure.

Summary

Functions

Builds a report from the results of a run.

Encodes a report as pretty-printed JSON, newline terminated.

Types

t()

@type t() :: %{
  version: String.t(),
  status: String.t(),
  duration_ms: non_neg_integer(),
  stages: [map()]
}

Functions

build(results, duration_ms)

@spec build([ExQuality.Stage.result()], non_neg_integer()) :: t()

Builds a report from the results of a run.

duration_ms is the wall clock time of the whole run, which is not the sum of the stage durations because the analysis stages run in parallel.

iex> alias ExQuality.{Report, Stage}
iex> report = Report.build([Stage.skipped("Dialyzer", "--quick")], 12)
iex> {report.status, report.duration_ms, length(report.stages)}
{"ok", 12, 1}

encode!(report)

@spec encode!(map()) :: String.t()

Encodes a report as pretty-printed JSON, newline terminated.

iex> ExQuality.Report.encode!(%{status: "ok"})
"{\n  \"status\": \"ok\"\n}\n"