@@ -330,58 +330,81 @@ def check_line(self, output, regex):
330330 self .assertRegex (output , regex )
331331
332332 def parse_executed_tests (self , output ):
333- parser = re .finditer (r'^\[[0-9]+/[0-9]+\] (%s)$' % self .TESTNAME_REGEX ,
334- output ,
335- re .MULTILINE )
336- return set (match .group (1 ) for match in parser )
333+ regex = r'^\[ *[0-9]+(?:/ *[0-9]+)?\] (%s)$' % self .TESTNAME_REGEX
334+ parser = re .finditer (regex , output , re .MULTILINE )
335+ return list (match .group (1 ) for match in parser )
337336
338- def check_executed_tests (self , output , tests , skipped = None ):
337+ def check_executed_tests (self , output , tests , skipped = (), failed = (),
338+ randomize = False ):
339339 if isinstance (tests , str ):
340340 tests = [tests ]
341- executed = self .parse_executed_tests (output )
342- self .assertEqual (executed , set (tests ), output )
341+ if isinstance (skipped , str ):
342+ skipped = [skipped ]
343+ if isinstance (failed , str ):
344+ failed = [failed ]
343345 ntest = len (tests )
344- if skipped :
345- if isinstance (skipped , str ):
346- skipped = [skipped ]
347- nskipped = len (skipped )
348-
349- plural = 's' if nskipped != 1 else ''
350- names = ' ' .join (sorted (skipped ))
351- expected = (r'%s test%s skipped:\n %s$'
352- % (nskipped , plural , names ))
353- self .check_line (output , expected )
354-
355- ok = ntest - nskipped
356- if ok :
357- self .check_line (output , r'%s test OK\.$' % ok )
346+ nskipped = len (skipped )
347+ nfailed = len (failed )
348+
349+ executed = self .parse_executed_tests (output )
350+ if randomize :
351+ self .assertEqual (set (executed ), set (tests ), output )
358352 else :
359- self .check_line (output , r'All %s tests OK\.$' % ntest )
353+ self .assertEqual (executed , tests , output )
354+
355+ def plural (count ):
356+ return 's' if count != 1 else ''
357+
358+ def list_regex (line_format , tests ):
359+ count = len (tests )
360+ names = ' ' .join (sorted (tests ))
361+ regex = line_format % (count , plural (count ))
362+ regex = r'%s:\n %s$' % (regex , names )
363+ return regex
364+
365+ if skipped :
366+ regex = list_regex ('%s test%s skipped' , skipped )
367+ self .check_line (output , regex )
368+
369+ if failed :
370+ regex = list_regex ('%s test%s failed' , failed )
371+ self .check_line (output , regex )
372+
373+ good = ntest - nskipped - nfailed
374+ if good :
375+ regex = r'%s test%s OK\.$' % (good , plural (good ))
376+ if not skipped and not failed and good > 1 :
377+ regex = 'All %s' % regex
378+ self .check_line (output , regex )
360379
361380 def parse_random_seed (self , output ):
362381 match = self .regex_search (r'Using random seed ([0-9]+)' , output )
363382 randseed = int (match .group (1 ))
364383 self .assertTrue (0 <= randseed <= 10000000 , randseed )
365384 return randseed
366385
367- def run_command (self , args , input = None ):
386+ def run_command (self , args , input = None , exitcode = 0 ):
368387 if not input :
369388 input = ''
370- try :
371- return subprocess .run (args ,
372- check = True , universal_newlines = True ,
373- input = input ,
374- stdout = subprocess .PIPE ,
375- stderr = subprocess .PIPE )
376- except subprocess .CalledProcessError as exc :
377- self .fail ("%s\n "
389+ proc = subprocess .run (args ,
390+ universal_newlines = True ,
391+ input = input ,
392+ stdout = subprocess .PIPE ,
393+ stderr = subprocess .PIPE )
394+ if proc .returncode != exitcode :
395+ self .fail ("Command %s failed with exit code %s\n "
378396 "\n "
379397 "stdout:\n "
398+ "---\n "
380399 "%s\n "
400+ "---\n "
381401 "\n "
382402 "stderr:\n "
403+ "---\n "
383404 "%s"
384- % (str (exc ), exc .stdout , exc .stderr ))
405+ "---\n "
406+ % (str (args ), proc .returncode , proc .stdout , proc .stderr ))
407+ return proc
385408
386409
387410 def run_python (self , args , ** kw ):
@@ -411,11 +434,11 @@ def setUp(self):
411434
412435 def check_output (self , output ):
413436 self .parse_random_seed (output )
414- self .check_executed_tests (output , self .tests )
437+ self .check_executed_tests (output , self .tests , randomize = True )
415438
416439 def run_tests (self , args ):
417- stdout = self .run_python (args )
418- self .check_output (stdout )
440+ output = self .run_python (args )
441+ self .check_output (output )
419442
420443 def test_script_regrtest (self ):
421444 # Lib/test/regrtest.py
@@ -492,8 +515,24 @@ class ArgsTestCase(BaseTestCase):
492515 Test arguments of the Python test suite.
493516 """
494517
495- def run_tests (self , * args , input = None ):
496- return self .run_python (['-m' , 'test' , * args ], input = input )
518+ def run_tests (self , * args , ** kw ):
519+ return self .run_python (['-m' , 'test' , * args ], ** kw )
520+
521+ def test_failing_test (self ):
522+ # test a failing test
523+ code = textwrap .dedent ("""
524+ import unittest
525+
526+ class FailingTest(unittest.TestCase):
527+ def test_failing(self):
528+ self.fail("bug")
529+ """ )
530+ test_ok = self .create_test ()
531+ test_failing = self .create_test (code = code )
532+ tests = [test_ok , test_failing ]
533+
534+ output = self .run_tests (* tests , exitcode = 1 )
535+ self .check_executed_tests (output , tests , failed = test_failing )
497536
498537 def test_resources (self ):
499538 # test -u command line option
@@ -572,8 +611,7 @@ def test_coverage(self):
572611 # test --coverage
573612 test = self .create_test ()
574613 output = self .run_tests ("--coverage" , test )
575- executed = self .parse_executed_tests (output )
576- self .assertEqual (executed , {test }, output )
614+ self .check_executed_tests (output , [test ])
577615 regex = ('lines +cov% +module +\(path\)\n '
578616 '(?: *[0-9]+ *[0-9]{1,2}% *[^ ]+ +\([^)]+\)+)+' )
579617 self .check_line (output , regex )
@@ -584,6 +622,23 @@ def test_wait(self):
584622 output = self .run_tests ("--wait" , test , input = 'key' )
585623 self .check_line (output , 'Press any key to continue' )
586624
625+ def test_forever (self ):
626+ # test --forever
627+ code = textwrap .dedent ("""
628+ import unittest
629+
630+ class ForeverTester(unittest.TestCase):
631+ RUN = 1
632+
633+ def test_run(self):
634+ ForeverTester.RUN += 1
635+ if ForeverTester.RUN > 3:
636+ self.fail("fail at the 3rd runs")
637+ """ )
638+ test = self .create_test (code = code )
639+ output = self .run_tests ('--forever' , test , exitcode = 1 )
640+ self .check_executed_tests (output , [test ]* 3 , failed = test )
641+
587642
588643if __name__ == '__main__' :
589644 unittest .main ()
0 commit comments