@@ -130,11 +130,13 @@ def get_tests(self) -> T.List[str]:
130
130
def get_first_failed (self ) -> T .Union [None , str ]:
131
131
return next (iter (o ['id' ] for o in self ._results ['collect' ] + self ._results ['run' ] if o ['outcome' ] == 'failed' ), None )
132
132
133
- def get_module (self , testid : str ) -> str :
134
- return testid .split ('::' )[0 ]
135
133
136
- def is_module (self , testid : str ) -> bool :
137
- return '::' not in testid
134
+ def _get_module (testid : str ) -> str :
135
+ return testid .split ('::' )[0 ]
136
+
137
+
138
+ def _is_module (testid : str ) -> bool :
139
+ return '::' not in testid
138
140
139
141
140
142
def _run_pytest (tests_path : Path , extra_args = (), * ,
@@ -183,6 +185,7 @@ def _bisect_items(items: T.List[str], failing: str, fails: T.Callable[[T.List[st
183
185
while len (items ) > 1 :
184
186
middle = len (items ) // 2
185
187
188
+ bar .refresh () # for when using --trace
186
189
bar .set_postfix ({"remaining" : len (items )})
187
190
bar .update ()
188
191
@@ -200,32 +203,45 @@ def _bisect_items(items: T.List[str], failing: str, fails: T.Callable[[T.List[st
200
203
if len (items ) == 1 and fails ([failing ]):
201
204
items = []
202
205
206
+ bar .refresh () # for when using --trace
203
207
bar .set_postfix ({"remaining" : len (items )})
204
208
bar .update ()
205
209
206
210
return items
207
211
208
212
209
- def _reduce_tests (tests_path : Path , tests : T .List [str ], failing_test : str ,
213
+ def _reduce_tests (tests_path : Path , tests : T .List [str ], failing_test : str , modules : T . List [ str ],
210
214
* , trace : bool = False , pytest_args : T .List [str ] = ()) -> T .List [str ]:
211
215
def fails (test_set : T .List [str ]):
212
216
trial = _run_pytest (tests_path , (* pytest_args , '--continue-on-collection-errors' ),
213
- tests = test_set , trace = trace )
217
+ tests = test_set , modules = modules , trace = trace )
214
218
return trial .get_outcome (failing_test ) == 'failed'
215
219
216
- with tqdm .tqdm (desc = "Trying to reduce tests....." , total = math .ceil (math .log (len (tests ), 2 ))) as bar :
220
+ module_set = {* modules }
221
+ tests = [t for t in tests if t != failing_test and _get_module (t ) in module_set ]
222
+ if not tests :
223
+ return tests
224
+
225
+ steps = math .ceil (math .log (len (tests ), 2 ))
226
+ with tqdm .tqdm (desc = "Trying to reduce tests....." , total = steps ) as bar :
217
227
return _bisect_items (tests , failing_test , fails , bar = bar )
218
228
219
229
220
- def _reduce_modules (tests_path : Path , tests : T .List [str ], failing_test : str ,
230
+ def _reduce_modules (tests_path : Path , tests : T .List [str ], failing_id : str ,
221
231
modules : T .List [str ], failing_module : str ,
222
232
* , trace : bool = False , pytest_args : T .List [str ] = ()) -> T .List [str ]:
233
+
223
234
def fails (module_set : T .List [str ]):
224
235
trial = _run_pytest (tests_path , (* pytest_args , '--continue-on-collection-errors' ,),
225
236
tests = tests , modules = module_set , trace = trace )
226
- return trial .get_outcome (failing_test ) == 'failed'
237
+ return trial .get_outcome (failing_id ) == 'failed'
227
238
228
- with tqdm .tqdm (desc = "Trying to reduce modules..." , total = math .ceil (math .log (len (modules ), 2 ))) as bar :
239
+ modules = [m for m in modules if m != failing_module ]
240
+ if not modules :
241
+ return modules
242
+
243
+ steps = math .ceil (math .log (len (modules ), 2 ))
244
+ with tqdm .tqdm (desc = "Trying to reduce modules..." , total = steps ) as bar :
229
245
return _bisect_items (modules , failing_module , fails , bar = bar )
230
246
231
247
@@ -252,53 +268,49 @@ def main():
252
268
print ("Running tests..." , flush = True )
253
269
results = _run_pytest (args .tests_path , (* pytest_args , '-x' ), trace = args .trace )
254
270
255
- failed = results .get_first_failed ()
256
- if failed is None :
271
+ failed_id = results .get_first_failed ()
272
+ if failed_id is None :
257
273
print ("No tests failed!" , flush = True )
258
274
if args .save_to :
259
275
with args .save_to .open ("w" ) as f :
260
276
json .dump ({
261
- 'failed' : failed ,
277
+ 'failed' : failed_id ,
262
278
'error' : 'No tests failed' ,
263
279
}, f )
264
280
return 1
265
281
266
- is_module = results .is_module (failed )
267
-
268
- if is_module :
282
+ failed_is_module = _is_module (failed_id )
283
+ if failed_is_module :
269
284
if args .trace : print ()
270
- print (f"Module \" { failed } \" 's collection failed; trying it by itself..." , flush = True )
271
- failed_module = failed
285
+ print (f"Module \" { failed_id } \" 's collection failed; trying it by itself..." , flush = True )
286
+ failed_module = failed_id
272
287
tests = None
273
288
else :
274
289
if args .trace : print ()
275
- print (f"Test \" { failed } \" failed; trying it by itself..." , flush = True )
276
- failed_module = results . get_module ( failed )
277
- tests = [failed ]
290
+ print (f"Test \" { failed_id } \" failed; trying it by itself..." , flush = True )
291
+ failed_module = _get_module ( failed_id )
292
+ tests = [failed_id ]
278
293
279
294
solo = _run_pytest (args .tests_path , pytest_args , modules = [failed_module ], tests = tests , trace = args .trace )
280
- if solo .get_outcome (failed ) != 'passed' :
295
+ if solo .get_outcome (failed_id ) != 'passed' :
281
296
print ("That also fails by itself!" , flush = True )
282
297
if args .save_to :
283
298
with args .save_to .open ("w" ) as f :
284
299
json .dump ({
285
- 'failed' : failed ,
286
- 'error' : f'{ "Module" if is_module else "Test" } also fails by itself' ,
300
+ 'failed' : failed_id ,
301
+ 'error' : f'{ "Module" if failed_is_module else "Test" } also fails by itself' ,
287
302
}, f )
288
303
return 1
289
304
290
305
tests = results .get_tests ()
291
- if not is_module :
292
- assert tests [- 1 ] == failed
293
- tests = tests [:- 1 ]
294
306
295
- if args .trace : print ()
296
- tests = _reduce_tests (args .tests_path , tests , failed , trace = args .trace , pytest_args = pytest_args )
307
+ if args .trace : print ()
308
+ modules = _reduce_modules (args .tests_path , tests , failed_id , results .get_modules (), failed_module ,
309
+ trace = args .trace , pytest_args = pytest_args )
297
310
298
311
if args .trace : print ()
299
- modules = [m for m in results .get_modules () if m != failed_module ]
300
- modules = _reduce_modules (args .tests_path , tests if is_module else tests + [failed ], failed ,
301
- modules , failed_module , trace = args .trace , pytest_args = pytest_args )
312
+ tests = _reduce_tests (args .tests_path , tests , failed_id , [* modules , failed_module ],
313
+ trace = args .trace , pytest_args = pytest_args )
302
314
303
315
if args .trace : print ()
304
316
print ("Reduced failure set:" )
@@ -309,7 +321,7 @@ def main():
309
321
if args .save_to :
310
322
with args .save_to .open ("w" ) as f :
311
323
json .dump ({
312
- 'failed' : failed ,
324
+ 'failed' : failed_id ,
313
325
'modules' : modules ,
314
326
'tests' : tests ,
315
327
}, f )
0 commit comments