diff --git a/README.rst b/README.rst
index 96db5ca6..a81b8322 100644
--- a/README.rst
+++ b/README.rst
@@ -114,13 +114,24 @@ and the tests will pass if the images are the same. If you omit the
runs, without checking the output images.
-Generating a Failure Summary
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Generating a Test Summary
+^^^^^^^^^^^^^^^^^^^^^^^^^
By specifying the ``--mpl-generate-summary=html`` CLI argument, a HTML summary
-page will be generated showing the baseline, diff and result image for each
-failing test. If no baseline images are configured, just the result images will
-be displayed. (See also, the **Results always** section below.)
+page will be generated showing the result, log entry and RMS of each test,
+and the hashes if configured. The baseline, diff and result image for each
+failing test will be shown. If **Results always** is configured
+(see section below), images for passing tests will also be shown.
+If no baseline images are configured, just the result images will
+be displayed.
+
++---------------+---------------+---------------+
+| |html all| | |html filter| | |html result| |
++---------------+---------------+---------------+
+
+As well as ``html``, ``basic-html`` can be specified for an alternative HTML
+summary which does not rely on JavaScript or external resources. A ``json``
+summary can also be saved. Multiple options can be specified comma-separated.
Options
-------
@@ -301,6 +312,9 @@ install the latest version of the plugin then do::
The reason for having to install the plugin first is to ensure that the
plugin is correctly loaded as part of the test suite.
+.. |html all| image:: images/html_all.png
+.. |html filter| image:: images/html_filter.png
+.. |html result| image:: images/html_result.png
.. |expected| image:: images/baseline-coords_overlay_auto_coord_meta.png
.. |actual| image:: images/coords_overlay_auto_coord_meta.png
.. |diff| image:: images/coords_overlay_auto_coord_meta-failed-diff.png
diff --git a/images/html_all.png b/images/html_all.png
new file mode 100644
index 00000000..82e3eec4
Binary files /dev/null and b/images/html_all.png differ
diff --git a/images/html_filter.png b/images/html_filter.png
new file mode 100644
index 00000000..9fc6f998
Binary files /dev/null and b/images/html_filter.png differ
diff --git a/images/html_result.png b/images/html_result.png
new file mode 100644
index 00000000..d8ef5c44
Binary files /dev/null and b/images/html_result.png differ
diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py
index 425b5314..a972d813 100644
--- a/pytest_mpl/plugin.py
+++ b/pytest_mpl/plugin.py
@@ -43,7 +43,9 @@
import pytest
-SUPPORTED_FORMATS = {'html', 'json'}
+from pytest_mpl.summary.html import generate_summary_basic_html, generate_summary_html
+
+SUPPORTED_FORMATS = {'html', 'json', 'basic-html'}
SHAPE_MISMATCH_ERROR = """Error: Image dimensions did not match.
Expected shape: {expected_shape}
@@ -51,37 +53,6 @@
Actual shape: {actual_shape}
{actual_path}"""
-HTML_INTRO = """
-
-
-
-
-
-
-Image test comparison
-%summary%
-
-
- Test Name |
- Baseline image |
- Diff |
- New image |
-
-"""
-
def _download_file(baseline, filename):
# Note that baseline can be a comma-separated list of URLs that we can
@@ -162,7 +133,7 @@ def pytest_addoption(parser):
group.addoption('--mpl-generate-summary', action='store',
help="Generate a summary report of any failed tests"
", in --mpl-results-path. The type of the report should be "
- "specified. Supported types are `html` and `json`. "
+ "specified. Supported types are `html`, `json` and `basic-html`. "
"Multiple types can be specified separated by commas.")
results_path_help = "directory for test results, relative to location where py.test is run"
@@ -712,105 +683,6 @@ def item_function_wrapper(*args, **kwargs):
else:
item.obj = item_function_wrapper
- def generate_stats(self):
- """
- Generate a dictionary of summary statistics.
- """
- stats = {'passed': 0, 'failed': 0, 'passed_baseline': 0, 'failed_baseline': 0, 'skipped': 0}
- for test in self._test_results.values():
- if test['status'] == 'passed':
- stats['passed'] += 1
- if test['rms'] is not None:
- stats['failed_baseline'] += 1
- elif test['status'] == 'failed':
- stats['failed'] += 1
- if test['rms'] is None:
- stats['passed_baseline'] += 1
- elif test['status'] == 'skipped':
- stats['skipped'] += 1
- else:
- raise ValueError(f"Unknown test status '{test['status']}'.")
- self._test_stats = stats
-
- def generate_summary_html(self):
- """
- Generate a simple HTML table of the failed test results
- """
- html_file = self.results_dir / 'fig_comparison.html'
- with open(html_file, 'w') as f:
-
- passed = f"{self._test_stats['passed']} passed"
- if self._test_stats['failed_baseline'] > 0:
- passed += (" hash comparison, although "
- f"{self._test_stats['failed_baseline']} "
- "of those have a different baseline image")
-
- failed = f"{self._test_stats['failed']} failed"
- if self._test_stats['passed_baseline'] > 0:
- failed += (" hash comparison, although "
- f"{self._test_stats['passed_baseline']} "
- "of those have a matching baseline image")
-
- f.write(HTML_INTRO.replace('%summary%', f'{passed}.
{failed}.
'))
-
- for test_name in sorted(self._test_results.keys()):
- summary = self._test_results[test_name]
-
- if not self.results_always and summary['result_image'] is None:
- continue # Don't show test if no result image
-
- if summary['rms'] is None and summary['tolerance'] is not None:
- rms = (f'\n'
- f' RMS: '
- f' < {summary["tolerance"]}\n'
- f'
')
- elif summary['rms'] is not None:
- rms = (f'\n'
- f' RMS: '
- f' {summary["rms"]}\n'
- f'
')
- else:
- rms = ''
-
- hashes = ''
- if summary['baseline_hash'] is not None:
- hashes += (f' Baseline: '
- f'{summary["baseline_hash"]}
\n')
- if summary['result_hash'] is not None:
- hashes += (f' Result: '
- f'{summary["result_hash"]}
\n')
- if len(hashes) > 0:
- if summary["baseline_hash"] == summary["result_hash"]:
- hash_result = 'passed'
- else:
- hash_result = 'failed'
- hashes = f'\n{hashes}
'
-
- images = {}
- for image_type in ['baseline_image', 'diff_image', 'result_image']:
- if summary[image_type] is not None:
- images[image_type] = f'
'
- else:
- images[image_type] = ''
-
- f.write(f'\n'
- ' \n'
- ' \n'
- f' {test_name} \n'
- f' {summary["status"]} \n'
- f' {rms}{hashes}\n'
- ' | \n'
- f' {images["baseline_image"]} | \n'
- f' {images["diff_image"]} | \n'
- f' {images["result_image"]} | \n'
- '
\n\n')
-
- f.write('
\n')
- f.write('\n')
- f.write('')
-
- return html_file
-
def generate_summary_json(self):
json_file = self.results_dir / 'results.json'
with open(json_file, 'w') as f:
@@ -843,13 +715,14 @@ def pytest_unconfigure(self, config):
if self._test_results[test_name][image_type] == '%EXISTS%':
self._test_results[test_name][image_type] = str(directory / filename)
- self.generate_stats()
-
if 'json' in self.generate_summary:
summary = self.generate_summary_json()
print(f"A JSON report can be found at: {summary}")
if 'html' in self.generate_summary:
- summary = self.generate_summary_html()
+ summary = generate_summary_html(self._test_results, self.results_dir)
+ print(f"A summary of the failed tests can be found at: {summary}")
+ if 'basic-html' in self.generate_summary:
+ summary = generate_summary_basic_html(self._test_results, self.results_dir)
print(f"A summary of the failed tests can be found at: {summary}")
diff --git a/pytest_mpl/summary/__init__.py b/pytest_mpl/summary/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/pytest_mpl/summary/html.py b/pytest_mpl/summary/html.py
new file mode 100644
index 00000000..858cfe21
--- /dev/null
+++ b/pytest_mpl/summary/html.py
@@ -0,0 +1,288 @@
+import os
+import sys
+import shutil
+
+if sys.version_info >= (3, 8):
+ from functools import cached_property
+else:
+ cached_property = property
+
+from jinja2 import Environment, PackageLoader, select_autoescape
+
+__all__ = ['generate_summary_html', 'generate_summary_basic_html']
+
+
+class Results:
+ """
+ Data for the whole test run, used for providing data to the template.
+
+ Parameters
+ ----------
+ results : dict
+ The `pytest_mpl.plugin.ImageComparison._test_results` object.
+ title : str
+ Value for HTML Codestin Search App
+
+
+{% include 'navbar.html' %}
+
+
+
+
+ {% filter indent(width=12) -%}
+ {% include 'result.html' %}
+ {%- endfilter %}
+
+
+
+
+{% include 'filter.html' %}
+
+
+
+
+