|
43 | 43 |
|
44 | 44 | import pytest |
45 | 45 |
|
46 | | -SUPPORTED_FORMATS = {'html', 'json'} |
| 46 | +from pytest_mpl.summary.html import generate_summary_basic_html, generate_summary_html |
| 47 | + |
| 48 | +SUPPORTED_FORMATS = {'html', 'json', 'basic-html'} |
47 | 49 |
|
48 | 50 | SHAPE_MISMATCH_ERROR = """Error: Image dimensions did not match. |
49 | 51 | Expected shape: {expected_shape} |
50 | 52 | {expected_path} |
51 | 53 | Actual shape: {actual_shape} |
52 | 54 | {actual_path}""" |
53 | 55 |
|
54 | | -HTML_INTRO = """ |
55 | | -<!DOCTYPE html> |
56 | | -<html> |
57 | | -<head> |
58 | | -<style> |
59 | | -table, th, td { |
60 | | - border: 1px solid black; |
61 | | -} |
62 | | -.summary > div { |
63 | | - padding: 0.5em; |
64 | | -} |
65 | | -tr.passed .status, .rms.passed, .hashes.passed { |
66 | | - color: green; |
67 | | -} |
68 | | -tr.failed .status, .rms.failed, .hashes.failed { |
69 | | - color: red; |
70 | | -} |
71 | | -</style> |
72 | | -</head> |
73 | | -<body> |
74 | | -<h2>Image test comparison</h2> |
75 | | -%summary% |
76 | | -<table> |
77 | | - <tr> |
78 | | - <th>Test Name</th> |
79 | | - <th>Baseline image</th> |
80 | | - <th>Diff</th> |
81 | | - <th>New image</th> |
82 | | - </tr> |
83 | | -""" |
84 | | - |
85 | 56 |
|
86 | 57 | def _download_file(baseline, filename): |
87 | 58 | # Note that baseline can be a comma-separated list of URLs that we can |
@@ -162,7 +133,7 @@ def pytest_addoption(parser): |
162 | 133 | group.addoption('--mpl-generate-summary', action='store', |
163 | 134 | help="Generate a summary report of any failed tests" |
164 | 135 | ", in --mpl-results-path. The type of the report should be " |
165 | | - "specified. Supported types are `html` and `json`. " |
| 136 | + "specified. Supported types are `html`, `json` and `basic-html`. " |
166 | 137 | "Multiple types can be specified separated by commas.") |
167 | 138 |
|
168 | 139 | 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): |
712 | 683 | else: |
713 | 684 | item.obj = item_function_wrapper |
714 | 685 |
|
715 | | - def generate_stats(self): |
716 | | - """ |
717 | | - Generate a dictionary of summary statistics. |
718 | | - """ |
719 | | - stats = {'passed': 0, 'failed': 0, 'passed_baseline': 0, 'failed_baseline': 0, 'skipped': 0} |
720 | | - for test in self._test_results.values(): |
721 | | - if test['status'] == 'passed': |
722 | | - stats['passed'] += 1 |
723 | | - if test['rms'] is not None: |
724 | | - stats['failed_baseline'] += 1 |
725 | | - elif test['status'] == 'failed': |
726 | | - stats['failed'] += 1 |
727 | | - if test['rms'] is None: |
728 | | - stats['passed_baseline'] += 1 |
729 | | - elif test['status'] == 'skipped': |
730 | | - stats['skipped'] += 1 |
731 | | - else: |
732 | | - raise ValueError(f"Unknown test status '{test['status']}'.") |
733 | | - self._test_stats = stats |
734 | | - |
735 | | - def generate_summary_html(self): |
736 | | - """ |
737 | | - Generate a simple HTML table of the failed test results |
738 | | - """ |
739 | | - html_file = self.results_dir / 'fig_comparison.html' |
740 | | - with open(html_file, 'w') as f: |
741 | | - |
742 | | - passed = f"{self._test_stats['passed']} passed" |
743 | | - if self._test_stats['failed_baseline'] > 0: |
744 | | - passed += (" hash comparison, although " |
745 | | - f"{self._test_stats['failed_baseline']} " |
746 | | - "of those have a different baseline image") |
747 | | - |
748 | | - failed = f"{self._test_stats['failed']} failed" |
749 | | - if self._test_stats['passed_baseline'] > 0: |
750 | | - failed += (" hash comparison, although " |
751 | | - f"{self._test_stats['passed_baseline']} " |
752 | | - "of those have a matching baseline image") |
753 | | - |
754 | | - f.write(HTML_INTRO.replace('%summary%', f'<p>{passed}.</p><p>{failed}.</p>')) |
755 | | - |
756 | | - for test_name in sorted(self._test_results.keys()): |
757 | | - summary = self._test_results[test_name] |
758 | | - |
759 | | - if not self.results_always and summary['result_image'] is None: |
760 | | - continue # Don't show test if no result image |
761 | | - |
762 | | - if summary['rms'] is None and summary['tolerance'] is not None: |
763 | | - rms = (f'<div class="rms passed">\n' |
764 | | - f' <strong>RMS:</strong> ' |
765 | | - f' < <span class="tolerance">{summary["tolerance"]}</span>\n' |
766 | | - f'</div>') |
767 | | - elif summary['rms'] is not None: |
768 | | - rms = (f'<div class="rms failed">\n' |
769 | | - f' <strong>RMS:</strong> ' |
770 | | - f' <span class="rms">{summary["rms"]}</span>\n' |
771 | | - f'</div>') |
772 | | - else: |
773 | | - rms = '' |
774 | | - |
775 | | - hashes = '' |
776 | | - if summary['baseline_hash'] is not None: |
777 | | - hashes += (f' <div class="baseline">Baseline: ' |
778 | | - f'{summary["baseline_hash"]}</div>\n') |
779 | | - if summary['result_hash'] is not None: |
780 | | - hashes += (f' <div class="result">Result: ' |
781 | | - f'{summary["result_hash"]}</div>\n') |
782 | | - if len(hashes) > 0: |
783 | | - if summary["baseline_hash"] == summary["result_hash"]: |
784 | | - hash_result = 'passed' |
785 | | - else: |
786 | | - hash_result = 'failed' |
787 | | - hashes = f'<div class="hashes {hash_result}">\n{hashes}</div>' |
788 | | - |
789 | | - images = {} |
790 | | - for image_type in ['baseline_image', 'diff_image', 'result_image']: |
791 | | - if summary[image_type] is not None: |
792 | | - images[image_type] = f'<img src="{summary[image_type]}" />' |
793 | | - else: |
794 | | - images[image_type] = '' |
795 | | - |
796 | | - f.write(f'<tr class="{summary["status"]}">\n' |
797 | | - ' <td>\n' |
798 | | - ' <div class="summary">\n' |
799 | | - f' <div class="test-name">{test_name}</div>\n' |
800 | | - f' <div class="status">{summary["status"]}</div>\n' |
801 | | - f' {rms}{hashes}\n' |
802 | | - ' </td>\n' |
803 | | - f' <td>{images["baseline_image"]}</td>\n' |
804 | | - f' <td>{images["diff_image"]}</td>\n' |
805 | | - f' <td>{images["result_image"]}</td>\n' |
806 | | - '</tr>\n\n') |
807 | | - |
808 | | - f.write('</table>\n') |
809 | | - f.write('</body>\n') |
810 | | - f.write('</html>') |
811 | | - |
812 | | - return html_file |
813 | | - |
814 | 686 | def generate_summary_json(self): |
815 | 687 | json_file = self.results_dir / 'results.json' |
816 | 688 | with open(json_file, 'w') as f: |
@@ -843,13 +715,14 @@ def pytest_unconfigure(self, config): |
843 | 715 | if self._test_results[test_name][image_type] == '%EXISTS%': |
844 | 716 | self._test_results[test_name][image_type] = str(directory / filename) |
845 | 717 |
|
846 | | - self.generate_stats() |
847 | | - |
848 | 718 | if 'json' in self.generate_summary: |
849 | 719 | summary = self.generate_summary_json() |
850 | 720 | print(f"A JSON report can be found at: {summary}") |
851 | 721 | if 'html' in self.generate_summary: |
852 | | - summary = self.generate_summary_html() |
| 722 | + summary = generate_summary_html(self._test_results, self.results_dir) |
| 723 | + print(f"A summary of the failed tests can be found at: {summary}") |
| 724 | + if 'basic-html' in self.generate_summary: |
| 725 | + summary = generate_summary_basic_html(self._test_results, self.results_dir) |
853 | 726 | print(f"A summary of the failed tests can be found at: {summary}") |
854 | 727 |
|
855 | 728 |
|
|
0 commit comments