Skip to content

qc.reporters

Reporter

Bases: ABC

Base class for test result reporters.

Reporters handle the presentation of test results in different formats such as console output or HTML files.

report_results abstractmethod

report_results(
    results: dict[str | None, list[Result]]
    | list[_TaggedResult],
    *,
    render_context: bool = True,
    render_description: bool = True,
    render_traceback: bool = True,
    render_message: bool = True,
    serialize_context_exportable_obj: bool = False,
    **kwargs,
) -> None

Report test results.

Parameters:

Name Type Description Default
results dict[str | None, list[Result]] | list[_TaggedResult]

List of tagged test results.

required
statistics

Overall statistics for the test run.

required
render_context bool

Whether to include test context.

True
render_description bool

Whether to include test descriptions.

True
render_traceback bool

Whether to include tracebacks for errors.

True
render_message bool

Whether to include test result messages.

True
serialize_context_exportable_obj bool

Whether to serialize ContextExportableObj instances.

False
Source code in src/contraqctor/qc/reporters.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@abc.abstractmethod
def report_results(
    self,
    results: dict[str | None, list[Result]] | list[_TaggedResult],
    *,
    render_context: bool = True,
    render_description: bool = True,
    render_traceback: bool = True,
    render_message: bool = True,
    serialize_context_exportable_obj: bool = False,
    **kwargs,
) -> None:
    """Report test results.

    Args:
        results: List of tagged test results.
        statistics: Overall statistics for the test run.
        render_context: Whether to include test context.
        render_description: Whether to include test descriptions.
        render_traceback: Whether to include tracebacks for errors.
        render_message: Whether to include test result messages.
        serialize_context_exportable_obj: Whether to serialize ContextExportableObj instances.
    """
    pass

ConsoleReporter

ConsoleReporter(
    console: Optional[Console] = None,
    include_status: Set[Status] = frozenset(
        {FAILED, ERROR, WARNING}
    ),
    default_group_name: str = "Ungrouped",
)

Bases: Reporter

Reporter that outputs test results to a rich console.

Parameters:

Name Type Description Default
console Optional[Console]

Optional rich Console instance. If not provided, creates a new one.

None
include_status Set[Status]

Set of statuses to include in detailed output.

frozenset({FAILED, ERROR, WARNING})
default_group_name str

Name to use for ungrouped tests.

'Ungrouped'
Source code in src/contraqctor/qc/reporters.py
72
73
74
75
76
77
78
79
80
def __init__(
    self,
    console: t.Optional[Console] = None,
    include_status: t.Set[Status] = frozenset({Status.FAILED, Status.ERROR, Status.WARNING}),
    default_group_name: str = "Ungrouped",
):
    self.console = console or Console()
    self.include_status = include_status
    self.default_group_name = default_group_name

report_results

report_results(
    results: dict[str | None, list[Result]]
    | list[_TaggedResult],
    *,
    render_context: bool = True,
    render_description: bool = True,
    render_traceback: bool = True,
    render_message: bool = True,
    serialize_context_exportable_obj: bool = False,
    asset_output_dir: str | Path = Path("./report/assets"),
    **kwargs,
) -> None

Print detailed test results to the console.

Parameters:

Name Type Description Default
results dict[str | None, list[Result]] | list[_TaggedResult]

List of tagged test results.

required
statistics

Overall statistics for the test run.

required
render_context bool

Whether to include test context.

True
render_description bool

Whether to include test descriptions.

True
render_traceback bool

Whether to include tracebacks for errors.

True
render_message bool

Whether to include test result messages.

True
serialize_context_exportable_obj bool

Whether to serialize ContextExportableObj instances.

False
asset_output_dir str | Path

Directory for saving serialized assets. Defaults to "./report/assets".

Path('./report/assets')
Source code in src/contraqctor/qc/reporters.py
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def report_results(
    self,
    results: dict[str | None, list[Result]] | list[_TaggedResult],
    *,
    render_context: bool = True,
    render_description: bool = True,
    render_traceback: bool = True,
    render_message: bool = True,
    serialize_context_exportable_obj: bool = False,
    asset_output_dir: str | Path = Path("./report/assets"),
    **kwargs,
) -> None:
    """Print detailed test results to the console.

    Args:
        results: List of tagged test results.
        statistics: Overall statistics for the test run.
        render_context: Whether to include test context.
        render_description: Whether to include test descriptions.
        render_traceback: Whether to include tracebacks for errors.
        render_message: Whether to include test result messages.
        serialize_context_exportable_obj: Whether to serialize ContextExportableObj instances.
        asset_output_dir: Directory for saving serialized assets. Defaults to "./report/assets".
    """
    if not results:
        return

    results = _normalize_results(results)
    # Setup serializer and serialize ALL results if needed
    # (not just the ones being displayed)
    serializer = None
    output_dir = None
    serialized_contexts = {}

    if serialize_context_exportable_obj:
        serializer = ContextExportableObjSerializer()
        if asset_output_dir is None:
            output_dir = Path("./report/assets")
        else:
            output_dir = Path(asset_output_dir)

        # Serialize all results, not just displayed ones
        for idx, tagged_result in enumerate(results):
            if tagged_result.result.context is not None:
                serialized_contexts[idx] = serializer.serialize_as_file(
                    tagged_result.result.context, output_dir, f"test_{idx}"
                )

    all_included_results = [
        tagged_result for tagged_result in results if tagged_result.result.status in self.include_status
    ]

    if not all_included_results:
        return

    self.console.print()
    self.console.print(f"[bold]contraqctor v{__version__}[/bold]")
    self.console.print(f"[dim]Test run: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}[/dim]")
    self.console.print()
    self._print_status_header(self.include_status)
    self.console.print()

    idx = 0
    for group, test_results in _TaggedResult.group_by_group(all_included_results):
        group_name = group or self.default_group_name
        for result in test_results:
            # Find the original index in the full results list
            original_idx = results.index(result)
            context = serialized_contexts.get(original_idx, result.result.context)

            self._print_test_result(
                result.result,
                group_name,
                idx,
                render_message,
                render_description,
                render_traceback,
                render_context,
                context,
            )
            self.console.print()
            idx += 1

