@@ -28,46 +28,11 @@ def __init__(self, config: Dict[str, Any]) -> None:
28
28
- only_mutate_file_paths (List[str]): List of specific files to mutate.
29
29
"""
30
30
self .config : Dict [str , Any ] = config
31
- self .config ["language" ] = self .determine_language (config ["test_file_path" ])
32
31
self .mutants : List [Mutant ] = []
33
32
self .mutant_report = MutantReport (config = self .config )
34
33
self .analyzer = Analyzer (self .config )
35
34
self .test_runner = TestRunner ()
36
35
37
- def determine_language (self , filename : str ) -> str :
38
- """
39
- Determines the programming language based on the file extension. For Tree-Sitter language detection.
40
-
41
- Args:
42
- filename (str): The filename to determine the language from.
43
-
44
- Returns:
45
- str: The programming language corresponding to the file extension.
46
-
47
- Raises:
48
- ValueError: If the file extension is not supported.
49
- """
50
- ext = filename .split ("." )[- 1 ]
51
- language_mappings = {
52
- "py" : "python" ,
53
- "java" : "java" ,
54
- "js" : "javascript" ,
55
- "ts" : "typescript" ,
56
- "c" : "c" ,
57
- "cpp" : "cpp" ,
58
- "rs" : "rust" ,
59
- "go" : "go" ,
60
- "php" : "php" ,
61
- "rb" : "ruby" ,
62
- "swift" : "swift" ,
63
- "kt" : "kotlin" ,
64
- "tsx" : "tsx" ,
65
- "ts" : "typescript" ,
66
- }
67
- if ext not in language_mappings :
68
- raise ValueError (f"Unsupported file extension: { ext } " )
69
- return language_mappings [ext ]
70
-
71
36
def run (self ) -> None :
72
37
"""
73
38
Executes the mutation testing process from start to finish.
@@ -109,19 +74,14 @@ def should_skip_file(self, filename: str) -> bool:
109
74
logger .error (f"File { file_path } does not exist." )
110
75
raise FileNotFoundError (f"File { file_path } does not exist." )
111
76
# NOTE: Only mutate the files specified in the config.
112
- for file_path in self .config ["only_mutate_file_paths" ]:
113
- if file_path == filename :
114
- return False
77
+ return all (
78
+ file_path != filename
79
+ for file_path in self .config ["only_mutate_file_paths" ]
80
+ )
81
+ if filename in self .config ["exclude_files" ]:
115
82
return True
116
- else :
117
- if filename in self .config ["exclude_files" ]:
118
- return True
119
- if any (
120
- keyword in filename
121
- for keyword in ["test/" , "tests/" , "test_" , "_test" , ".test" ]
122
- ):
123
- return True
124
- return False
83
+ test_keywords = ["test/" , "tests/" , "test_" , "_test" , ".test" ]
84
+ return any (keyword in filename for keyword in test_keywords )
125
85
126
86
def generate_mutations (self ) -> Generator [Dict [str , Any ], None , None ]:
127
87
"""
@@ -131,47 +91,41 @@ def generate_mutations(self) -> Generator[Dict[str, Any], None, None]:
131
91
Generator[Dict[str, Any], None, None]: Dictionary containing mutation details.
132
92
"""
133
93
all_covered_files = self .analyzer .file_lines_executed .keys ()
134
- for filename in tqdm (all_covered_files ):
135
- if self .should_skip_file (filename ):
94
+ for covered_file_path in tqdm (all_covered_files ):
95
+ if self .should_skip_file (covered_file_path ):
136
96
continue
137
97
covered_function_blocks , covered_function_block_executed_lines = (
138
98
self .analyzer .get_covered_function_blocks (
139
- executed_lines = self .analyzer .file_lines_executed [filename ],
140
- filename = filename ,
99
+ executed_lines = self .analyzer .file_lines_executed [covered_file_path ],
100
+ source_file_path = covered_file_path ,
141
101
)
142
102
)
143
103
logger .info (
144
- f"Total function blocks to mutate: { len (covered_function_blocks )} "
104
+ f"Total function blocks to mutate for { covered_file_path } : { len (covered_function_blocks )} "
145
105
)
146
106
if not covered_function_blocks :
147
107
continue
148
108
149
- with open (filename , "rb" ) as f :
150
- source_code = f .read ()
151
-
152
109
for function_block , executed_lines in zip (
153
110
covered_function_blocks ,
154
111
covered_function_block_executed_lines ,
155
112
):
156
113
start_byte = function_block .start_byte
157
114
end_byte = function_block .end_byte
158
- function_block_source_code = source_code [start_byte :end_byte ].decode (
159
- "utf-8"
160
- )
161
115
162
116
mutant_generator = MutantGenerator (
163
117
config = self .config ,
164
118
executed_lines = executed_lines ,
165
119
cov_files = list (all_covered_files ),
166
120
test_file_path = self .config ["test_file_path" ],
167
- filename = filename ,
168
- function_block_source_code = function_block_source_code ,
169
- language = self . config [ "language" ] ,
121
+ source_file_path = covered_file_path ,
122
+ start_byte = start_byte ,
123
+ end_byte = end_byte ,
170
124
)
171
125
172
- for path , hunk , content in mutant_generator .generate ():
126
+ for _ , hunk , content in mutant_generator .generate ():
173
127
yield {
174
- "source_path" : filename ,
128
+ "source_path" : covered_file_path ,
175
129
"start_byte" : start_byte ,
176
130
"end_byte" : end_byte ,
177
131
"hunk" : hunk ,
@@ -188,7 +142,7 @@ def run_mutation_testing(self) -> None:
188
142
mutant_id = str (len (self .mutants ) + 1 )
189
143
mutant_path = self .prepare_mutant_file (
190
144
mutant_id = mutant_id ,
191
- source_path = mutant_data ["source_path" ],
145
+ source_file_path = mutant_data ["source_path" ],
192
146
start_byte = mutant_data ["start_byte" ],
193
147
end_byte = mutant_data ["end_byte" ],
194
148
mutant_code = mutant_data ["mutant_code_snippet" ],
@@ -215,7 +169,7 @@ def run_mutation_testing(self) -> None:
215
169
def prepare_mutant_file (
216
170
self ,
217
171
mutant_id : str ,
218
- source_path : str ,
172
+ source_file_path : str ,
219
173
start_byte : int ,
220
174
end_byte : int ,
221
175
mutant_code : str ,
@@ -236,12 +190,12 @@ def prepare_mutant_file(
236
190
Raises:
237
191
Exception: If the mutant code has syntax errors.
238
192
"""
239
- mutant_file_name = f"{ mutant_id } _{ os .path .basename (source_path )} "
193
+ mutant_file_name = f"{ mutant_id } _{ os .path .basename (source_file_path )} "
240
194
mutant_path = os .path .join (
241
195
os .getcwd (), f"logs/_latest/mutants/{ mutant_file_name } "
242
196
)
243
197
244
- with open (source_path , "rb" ) as f :
198
+ with open (source_file_path , "rb" ) as f :
245
199
source_code = f .read ()
246
200
247
201
modified_byte_code = (
@@ -250,12 +204,15 @@ def prepare_mutant_file(
250
204
+ source_code [end_byte :]
251
205
)
252
206
253
- if self .analyzer .check_syntax (modified_byte_code .decode ("utf-8" )):
207
+ if self .analyzer .check_syntax (
208
+ source_file_path = source_file_path ,
209
+ source_code = modified_byte_code .decode ("utf-8" ),
210
+ ):
254
211
with open (mutant_path , "wb" ) as f :
255
212
f .write (modified_byte_code )
256
213
return mutant_path
257
214
else :
258
- raise Exception ("Mutant code has syntax errors." )
215
+ raise SyntaxError ("Mutant code has syntax errors." )
259
216
260
217
def run_test (self , params : Dict [str , str ]) -> Any :
261
218
"""
0 commit comments