1616
1717def parse_mutant_line (line ):
1818 """Parse a mutant line and extract key information."""
19- parts = line .strip ().split (':' )
19+ parts = line .strip ().split (":" )
2020 if len (parts ) < 7 :
2121 return None
22-
22+
2323 mutant_id = parts [0 ]
2424 operator = parts [1 ]
25-
25+
2626 # Find the method identifier (format: CLASS@METHOD or just CLASS)
2727 # It's the 5th field (index 4)
2828 method_identifier = parts [4 ]
29-
29+
3030 return {
31- 'id' : mutant_id ,
32- ' operator' : operator ,
33- ' method' : method_identifier ,
34- ' line' : line .strip ()
31+ "id" : mutant_id ,
32+ " operator" : operator ,
33+ " method" : method_identifier ,
34+ " line" : line .strip (),
3535 }
3636
3737
3838def group_mutants_by_method (mutants_file ):
3939 """Group mutants by their method identifier."""
4040 method_mutants = defaultdict (list )
41-
42- with open (mutants_file , 'r' ) as f :
41+
42+ with open (mutants_file , "r" ) as f :
4343 for line in f :
4444 mutant = parse_mutant_line (line )
4545 if mutant :
46- method_mutants [mutant [' method' ]].append (mutant )
47-
46+ method_mutants [mutant [" method" ]].append (mutant )
47+
4848 return method_mutants
4949
5050
5151def select_diverse_mutants (mutants , max_per_method = 3 ):
5252 """
5353 Select a limited number of mutants per method, preferring diversity.
54-
54+
5555 Strategy:
5656 1. Group by mutation operator
5757 2. Select one mutant from each operator type until we hit the limit
5858 3. If we still have room, add more mutants round-robin style
5959 """
6060 if len (mutants ) <= max_per_method :
6161 return mutants
62-
62+
6363 # Group by operator
6464 by_operator = defaultdict (list )
6565 for mutant in mutants :
66- by_operator [mutant [' operator' ]].append (mutant )
67-
66+ by_operator [mutant [" operator" ]].append (mutant )
67+
6868 selected = []
6969 operators = list (by_operator .keys ())
70-
70+
7171 # First pass: select one from each operator type
7272 for op in operators :
7373 if len (selected ) >= max_per_method :
7474 break
7575 selected .append (by_operator [op ][0 ])
76-
76+
7777 # If we still need more and have fewer operators than max_per_method,
7878 # add more mutants round-robin
7979 if len (selected ) < max_per_method :
@@ -89,14 +89,14 @@ def select_diverse_mutants(mutants, max_per_method=3):
8989 # Safety check to avoid infinite loop
9090 if op_index > len (operators ) * max_per_method :
9191 break
92-
92+
9393 return selected
9494
9595
9696def trim_mutants (input_file , output_file , max_per_method = 3 , verbose = False ):
9797 """
9898 Create an exclude list for mutants to reduce redundancy.
99-
99+
100100 Args:
101101 input_file: Path to input mutants.log
102102 output_file: Path to output exclude_mutants.txt
@@ -105,34 +105,34 @@ def trim_mutants(input_file, output_file, max_per_method=3, verbose=False):
105105 """
106106 # Group mutants by method
107107 method_mutants = group_mutants_by_method (input_file )
108-
108+
109109 if verbose :
110110 total_original = sum (len (mutants ) for mutants in method_mutants .values ())
111111 print (f"Original mutants: { total_original } " )
112112 print (f"Methods with mutants: { len (method_mutants )} " )
113113 print (f"Max mutants per method: { max_per_method } " )
114-
114+
115115 # Select mutants to keep
116116 selected_mutants = []
117117 all_mutants = []
118118 for method , mutants in sorted (method_mutants .items ()):
119119 diverse_mutants = select_diverse_mutants (mutants , max_per_method )
120120 selected_mutants .extend (diverse_mutants )
121121 all_mutants .extend (mutants )
122-
122+
123123 if verbose and len (mutants ) > max_per_method :
124124 print (f" { method } : { len (mutants )} -> { len (diverse_mutants )} " )
125-
125+
126126 # Determine which mutants to exclude (all mutants NOT selected)
127- selected_ids = {int (m ['id' ]) for m in selected_mutants }
128- all_ids = {int (m ['id' ]) for m in all_mutants }
127+ selected_ids = {int (m ["id" ]) for m in selected_mutants }
128+ all_ids = {int (m ["id" ]) for m in all_mutants }
129129 excluded_ids = sorted (all_ids - selected_ids )
130-
130+
131131 # Write excluded mutant IDs to file
132- with open (output_file , 'w' ) as f :
132+ with open (output_file , "w" ) as f :
133133 for mutant_id in excluded_ids :
134134 f .write (f"{ mutant_id } \n " )
135-
135+
136136 if verbose :
137137 print (f"\n Mutants to keep: { len (selected_mutants )} " )
138138 print (f"Mutants to exclude: { len (excluded_ids )} " )
@@ -143,43 +143,38 @@ def trim_mutants(input_file, output_file, max_per_method=3, verbose=False):
143143
144144def main ():
145145 parser = argparse .ArgumentParser (
146- description = 'Create an exclude list for redundant mutants from a mutants.log file'
147- )
148- parser .add_argument (
149- 'input_file' ,
150- help = 'Input mutants.log file'
146+ description = "Create an exclude list for redundant mutants from a mutants.log file"
151147 )
148+ parser .add_argument ("input_file" , help = "Input mutants.log file" )
152149 parser .add_argument (
153- '-o' , '--output' ,
154- help = 'Output file (default: exclude_mutants.txt in same directory)' ,
155- default = None
150+ "-o" ,
151+ "--output" ,
152+ help = "Output file (default: exclude_mutants.txt in same directory)" ,
153+ default = None ,
156154 )
157155 parser .add_argument (
158- '-m' , '--max-per-method' ,
156+ "-m" ,
157+ "--max-per-method" ,
159158 type = int ,
160159 default = 3 ,
161- help = ' Maximum number of mutants to keep per method (default: 3)'
160+ help = " Maximum number of mutants to keep per method (default: 3)" ,
162161 )
163- parser .add_argument (
164- '-v' , '--verbose' ,
165- action = 'store_true' ,
166- help = 'Print detailed statistics'
167- )
168-
162+ parser .add_argument ("-v" , "--verbose" , action = "store_true" , help = "Print detailed statistics" )
163+
169164 args = parser .parse_args ()
170-
165+
171166 input_path = Path (args .input_file )
172167 if not input_path .exists ():
173168 print (f"Error: Input file not found: { args .input_file } " , file = sys .stderr )
174169 sys .exit (1 )
175-
170+
176171 if args .output :
177172 output_path = Path (args .output )
178173 else :
179174 output_path = input_path .parent / "exclude_mutants.txt"
180-
175+
181176 trim_mutants (input_path , output_path , args .max_per_method , args .verbose )
182177
183178
184- if __name__ == ' __main__' :
179+ if __name__ == " __main__" :
185180 main ()
0 commit comments