@@ -377,19 +377,19 @@ def parse_executed_tests(self, output):
377377 return list (match .group (1 ) for match in parser )
378378
379379 def check_executed_tests (self , output , tests , skipped = (), failed = (),
380- omitted = (), randomize = False , interrupted = False ):
380+ env_changed = (), omitted = (),
381+ randomize = False , interrupted = False ,
382+ fail_env_changed = False ):
381383 if isinstance (tests , str ):
382384 tests = [tests ]
383385 if isinstance (skipped , str ):
384386 skipped = [skipped ]
385387 if isinstance (failed , str ):
386388 failed = [failed ]
389+ if isinstance (env_changed , str ):
390+ env_changed = [env_changed ]
387391 if isinstance (omitted , str ):
388392 omitted = [omitted ]
389- ntest = len (tests )
390- nskipped = len (skipped )
391- nfailed = len (failed )
392- nomitted = len (omitted )
393393
394394 executed = self .parse_executed_tests (output )
395395 if randomize :
@@ -415,11 +415,17 @@ def list_regex(line_format, tests):
415415 regex = list_regex ('%s test%s failed' , failed )
416416 self .check_line (output , regex )
417417
418+ if env_changed :
419+ regex = list_regex ('%s test%s altered the execution environment' ,
420+ env_changed )
421+ self .check_line (output , regex )
422+
418423 if omitted :
419424 regex = list_regex ('%s test%s omitted' , omitted )
420425 self .check_line (output , regex )
421426
422- good = ntest - nskipped - nfailed - nomitted
427+ good = (len (tests ) - len (skipped ) - len (failed )
428+ - len (omitted ) - len (env_changed ))
423429 if good :
424430 regex = r'%s test%s OK\.$' % (good , plural (good ))
425431 if not skipped and not failed and good > 1 :
@@ -429,10 +435,12 @@ def list_regex(line_format, tests):
429435 if interrupted :
430436 self .check_line (output , 'Test suite interrupted by signal SIGINT.' )
431437
432- if nfailed :
438+ if failed :
433439 result = 'FAILURE'
434440 elif interrupted :
435441 result = 'INTERRUPTED'
442+ elif fail_env_changed and env_changed :
443+ result = 'ENV CHANGED'
436444 else :
437445 result = 'SUCCESS'
438446 self .check_line (output , 'Tests result: %s' % result )
@@ -604,7 +612,7 @@ def test_failing(self):
604612 test_failing = self .create_test ('failing' , code = code )
605613 tests = [test_ok , test_failing ]
606614
607- output = self .run_tests (* tests , exitcode = 1 )
615+ output = self .run_tests (* tests , exitcode = 2 )
608616 self .check_executed_tests (output , tests , failed = test_failing )
609617
610618 def test_resources (self ):
@@ -703,7 +711,7 @@ def test_fromfile(self):
703711 def test_interrupted (self ):
704712 code = TEST_INTERRUPTED
705713 test = self .create_test ('sigint' , code = code )
706- output = self .run_tests (test , exitcode = 1 )
714+ output = self .run_tests (test , exitcode = 130 )
707715 self .check_executed_tests (output , test , omitted = test ,
708716 interrupted = True )
709717
@@ -732,7 +740,7 @@ def test_slow_interrupted(self):
732740 args = ("--slowest" , "-j2" , test )
733741 else :
734742 args = ("--slowest" , test )
735- output = self .run_tests (* args , exitcode = 1 )
743+ output = self .run_tests (* args , exitcode = 130 )
736744 self .check_executed_tests (output , test ,
737745 omitted = test , interrupted = True )
738746
@@ -772,9 +780,43 @@ def test_run(self):
772780 builtins.__dict__['RUN'] = 1
773781 """ )
774782 test = self .create_test ('forever' , code = code )
775- output = self .run_tests ('--forever' , test , exitcode = 1 )
783+ output = self .run_tests ('--forever' , test , exitcode = 2 )
776784 self .check_executed_tests (output , [test ]* 3 , failed = test )
777785
786+ def check_leak (self , code , what ):
787+ test = self .create_test ('huntrleaks' , code = code )
788+
789+ filename = 'reflog.txt'
790+ self .addCleanup (support .unlink , filename )
791+ output = self .run_tests ('--huntrleaks' , '3:3:' , test ,
792+ exitcode = 2 ,
793+ stderr = subprocess .STDOUT )
794+ self .check_executed_tests (output , [test ], failed = test )
795+
796+ line = 'beginning 6 repetitions\n 123456\n ......\n '
797+ self .check_line (output , re .escape (line ))
798+
799+ line2 = '%s leaked [1, 1, 1] %s, sum=3\n ' % (test , what )
800+ self .assertIn (line2 , output )
801+
802+ with open (filename ) as fp :
803+ reflog = fp .read ()
804+ self .assertIn (line2 , reflog )
805+
806+ @unittest .skipUnless (Py_DEBUG , 'need a debug build' )
807+ def test_huntrleaks (self ):
808+ # test --huntrleaks
809+ code = textwrap .dedent ("""
810+ import unittest
811+
812+ GLOBAL_LIST = []
813+
814+ class RefLeakTest(unittest.TestCase):
815+ def test_leak(self):
816+ GLOBAL_LIST.append(object())
817+ """ )
818+ self .check_leak (code , 'references' )
819+
778820 @unittest .skipUnless (Py_DEBUG , 'need a debug build' )
779821 def test_huntrleaks_fd_leak (self ):
780822 # test --huntrleaks for file descriptor leak
@@ -799,24 +841,7 @@ def test_leak(self):
799841 fd = os.open(__file__, os.O_RDONLY)
800842 # bug: never cloes the file descriptor
801843 """ )
802- test = self .create_test ('huntrleaks' , code = code )
803-
804- filename = 'reflog.txt'
805- self .addCleanup (support .unlink , filename )
806- output = self .run_tests ('--huntrleaks' , '3:3:' , test ,
807- exitcode = 1 ,
808- stderr = subprocess .STDOUT )
809- self .check_executed_tests (output , [test ], failed = test )
810-
811- line = 'beginning 6 repetitions\n 123456\n ......\n '
812- self .check_line (output , re .escape (line ))
813-
814- line2 = '%s leaked [1, 1, 1] file descriptors, sum=3\n ' % test
815- self .assertIn (line2 , output )
816-
817- with open (filename ) as fp :
818- reflog = fp .read ()
819- self .assertIn (line2 , reflog )
844+ self .check_leak (code , 'file descriptors' )
820845
821846 def test_list_tests (self ):
822847 # test --list-tests
@@ -837,19 +862,28 @@ def test_method2(self):
837862 pass
838863 """ )
839864 testname = self .create_test (code = code )
865+
866+ # Test --list-cases
840867 all_methods = ['%s.Tests.test_method1' % testname ,
841868 '%s.Tests.test_method2' % testname ]
842869 output = self .run_tests ('--list-cases' , testname )
843870 self .assertEqual (output .splitlines (), all_methods )
844871
872+ # Test --list-cases with --match
873+ all_methods = ['%s.Tests.test_method1' % testname ]
874+ output = self .run_tests ('--list-cases' ,
875+ '-m' , 'test_method1' ,
876+ testname )
877+ self .assertEqual (output .splitlines (), all_methods )
878+
845879 def test_crashed (self ):
846880 # Any code which causes a crash
847881 code = 'import faulthandler; faulthandler._sigsegv()'
848882 crash_test = self .create_test (name = "crash" , code = code )
849883 ok_test = self .create_test (name = "ok" )
850884
851885 tests = [crash_test , ok_test ]
852- output = self .run_tests ("-j2" , * tests , exitcode = 1 )
886+ output = self .run_tests ("-j2" , * tests , exitcode = 2 )
853887 self .check_executed_tests (output , tests , failed = crash_test ,
854888 randomize = True )
855889
@@ -898,6 +932,25 @@ def test_method4(self):
898932 subset = ['test_method1' , 'test_method3' ]
899933 self .assertEqual (methods , subset )
900934
935+ def test_env_changed (self ):
936+ code = textwrap .dedent ("""
937+ import unittest
938+
939+ class Tests(unittest.TestCase):
940+ def test_env_changed(self):
941+ open("env_changed", "w").close()
942+ """ )
943+ testname = self .create_test (code = code )
944+
945+ # don't fail by default
946+ output = self .run_tests (testname )
947+ self .check_executed_tests (output , [testname ], env_changed = testname )
948+
949+ # fail with --fail-env-changed
950+ output = self .run_tests ("--fail-env-changed" , testname , exitcode = 3 )
951+ self .check_executed_tests (output , [testname ], env_changed = testname ,
952+ fail_env_changed = True )
953+
901954
902955if __name__ == '__main__' :
903956 unittest .main ()
0 commit comments