17db96d56Sopenharmony_ci"""Test suite for the profile module.""" 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciimport sys 47db96d56Sopenharmony_ciimport pstats 57db96d56Sopenharmony_ciimport unittest 67db96d56Sopenharmony_ciimport os 77db96d56Sopenharmony_cifrom difflib import unified_diff 87db96d56Sopenharmony_cifrom io import StringIO 97db96d56Sopenharmony_cifrom test.support.os_helper import TESTFN, unlink, temp_dir, change_cwd 107db96d56Sopenharmony_cifrom contextlib import contextmanager 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ciimport profile 137db96d56Sopenharmony_cifrom test.profilee import testfunc, timer 147db96d56Sopenharmony_cifrom test.support.script_helper import assert_python_failure, assert_python_ok 157db96d56Sopenharmony_ci 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ciclass ProfileTest(unittest.TestCase): 187db96d56Sopenharmony_ci 197db96d56Sopenharmony_ci profilerclass = profile.Profile 207db96d56Sopenharmony_ci profilermodule = profile 217db96d56Sopenharmony_ci methodnames = ['print_stats', 'print_callers', 'print_callees'] 227db96d56Sopenharmony_ci expected_max_output = ':0(max)' 237db96d56Sopenharmony_ci 247db96d56Sopenharmony_ci def tearDown(self): 257db96d56Sopenharmony_ci unlink(TESTFN) 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ci def get_expected_output(self): 287db96d56Sopenharmony_ci return _ProfileOutput 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ci @classmethod 317db96d56Sopenharmony_ci def do_profiling(cls): 327db96d56Sopenharmony_ci results = [] 337db96d56Sopenharmony_ci prof = cls.profilerclass(timer, 0.001) 347db96d56Sopenharmony_ci start_timer = timer() 357db96d56Sopenharmony_ci prof.runctx("testfunc()", globals(), locals()) 367db96d56Sopenharmony_ci results.append(timer() - start_timer) 377db96d56Sopenharmony_ci for methodname in cls.methodnames: 387db96d56Sopenharmony_ci s = StringIO() 397db96d56Sopenharmony_ci stats = pstats.Stats(prof, stream=s) 407db96d56Sopenharmony_ci stats.strip_dirs().sort_stats("stdname") 417db96d56Sopenharmony_ci getattr(stats, methodname)() 427db96d56Sopenharmony_ci output = s.getvalue().splitlines() 437db96d56Sopenharmony_ci mod_name = testfunc.__module__.rsplit('.', 1)[1] 447db96d56Sopenharmony_ci # Only compare against stats originating from the test file. 457db96d56Sopenharmony_ci # Prevents outside code (e.g., the io module) from causing 467db96d56Sopenharmony_ci # unexpected output. 477db96d56Sopenharmony_ci output = [line.rstrip() for line in output if mod_name in line] 487db96d56Sopenharmony_ci results.append('\n'.join(output)) 497db96d56Sopenharmony_ci return results 507db96d56Sopenharmony_ci 517db96d56Sopenharmony_ci def test_cprofile(self): 527db96d56Sopenharmony_ci results = self.do_profiling() 537db96d56Sopenharmony_ci expected = self.get_expected_output() 547db96d56Sopenharmony_ci self.assertEqual(results[0], 1000) 557db96d56Sopenharmony_ci fail = [] 567db96d56Sopenharmony_ci for i, method in enumerate(self.methodnames): 577db96d56Sopenharmony_ci a = expected[method] 587db96d56Sopenharmony_ci b = results[i+1] 597db96d56Sopenharmony_ci if a != b: 607db96d56Sopenharmony_ci fail.append(f"\nStats.{method} output for " 617db96d56Sopenharmony_ci f"{self.profilerclass.__name__} " 627db96d56Sopenharmony_ci "does not fit expectation:") 637db96d56Sopenharmony_ci fail.extend(unified_diff(a.split('\n'), b.split('\n'), 647db96d56Sopenharmony_ci lineterm="")) 657db96d56Sopenharmony_ci if fail: 667db96d56Sopenharmony_ci self.fail("\n".join(fail)) 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci def test_calling_conventions(self): 697db96d56Sopenharmony_ci # Issue #5330: profile and cProfile wouldn't report C functions called 707db96d56Sopenharmony_ci # with keyword arguments. We test all calling conventions. 717db96d56Sopenharmony_ci stmts = [ 727db96d56Sopenharmony_ci "max([0])", 737db96d56Sopenharmony_ci "max([0], key=int)", 747db96d56Sopenharmony_ci "max([0], **dict(key=int))", 757db96d56Sopenharmony_ci "max(*([0],))", 767db96d56Sopenharmony_ci "max(*([0],), key=int)", 777db96d56Sopenharmony_ci "max(*([0],), **dict(key=int))", 787db96d56Sopenharmony_ci ] 797db96d56Sopenharmony_ci for stmt in stmts: 807db96d56Sopenharmony_ci s = StringIO() 817db96d56Sopenharmony_ci prof = self.profilerclass(timer, 0.001) 827db96d56Sopenharmony_ci prof.runctx(stmt, globals(), locals()) 837db96d56Sopenharmony_ci stats = pstats.Stats(prof, stream=s) 847db96d56Sopenharmony_ci stats.print_stats() 857db96d56Sopenharmony_ci res = s.getvalue() 867db96d56Sopenharmony_ci self.assertIn(self.expected_max_output, res, 877db96d56Sopenharmony_ci "Profiling {0!r} didn't report max:\n{1}".format(stmt, res)) 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ci def test_run(self): 907db96d56Sopenharmony_ci with silent(): 917db96d56Sopenharmony_ci self.profilermodule.run("int('1')") 927db96d56Sopenharmony_ci self.profilermodule.run("int('1')", filename=TESTFN) 937db96d56Sopenharmony_ci self.assertTrue(os.path.exists(TESTFN)) 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ci def test_runctx(self): 967db96d56Sopenharmony_ci with silent(): 977db96d56Sopenharmony_ci self.profilermodule.runctx("testfunc()", globals(), locals()) 987db96d56Sopenharmony_ci self.profilermodule.runctx("testfunc()", globals(), locals(), 997db96d56Sopenharmony_ci filename=TESTFN) 1007db96d56Sopenharmony_ci self.assertTrue(os.path.exists(TESTFN)) 1017db96d56Sopenharmony_ci 1027db96d56Sopenharmony_ci def test_run_profile_as_module(self): 1037db96d56Sopenharmony_ci # Test that -m switch needs an argument 1047db96d56Sopenharmony_ci assert_python_failure('-m', self.profilermodule.__name__, '-m') 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_ci # Test failure for not-existent module 1077db96d56Sopenharmony_ci assert_python_failure('-m', self.profilermodule.__name__, 1087db96d56Sopenharmony_ci '-m', 'random_module_xyz') 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_ci # Test successful run 1117db96d56Sopenharmony_ci assert_python_ok('-m', self.profilermodule.__name__, 1127db96d56Sopenharmony_ci '-m', 'timeit', '-n', '1') 1137db96d56Sopenharmony_ci 1147db96d56Sopenharmony_ci def test_output_file_when_changing_directory(self): 1157db96d56Sopenharmony_ci with temp_dir() as tmpdir, change_cwd(tmpdir): 1167db96d56Sopenharmony_ci os.mkdir('dest') 1177db96d56Sopenharmony_ci with open('demo.py', 'w', encoding="utf-8") as f: 1187db96d56Sopenharmony_ci f.write('import os; os.chdir("dest")') 1197db96d56Sopenharmony_ci 1207db96d56Sopenharmony_ci assert_python_ok( 1217db96d56Sopenharmony_ci '-m', self.profilermodule.__name__, 1227db96d56Sopenharmony_ci '-o', 'out.pstats', 1237db96d56Sopenharmony_ci 'demo.py', 1247db96d56Sopenharmony_ci ) 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci self.assertTrue(os.path.exists('out.pstats')) 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ci 1297db96d56Sopenharmony_cidef regenerate_expected_output(filename, cls): 1307db96d56Sopenharmony_ci filename = filename.rstrip('co') 1317db96d56Sopenharmony_ci print('Regenerating %s...' % filename) 1327db96d56Sopenharmony_ci results = cls.do_profiling() 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci newfile = [] 1357db96d56Sopenharmony_ci with open(filename, 'r') as f: 1367db96d56Sopenharmony_ci for line in f: 1377db96d56Sopenharmony_ci newfile.append(line) 1387db96d56Sopenharmony_ci if line.startswith('#--cut'): 1397db96d56Sopenharmony_ci break 1407db96d56Sopenharmony_ci 1417db96d56Sopenharmony_ci with open(filename, 'w') as f: 1427db96d56Sopenharmony_ci f.writelines(newfile) 1437db96d56Sopenharmony_ci f.write("_ProfileOutput = {}\n") 1447db96d56Sopenharmony_ci for i, method in enumerate(cls.methodnames): 1457db96d56Sopenharmony_ci f.write('_ProfileOutput[%r] = """\\\n%s"""\n' % ( 1467db96d56Sopenharmony_ci method, results[i+1])) 1477db96d56Sopenharmony_ci f.write('\nif __name__ == "__main__":\n main()\n') 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ci@contextmanager 1507db96d56Sopenharmony_cidef silent(): 1517db96d56Sopenharmony_ci stdout = sys.stdout 1527db96d56Sopenharmony_ci try: 1537db96d56Sopenharmony_ci sys.stdout = StringIO() 1547db96d56Sopenharmony_ci yield 1557db96d56Sopenharmony_ci finally: 1567db96d56Sopenharmony_ci sys.stdout = stdout 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_ci 1597db96d56Sopenharmony_cidef main(): 1607db96d56Sopenharmony_ci if '-r' not in sys.argv: 1617db96d56Sopenharmony_ci unittest.main() 1627db96d56Sopenharmony_ci else: 1637db96d56Sopenharmony_ci regenerate_expected_output(__file__, ProfileTest) 1647db96d56Sopenharmony_ci 1657db96d56Sopenharmony_ci 1667db96d56Sopenharmony_ci# Don't remove this comment. Everything below it is auto-generated. 1677db96d56Sopenharmony_ci#--cut-------------------------------------------------------------------------- 1687db96d56Sopenharmony_ci_ProfileOutput = {} 1697db96d56Sopenharmony_ci_ProfileOutput['print_stats'] = """\ 1707db96d56Sopenharmony_ci 28 27.972 0.999 27.972 0.999 profilee.py:110(__getattr__) 1717db96d56Sopenharmony_ci 1 269.996 269.996 999.769 999.769 profilee.py:25(testfunc) 1727db96d56Sopenharmony_ci 23/3 149.937 6.519 169.917 56.639 profilee.py:35(factorial) 1737db96d56Sopenharmony_ci 20 19.980 0.999 19.980 0.999 profilee.py:48(mul) 1747db96d56Sopenharmony_ci 2 39.986 19.993 599.830 299.915 profilee.py:55(helper) 1757db96d56Sopenharmony_ci 4 115.984 28.996 119.964 29.991 profilee.py:73(helper1) 1767db96d56Sopenharmony_ci 2 -0.006 -0.003 139.946 69.973 profilee.py:84(helper2_indirect) 1777db96d56Sopenharmony_ci 8 311.976 38.997 399.912 49.989 profilee.py:88(helper2) 1787db96d56Sopenharmony_ci 8 63.976 7.997 79.960 9.995 profilee.py:98(subhelper)""" 1797db96d56Sopenharmony_ci_ProfileOutput['print_callers'] = """\ 1807db96d56Sopenharmony_ci:0(append) <- profilee.py:73(helper1)(4) 119.964 1817db96d56Sopenharmony_ci:0(exc_info) <- profilee.py:73(helper1)(4) 119.964 1827db96d56Sopenharmony_ci:0(hasattr) <- profilee.py:73(helper1)(4) 119.964 1837db96d56Sopenharmony_ci profilee.py:88(helper2)(8) 399.912 1847db96d56Sopenharmony_ciprofilee.py:110(__getattr__) <- :0(hasattr)(12) 11.964 1857db96d56Sopenharmony_ci profilee.py:98(subhelper)(16) 79.960 1867db96d56Sopenharmony_ciprofilee.py:25(testfunc) <- <string>:1(<module>)(1) 999.767 1877db96d56Sopenharmony_ciprofilee.py:35(factorial) <- profilee.py:25(testfunc)(1) 999.769 1887db96d56Sopenharmony_ci profilee.py:35(factorial)(20) 169.917 1897db96d56Sopenharmony_ci profilee.py:84(helper2_indirect)(2) 139.946 1907db96d56Sopenharmony_ciprofilee.py:48(mul) <- profilee.py:35(factorial)(20) 169.917 1917db96d56Sopenharmony_ciprofilee.py:55(helper) <- profilee.py:25(testfunc)(2) 999.769 1927db96d56Sopenharmony_ciprofilee.py:73(helper1) <- profilee.py:55(helper)(4) 599.830 1937db96d56Sopenharmony_ciprofilee.py:84(helper2_indirect) <- profilee.py:55(helper)(2) 599.830 1947db96d56Sopenharmony_ciprofilee.py:88(helper2) <- profilee.py:55(helper)(6) 599.830 1957db96d56Sopenharmony_ci profilee.py:84(helper2_indirect)(2) 139.946 1967db96d56Sopenharmony_ciprofilee.py:98(subhelper) <- profilee.py:88(helper2)(8) 399.912""" 1977db96d56Sopenharmony_ci_ProfileOutput['print_callees'] = """\ 1987db96d56Sopenharmony_ci:0(hasattr) -> profilee.py:110(__getattr__)(12) 27.972 1997db96d56Sopenharmony_ci<string>:1(<module>) -> profilee.py:25(testfunc)(1) 999.769 2007db96d56Sopenharmony_ciprofilee.py:110(__getattr__) -> 2017db96d56Sopenharmony_ciprofilee.py:25(testfunc) -> profilee.py:35(factorial)(1) 169.917 2027db96d56Sopenharmony_ci profilee.py:55(helper)(2) 599.830 2037db96d56Sopenharmony_ciprofilee.py:35(factorial) -> profilee.py:35(factorial)(20) 169.917 2047db96d56Sopenharmony_ci profilee.py:48(mul)(20) 19.980 2057db96d56Sopenharmony_ciprofilee.py:48(mul) -> 2067db96d56Sopenharmony_ciprofilee.py:55(helper) -> profilee.py:73(helper1)(4) 119.964 2077db96d56Sopenharmony_ci profilee.py:84(helper2_indirect)(2) 139.946 2087db96d56Sopenharmony_ci profilee.py:88(helper2)(6) 399.912 2097db96d56Sopenharmony_ciprofilee.py:73(helper1) -> :0(append)(4) -0.004 2107db96d56Sopenharmony_ciprofilee.py:84(helper2_indirect) -> profilee.py:35(factorial)(2) 169.917 2117db96d56Sopenharmony_ci profilee.py:88(helper2)(2) 399.912 2127db96d56Sopenharmony_ciprofilee.py:88(helper2) -> :0(hasattr)(8) 11.964 2137db96d56Sopenharmony_ci profilee.py:98(subhelper)(8) 79.960 2147db96d56Sopenharmony_ciprofilee.py:98(subhelper) -> profilee.py:110(__getattr__)(16) 27.972""" 2157db96d56Sopenharmony_ci 2167db96d56Sopenharmony_ciif __name__ == "__main__": 2177db96d56Sopenharmony_ci main() 218