@@ -22,133 +22,189 @@ class MutantReport:
22
22
23
23
def __init__ (self , config : MutahunterConfig ) -> None :
24
24
self .config = config
25
+ self .log_file = "logs/_latest/coverage.txt"
25
26
26
27
def generate_report (
27
- self ,
28
- mutants : List [Mutant ],
29
- total_cost : float ,
30
- line_rate : float ,
28
+ self , mutants : List [Mutant ], total_cost : float , line_rate : float
31
29
) -> None :
32
30
"""
33
31
Generates a comprehensive mutation testing report.
34
32
35
33
Args:
36
34
mutants (List[Mutant]): List of mutants generated during mutation testing.
35
+ total_cost (float): The total cost of mutation testing.
36
+ line_rate (float): The line coverage rate.
37
37
"""
38
- mutants = [asdict (mutant ) for mutant in mutants ]
39
- self .save_report ("logs/_latest/mutants.json" , mutants )
38
+ mutant_dicts = [asdict (mutant ) for mutant in mutants ]
39
+ self .save_report ("logs/_latest/mutants.json" , mutant_dicts )
40
40
print (MUTAHUNTER_ASCII )
41
- self .generate_mutant_report (mutants , total_cost , line_rate )
42
- self .generate_mutant_report_detail (mutants )
43
-
44
- def generate_mutant_report (
45
- self ,
46
- mutants : List [Mutant ],
47
- total_cost : float ,
48
- line_rate : float ,
41
+ self ._generate_summary_report (mutant_dicts , total_cost , line_rate )
42
+ self ._generate_detailed_report (mutant_dicts )
43
+
44
+ def _generate_summary_report (
45
+ self , mutants : List [dict ], total_cost : float , line_rate : float
49
46
) -> None :
50
- killed_mutants = [mutant for mutant in mutants if mutant ["status" ] == "KILLED" ]
51
- survived_mutants = [
52
- mutant for mutant in mutants if mutant ["status" ] == "SURVIVED"
53
- ]
54
- timeout_mutants = [
55
- mutant for mutant in mutants if mutant ["status" ] == "TIMEOUT"
56
- ]
57
- compile_error_mutants = [
58
- mutant for mutant in mutants if mutant ["status" ] == "COMPILE_ERROR"
59
- ]
60
- valid_mutants = [
61
- m for m in mutants if m ["status" ] not in ["COMPILE_ERROR" , "TIMEOUT" ]
62
- ]
47
+ """
48
+ Generates a summary mutation testing report.
63
49
64
- total_mutation_coverage = (
65
- f"{ len (killed_mutants ) / len (valid_mutants ) * 100 :.2f} %"
66
- if valid_mutants
50
+ Args:
51
+ mutants (List[dict]): List of mutant dictionaries.
52
+ total_cost (float): The total cost of mutation testing.
53
+ line_rate (float): The line coverage rate.
54
+ """
55
+ report_data = self ._compute_summary_data (mutants )
56
+ summary_text = self ._format_summary (report_data , total_cost , line_rate )
57
+ self ._log_and_write (summary_text )
58
+
59
+ def _compute_summary_data (self , mutants : List [dict ]) -> dict :
60
+ """
61
+ Computes summary data from the list of mutants.
62
+
63
+ Args:
64
+ mutants (List[dict]): List of mutant dictionaries.
65
+
66
+ Returns:
67
+ dict: Summary data including counts of different mutant statuses.
68
+ """
69
+ data = {
70
+ "killed_mutants" : len ([m for m in mutants if m ["status" ] == "KILLED" ]),
71
+ "survived_mutants" : len ([m for m in mutants if m ["status" ] == "SURVIVED" ]),
72
+ "timeout_mutants" : len ([m for m in mutants if m ["status" ] == "TIMEOUT" ]),
73
+ "compile_error_mutants" : len (
74
+ [m for m in mutants if m ["status" ] == "COMPILE_ERROR" ]
75
+ ),
76
+ }
77
+ data ["total_mutants" ] = len (mutants )
78
+ data ["valid_mutants" ] = (
79
+ data ["total_mutants" ]
80
+ - data ["compile_error_mutants" ]
81
+ - data ["timeout_mutants" ]
82
+ )
83
+ data ["mutation_coverage" ] = (
84
+ f"{ data ['killed_mutants' ] / data ['valid_mutants' ] * 100 :.2f} %"
85
+ if data ["valid_mutants" ]
67
86
else "0.00%"
68
87
)
69
- line_coverage = f"{ line_rate * 100 :.2f} %"
88
+ return data
89
+
90
+ def _format_summary (self , data : dict , total_cost : float , line_rate : float ) -> str :
91
+ """
92
+ Formats the summary data into a string.
70
93
71
- logger .info ("π Line Coverage: %s π" , line_coverage )
72
- logger .info ("π― Mutation Coverage: %s π―" , total_mutation_coverage )
73
- logger .info ("π¦ Total Mutants: %d π¦ " , len (mutants ))
74
- logger .info ("π‘οΈ Survived Mutants: %d π‘οΈ" , len (survived_mutants ))
75
- logger .info ("π‘οΈ Killed Mutants: %d π‘οΈ" , len (killed_mutants ))
76
- logger .info ("π Timeout Mutants: %d π" , len (timeout_mutants ))
77
- logger .info ("π₯ Compile Error Mutants: %d π₯" , len (compile_error_mutants ))
94
+ Args:
95
+ data (dict): Summary data including counts of different mutant statuses.
96
+ total_cost (float): The total cost of mutation testing.
97
+ line_rate (float): The line coverage rate.
98
+
99
+ Returns:
100
+ str: Formatted summary report.
101
+ """
102
+ line_coverage = f"{ line_rate * 100 :.2f} %"
103
+ summary = [
104
+ "Mutation Coverage:" ,
105
+ f"π Line Coverage: { line_coverage } π" ,
106
+ f"π― Mutation Coverage: { data ['mutation_coverage' ]} π―" ,
107
+ f"π¦ Total Mutants: { data ['total_mutants' ]} π¦ " ,
108
+ f"π‘οΈ Survived Mutants: { data ['survived_mutants' ]} π‘οΈ" ,
109
+ f"π‘οΈ Killed Mutants: { data ['killed_mutants' ]} π‘οΈ" ,
110
+ f"π Timeout Mutants: { data ['timeout_mutants' ]} π" ,
111
+ f"π₯ Compile Error Mutants: { data ['compile_error_mutants' ]} π₯" ,
112
+ ]
78
113
if self .config .extreme :
79
- logger . info ("π° No Cost for extreme mutation testing π°" )
114
+ summary . append ("π° No Cost for extreme mutation testing π°" )
80
115
else :
81
- logger .info ("π° Expected Cost: $%.5f USD π°" , total_cost )
82
-
83
- with open ("logs/_latest/coverage.txt" , "a" ) as file :
84
- file .write ("Mutation Coverage:\n " )
85
- file .write (f"π Line Coverage: { line_coverage } π\n " )
86
- file .write (f"π― Mutation Coverage: { total_mutation_coverage } π―\n " )
87
- file .write (f"π¦ Total Mutants: { len (mutants )} π¦ \n " )
88
- file .write (f"π‘οΈ Survived Mutants: { len (survived_mutants )} π‘οΈ\n " )
89
- file .write (f"π‘οΈ Killed Mutants: { len (killed_mutants )} π‘οΈ\n " )
90
- file .write (f"π Timeout Mutants: { len (timeout_mutants )} π\n " )
91
- file .write (f"π₯ Compile Error Mutants: { len (compile_error_mutants )} π₯\n " )
92
- if self .config .extreme :
93
- file .write ("π° No Cost for extreme mutation testing π°\n " )
94
- else :
95
- file .write ("π° Expected Cost: $%.5f USD π°\n " , total_cost )
96
-
97
- def generate_mutant_report_detail (self , mutants : List [Mutant ]) -> None :
116
+ summary .append (f"π° Expected Cost: ${ total_cost :.5f} USD π°" )
117
+ return "\n " .join (summary )
118
+
119
+ def _generate_detailed_report (self , mutants : List [dict ]) -> None :
98
120
"""
99
121
Generates a detailed mutation testing report per source file.
100
122
101
123
Args:
102
- mutants (List[Mutant]): List of mutants generated during mutation testing.
124
+ mutants (List[dict]): List of mutant dictionaries.
125
+ """
126
+ report_detail = self ._compute_detailed_data (mutants )
127
+ detailed_text = self ._format_detailed_report (report_detail )
128
+ self ._log_and_write ("\n Detailed Mutation Coverage:\n " + detailed_text )
129
+
130
+ def _compute_detailed_data (self , mutants : List [dict ]) -> dict :
131
+ """
132
+ Computes detailed data for each source file from the list of mutants.
133
+
134
+ Args:
135
+ mutants (List[dict]): List of mutant dictionaries.
136
+
137
+ Returns:
138
+ dict: Detailed data including counts of different mutant statuses per source file.
103
139
"""
104
- report_detail = {}
140
+ detail = {}
105
141
for mutant in mutants :
106
142
source_path = mutant ["source_path" ]
107
- if source_path not in report_detail :
108
- report_detail [source_path ] = {
143
+ if source_path not in detail :
144
+ detail [source_path ] = {
109
145
"total_mutants" : 0 ,
110
146
"killed_mutants" : 0 ,
111
147
"survived_mutants" : 0 ,
112
148
"timeout_mutants" : 0 ,
113
149
"compile_error_mutants" : 0 ,
114
150
}
115
- report_detail [source_path ]["total_mutants" ] += 1
151
+ detail [source_path ]["total_mutants" ] += 1
116
152
if mutant ["status" ] == "KILLED" :
117
- report_detail [source_path ]["killed_mutants" ] += 1
153
+ detail [source_path ]["killed_mutants" ] += 1
118
154
elif mutant ["status" ] == "SURVIVED" :
119
- report_detail [source_path ]["survived_mutants" ] += 1
155
+ detail [source_path ]["survived_mutants" ] += 1
120
156
elif mutant ["status" ] == "TIMEOUT" :
121
- report_detail [source_path ]["timeout_mutants" ] += 1
122
-
157
+ detail [source_path ]["timeout_mutants" ] += 1
123
158
elif mutant ["status" ] == "COMPILE_ERROR" :
124
- report_detail [source_path ]["compile_error_mutants" ] += 1
159
+ detail [source_path ]["compile_error_mutants" ] += 1
125
160
126
- for source_path , detail in report_detail .items ():
161
+ for source_path , data in detail .items ():
127
162
valid_mutants = (
128
- detail ["total_mutants" ]
129
- - detail ["compile_error_mutants" ]
130
- - detail ["timeout_mutants" ]
163
+ data ["total_mutants" ]
164
+ - data ["compile_error_mutants" ]
165
+ - data ["timeout_mutants" ]
131
166
)
132
- mutation_coverage = (
133
- f"{ detail ['killed_mutants' ] / valid_mutants * 100 :.2f} %"
167
+ data [ " mutation_coverage" ] = (
168
+ f"{ data ['killed_mutants' ] / valid_mutants * 100 :.2f} %"
134
169
if valid_mutants
135
170
else "0.00%"
136
171
)
137
- detail ["mutation_coverage" ] = mutation_coverage
138
-
139
- with open ("logs/_latest/coverage.txt" , "a" ) as file :
140
- file .write ("\n Detailed Mutation Coverage:\n " )
141
- for source_path , detail in report_detail .items ():
142
- file .write (f"π Source File: { source_path } π\n " )
143
- file .write (f"π― Mutation Coverage: { detail ['mutation_coverage' ]} π―\n " )
144
- file .write (f"π¦ Total Mutants: { detail ['total_mutants' ]} π¦ \n " )
145
- file .write (f"π‘οΈ Survived Mutants: { detail ['survived_mutants' ]} π‘οΈ\n " )
146
- file .write (f"π‘οΈ Killed Mutants: { detail ['killed_mutants' ]} π‘οΈ\n " )
147
- file .write (f"π Timeout Mutants: { detail ['timeout_mutants' ]} π\n " )
148
- file .write (
149
- f"π₯ Compile Error Mutants: { detail ['compile_error_mutants' ]} π₯\n "
150
- )
151
- file .write ("\n " )
172
+ return detail
173
+
174
+ def _format_detailed_report (self , report_detail : dict ) -> str :
175
+ """
176
+ Formats the detailed report data into a string.
177
+
178
+ Args:
179
+ report_detail (dict): Detailed data including counts of different mutant statuses per source file.
180
+
181
+ Returns:
182
+ str: Formatted detailed report.
183
+ """
184
+ details = []
185
+ for source_path , detail in report_detail .items ():
186
+ details .append (f"π Source File: { source_path } π" )
187
+ details .append (f"π― Mutation Coverage: { detail ['mutation_coverage' ]} π―" )
188
+ details .append (f"π¦ Total Mutants: { detail ['total_mutants' ]} π¦ " )
189
+ details .append (f"π‘οΈ Survived Mutants: { detail ['survived_mutants' ]} π‘οΈ" )
190
+ details .append (f"π‘οΈ Killed Mutants: { detail ['killed_mutants' ]} π‘οΈ" )
191
+ details .append (f"π Timeout Mutants: { detail ['timeout_mutants' ]} π" )
192
+ details .append (
193
+ f"π₯ Compile Error Mutants: { detail ['compile_error_mutants' ]} π₯"
194
+ )
195
+ details .append ("\n " )
196
+ return "\n " .join (details )
197
+
198
+ def _log_and_write (self , text : str ) -> None :
199
+ """
200
+ Logs and writes the given text to a file.
201
+
202
+ Args:
203
+ text (str): The text to log and write.
204
+ """
205
+ logger .info (text )
206
+ with open (self .log_file , "a" ) as file :
207
+ file .write (text + "\n " )
152
208
153
209
def save_report (self , filepath : str , data : Any ) -> None :
154
210
"""
0 commit comments