Skip to content

Commit cf3300a

Browse files
authored
report generator (#12)
* feat: path issue when loading Jinja template * fix: modify to package loader * feat: Update Jinja template loading to use package loader * update tempalte dir * fix package * feat: add __init__.py for templates * fix directory * feat: add default templates
1 parent 541d29f commit cf3300a

File tree

7 files changed

+249
-9
lines changed

7 files changed

+249
-9
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ vendor
164164
*.lcov
165165
dist*
166166

167-
*.html
168167
*.log
169168
logs/*
170169
*.db

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
include src/mutahunter/core/queries/*.scm
2-
include src/mutahunter/core/html/*.html
2+
include src/mutahunter/core/templates/*.html

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ authors = [{ name = "Steven Jung" }]
2626
maintainers = [{ name = "Steven Jung" }]
2727

2828
[project.urls]
29-
Homepage = "https://www.mutahunter.ai"
29+
Homepage = "https://mutahunter.ai"
3030
Repository = "https://github.com/codeintegrity-ai/mutahunter"
3131

3232
[project.optional-dependencies]
@@ -39,5 +39,5 @@ mutahunter = "mutahunter.main:run"
3939
[tool.setuptools.package-data]
4040
mutahunter = [
4141
'src/mutahunter/core/queries/*.scm',
42-
'src/mutahunter/core/html/*.html',
42+
'src/mutahunter/core/templates/*.html',
4343
]

src/mutahunter/core/report.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import os
66
from typing import Any, Dict, List
77

8-
from jinja2 import Environment, FileSystemLoader
8+
from jinja2 import (
9+
Environment,
10+
PackageLoader,
11+
FileSystemLoader,
12+
select_autoescape,
13+
)
914
from importlib import resources
10-
1115
from mutahunter.core.db import MutationDatabase
1216
from mutahunter.core.logger import logger
1317

@@ -24,10 +28,12 @@ class MutantReport:
2428
def __init__(self, db: MutationDatabase) -> None:
2529
self.log_file = "logs/_latest/coverage.txt"
2630
self.db = db
27-
module_dir = os.path.dirname(__file__)
28-
templates_dir = os.path.join(module_dir, "html")
29-
self.template_env = Environment(loader=FileSystemLoader(templates_dir))
3031
os.makedirs("logs/_latest/html", exist_ok=True)
32+
self.template_env = Environment(
33+
loader=FileSystemLoader(resources.files(__package__).joinpath("templates"))
34+
)
35+
assert self.template_env.get_template("report_template.html")
36+
assert self.template_env.get_template("file_detail_template.html")
3137

3238
def generate_report(self, total_cost: float, line_rate: float) -> None:
3339
"""

src/mutahunter/core/templates/__init__.py

Whitespace-only changes.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>{{ file_name }} - MutaHunter Report</title>
7+
<style>
8+
body {
9+
font-family: Arial, sans-serif;
10+
line-height: 1.6;
11+
padding: 20px;
12+
color: #333;
13+
background: #f0f0f0;
14+
}
15+
h1 {
16+
color: #333;
17+
}
18+
.source-code {
19+
font-family: 'Courier New', monospace;
20+
background-color: #fff;
21+
padding: 10px;
22+
border-radius: 5px;
23+
box-shadow: 0 2px 15px rgba(0,0,0,0.1);
24+
}
25+
.line {
26+
display: flex;
27+
align-items: flex-start;
28+
padding: 4px 2px;
29+
position: relative;
30+
}
31+
.line-number, .mutation-links {
32+
width: 50px;
33+
flex-shrink: 0;
34+
text-align: right;
35+
padding-right: 10px;
36+
color: #666;
37+
font-size: 14px;
38+
}
39+
.mutation-links {
40+
flex-shrink: 0;
41+
padding-left: 5px;
42+
text-align: left;
43+
}
44+
.mutation-link {
45+
color: #2196F3;
46+
font-weight: bold;
47+
margin-right: 10px;
48+
}
49+
.code {
50+
flex: 1;
51+
white-space: pre-wrap; /* respects indentation inside code */
52+
}
53+
.survived {
54+
background-color: #ffcccb;
55+
}
56+
.killed {
57+
background-color: #90ee90;
58+
}
59+
.mutation-details {
60+
background-color: #fff;
61+
padding: 15px;
62+
margin-top: 20px;
63+
border: 1px solid #ccc;
64+
border-radius: 5px;
65+
box-shadow: 0 2px 15px rgba(0,0,0,0.1);
66+
}
67+
.mutation-details ol {
68+
list-style-type: none;
69+
padding: 0;
70+
margin-left: 0;
71+
}
72+
.mutation-details li {
73+
margin: 10px 0;
74+
}
75+
.footer {
76+
margin-top: 30px;
77+
text-align: center;
78+
}
79+
.footer a {
80+
text-decoration: none;
81+
background-color: #2196F3;
82+
color: white;
83+
padding: 10px 15px;
84+
border-radius: 5px;
85+
}
86+
code {
87+
background-color: #f4f4f4;
88+
border-radius: 5px;
89+
padding: 2px 6px;
90+
font-size: 90%;
91+
}
92+
</style>
93+
</head>
94+
<body>
95+
<h1>{{ file_name }}</h1>
96+
<div class="source-code">
97+
{% for line in source_lines %}
98+
<div class="line {% if line.mutations %}{{ 'survived' if line.mutations[0].status == 'SURVIVED' else 'killed' }}{% endif %}">
99+
<span class="line-number">{{ loop.index }}</span>
100+
<span class="mutation-links">
101+
{% for mutation in line.mutations %}
102+
<a href="#mutation-details-{{ mutation.id }}" class="mutation-link">M{{ mutation.id }}</a>
103+
{% endfor %}
104+
</span>
105+
<span class="code">{{ line.code | e }}</span>
106+
</div>
107+
{% endfor %}
108+
</div>
109+
<div class="mutation-details" id="mutation-details">
110+
<h2>Mutation Details</h2>
111+
<ol>
112+
{% for line in source_lines %}
113+
{% for mutation in line.mutations %}
114+
<li id="mutation-details-{{ mutation.id }}" class="{{ 'survived' if mutation.status == 'SURVIVED' else 'killed' }}">
115+
<strong>M{{ mutation.id }}</strong>:
116+
<ul>
117+
<li><strong>Type:</strong> <span class="mutation-type">{{ mutation.type }}</span></li>
118+
<li><strong>Modification:</strong> Changed <code>{{ mutation.original_code }}</code> to <code>{{ mutation.mutated_code }}</code></li>
119+
<li><strong>Status:</strong> {{ 'Survived' if mutation.status == 'SURVIVED' else 'Killed' }}</li>
120+
<li><strong>Impact:</strong> {{ mutation.description }}</li>
121+
{% if mutation.error_msg %}
122+
<!-- <li><strong>Error:</strong> {{mutation.error_msg}} </li>
123+
{% endif %} -->
124+
</ul>
125+
</li>
126+
{% endfor %}
127+
{% endfor %}
128+
</ol>
129+
</div>
130+
<div class="footer">
131+
<a href="mutation_report.html">Back to Summary</a>
132+
</div>
133+
</body>
134+
</html>
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Mutahunter Report</title>
7+
<style>
8+
body { font-family: Arial, sans-serif; line-height: 1.6; padding: 20px; }
9+
h1 { color: #333; }
10+
.summary, .file-list { margin-bottom: 30px; }
11+
table { border-collapse: collapse; width: 100%; }
12+
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
13+
th { background-color: #f2f2f2; }
14+
.progress-bar { background-color: #e0e0e0; height: 20px; border-radius: 10px; }
15+
.progress-bar-fill { height: 100%; border-radius: 10px; }
16+
.summary {
17+
background: #fff;
18+
padding: 15px;
19+
border-radius: 8px;
20+
box-shadow: 0 2px 16px rgba(0,0,0,0.1);
21+
margin-top: 20px;
22+
}
23+
24+
.summary h2 {
25+
color: #333;
26+
margin-bottom: 20px;
27+
}
28+
29+
.metrics > div {
30+
margin-bottom: 10px;
31+
display: flex;
32+
align-items: center;
33+
}
34+
35+
.metrics label {
36+
flex: 1;
37+
font-weight: bold;
38+
}
39+
40+
.progress-bar {
41+
flex: 2;
42+
height: 20px;
43+
background-color: #e0e0e0;
44+
border-radius: 10px;
45+
overflow: hidden;
46+
margin: 10px 10px;
47+
}
48+
49+
.progress-bar-fill {
50+
height: 100%;
51+
border-radius: 10px;
52+
transition: width 0.3s ease-in-out;
53+
}
54+
55+
.metrics span {
56+
min-width: 50px;
57+
text-align: right;
58+
}
59+
</style>
60+
</head>
61+
<body>
62+
<h1>Mutahunter Mutation Testing Report</h1>
63+
<div class="summary">
64+
<h2>Overall Summary</h2>
65+
<p>Line Coverage: {{ line_coverage }}%</p>
66+
<div class="progress-bar">
67+
<div class="progress-bar-fill" style="width: {{ line_coverage }}
68+
%; background-color: #2196F3;"></div>
69+
</div>
70+
<p>Mutation Coverage: {{ mutation_coverage }}%</p>
71+
<div class="progress-bar">
72+
<div class="progress-bar-fill" style="width: {{ mutation_coverage }}%; background-color: #2196F3;"></div>
73+
</div>
74+
<div><label>Total Mutants:</label> {{ total_mutants }}</div>
75+
<div><label>Killed Mutants:</label> {{ killed_mutants }}</div>
76+
<div><label>Survived Mutants:</label> {{ survived_mutants }}</div>
77+
<div><label>Timeout Mutants:</label> {{ timeout_mutants }}</div>
78+
<div><label>Compile Error Mutants:</label> {{ compile_error_mutants }}</div>
79+
<div><label>Total Cost:</label> ${{ total_cost }} USD</div>
80+
</div>
81+
<div class="file-list">
82+
<h2>File List</h2>
83+
<table>
84+
<tr>
85+
<th>File Name</th>
86+
<th>Mutants</th>
87+
<th>Mutation Coverage</th>
88+
<th>Survived Mutants</th>
89+
</tr>
90+
{% for file in file_data %}
91+
<tr>
92+
<td><a href="{{ file.id }}.html">{{ file.name }}</a></td>
93+
<td>{{ file.totalMutants }}</td>
94+
<td>{{ file.mutationCoverage }}%</td>
95+
<td>{{ file.survivedMutants }}</td>
96+
</tr>
97+
{% endfor %}
98+
</table>
99+
</div>
100+
</body>
101+
</html>

0 commit comments

Comments
 (0)