HtmlReporter

HtmlReporter(
    output_path: Union[str, Path] = "report.html",
    template_dir: Optional[Union[str, Path]] = None,
    default_group_name: str = "Ungrouped",
    serializer: Optional[
        ContextExportableObjSerializer
    ] = None,
)

Bases: Reporter

Reporter that generates HTML output for test results.

Parameters:

Name Type Description Default
output_path Union[str, Path]

Path where the HTML report should be written.

'report.html'
template_dir Optional[Union[str, Path]]

Optional directory containing custom Jinja2 templates.

None
default_group_name str

Name to use for ungrouped tests.

'Ungrouped'
serializer Optional[ContextExportableObjSerializer]

Optional custom ContextExportableObjSerializer instance.

None
Source code in src/contraqctor/qc/reporters.py
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def __init__(
    self,
    output_path: t.Union[str, Path] = "report.html",
    template_dir: t.Optional[t.Union[str, Path]] = None,
    default_group_name: str = "Ungrouped",
    serializer: t.Optional[ContextExportableObjSerializer] = None,
):
    self.output_path = Path(output_path)
    self.default_group_name = default_group_name
    self.serializer = serializer or ContextExportableObjSerializer()

    if template_dir:
        self.template_dir = Path(template_dir)
    else:
        self.template_dir = Path(__file__).parent / "templates"

    self.env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(str(self.template_dir)), autoescape=jinja2.select_autoescape(["html", "xml"])
    )

report_results

report_results(
    results: dict[str | None, list[Result]]
    | list[_TaggedResult],
    *,
    render_context: bool = True,
    render_description: bool = True,
    render_traceback: bool = True,
    render_message: bool = True,
    serialize_context_exportable_obj: bool = True,
    **kwargs,
) -> None

Generate HTML report of test results.

Parameters:

Name Type Description Default
results dict[str | None, list[Result]] | list[_TaggedResult]

List of tagged test results.

required
statistics

Overall statistics for the test run.

required
render_context bool

Whether to include test context.

True
render_description bool

Whether to include test descriptions.

True
render_traceback bool

Whether to include tracebacks for errors.

True
render_message bool

Whether to include test result messages.

True
serialize_context_exportable_obj bool

Whether to serialize ContextExportableObj instances.

True
asset_output_dir

Directory for saving serialized assets (not used for HTML, uses base64).

required
Source code in src/contraqctor/qc/reporters.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
def report_results(
    self,
    results: dict[str | None, list[Result]] | list[_TaggedResult],
    *,
    render_context: bool = True,
    render_description: bool = True,
    render_traceback: bool = True,
    render_message: bool = True,
    serialize_context_exportable_obj: bool = True,
    **kwargs,
) -> None:
    """Generate HTML report of test results.

    Args:
        results: List of tagged test results.
        statistics: Overall statistics for the test run.
        render_context: Whether to include test context.
        render_description: Whether to include test descriptions.
        render_traceback: Whether to include tracebacks for errors.
        render_message: Whether to include test result messages.
        serialize_context_exportable_obj: Whether to serialize ContextExportableObj instances.
        asset_output_dir: Directory for saving serialized assets (not used for HTML, uses base64).
    """
    template = self.env.get_template("report.html")

    results = _normalize_results(results)

    grouped_results = []
    for group, test_results in _TaggedResult.group_by_group(results):
        group_name = group or self.default_group_name
        group_stats = ResultsStatistics.from_results([tr.result for tr in test_results])

        # Group by suite within the group
        suites: dict[str, list[dict]] = {}
        for tr in test_results:
            suite_name = tr.suite.name
            if suite_name not in suites:
                suites[suite_name] = []

            # Serialize context if requested
            context = tr.result.context
            if serialize_context_exportable_obj and context is not None:
                context = self.serializer.serialize_as_bytes(context)

            suites[suite_name].append(
                {
                    "result": tr.result,
                    "suite_name": suite_name,
                    "serialized_context": context,
                }
            )

        grouped_results.append(
            {
                "name": group_name,
                "statistics": group_stats,
                "suites": suites,
                "results": [
                    {
                        "result": tr.result,
                        "suite_name": tr.suite.name,
                        "serialized_context": self.serializer.serialize_as_bytes(tr.result.context)
                        if serialize_context_exportable_obj and tr.result.context is not None
                        else tr.result.context,
                    }
                    for tr in test_results
                ],
            }
        )

    html_content = template.render(
        groups=grouped_results,
        statistics=ResultsStatistics.from_results([tr.result for tr in results]),
        status_color=STATUS_COLOR,
        render_context=render_context,
        render_description=render_description,
        render_traceback=render_traceback,
        render_message=render_message,
        serialize_context_exportable_obj=serialize_context_exportable_obj,
        version=__version__,
        timestamp=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"),
    )

    self.output_path.write_text(html_content, encoding="utf-8")