|
4 | 4 | import pstats
|
5 | 5 | import unittest
|
6 | 6 | import os
|
| 7 | +import subprocess |
7 | 8 | from difflib import unified_diff
|
8 | 9 | from io import StringIO
|
9 | 10 | from test.support.os_helper import TESTFN, unlink, temp_dir, change_cwd
|
@@ -130,6 +131,112 @@ def test_output_file_when_changing_directory(self):
|
130 | 131 |
|
131 | 132 | self.assertTrue(os.path.exists('out.pstats'))
|
132 | 133 |
|
| 134 | +class ProfileCLITests(unittest.TestCase): |
| 135 | + """Tests for the profile module's command line interface.""" |
| 136 | + |
| 137 | + def setUp(self): |
| 138 | + # Create a simple Python script to profile |
| 139 | + self.script_content = """\ |
| 140 | +def factorial(n): |
| 141 | + if n <= 1: |
| 142 | + return 1 |
| 143 | + return n * factorial(n-1) |
| 144 | +
|
| 145 | +if __name__ == "__main__": |
| 146 | + factorial(10) |
| 147 | +""" |
| 148 | + self.script_file = TESTFN |
| 149 | + with open(self.script_file, "w") as f: |
| 150 | + f.write(self.script_content) |
| 151 | + self.addCleanup(unlink, self.script_file) |
| 152 | + |
| 153 | + def _run_profile_cli(self, *args): |
| 154 | + """Helper to run the profile CLI with given arguments.""" |
| 155 | + cmd = [sys.executable, '-m', 'profile'] + list(args) |
| 156 | + proc = subprocess.Popen( |
| 157 | + cmd, |
| 158 | + stdout=subprocess.PIPE, |
| 159 | + stderr=subprocess.PIPE, |
| 160 | + universal_newlines=True |
| 161 | + ) |
| 162 | + stdout, stderr = proc.communicate() |
| 163 | + return proc.returncode, stdout, stderr |
| 164 | + |
| 165 | + def test_basic_profile(self): |
| 166 | + """Test basic profiling of a script.""" |
| 167 | + returncode, stdout, stderr = self._run_profile_cli(self.script_file) |
| 168 | + self.assertEqual(returncode, 0) |
| 169 | + self.assertIn("function calls", stdout) |
| 170 | + self.assertIn("factorial", stdout) |
| 171 | + |
| 172 | + def test_sort_options(self): |
| 173 | + """Test different sort options.""" |
| 174 | + # List of sort options known to work |
| 175 | + sort_options = ['calls', 'cumulative', 'cumtime', 'file', |
| 176 | + 'filename', 'module', 'ncalls', 'pcalls', |
| 177 | + 'line', 'stdname', 'time', 'tottime'] |
| 178 | + |
| 179 | + # Test each sort option individually |
| 180 | + for option in sort_options: |
| 181 | + with self.subTest(sort_option=option): |
| 182 | + returncode, stdout, stderr = self._run_profile_cli( |
| 183 | + '-s', option, self.script_file |
| 184 | + ) |
| 185 | + self.assertEqual(returncode, 0) |
| 186 | + self.assertIn("function calls", stdout) |
| 187 | + |
| 188 | + def test_output_file(self): |
| 189 | + """Test writing profile results to a file.""" |
| 190 | + output_file = TESTFN + '.prof' |
| 191 | + self.addCleanup(unlink, output_file) |
| 192 | + |
| 193 | + returncode, stdout, stderr = self._run_profile_cli( |
| 194 | + '-o', output_file, self.script_file |
| 195 | + ) |
| 196 | + self.assertEqual(returncode, 0) |
| 197 | + |
| 198 | + # Check that the output file exists and contains profile data |
| 199 | + self.assertTrue(os.path.exists(output_file)) |
| 200 | + stats = pstats.Stats(output_file) |
| 201 | + self.assertGreater(stats.total_calls, 0) |
| 202 | + |
| 203 | + def test_invalid_option(self): |
| 204 | + """Test behavior with an invalid option.""" |
| 205 | + returncode, stdout, stderr = self._run_profile_cli( |
| 206 | + '--invalid-option', self.script_file |
| 207 | + ) |
| 208 | + self.assertNotEqual(returncode, 0) |
| 209 | + self.assertIn("error", stderr.lower()) |
| 210 | + |
| 211 | + def test_no_arguments(self): |
| 212 | + """Test behavior with no arguments.""" |
| 213 | + returncode, stdout, stderr = self._run_profile_cli() |
| 214 | + self.assertNotEqual(returncode, 0) |
| 215 | + |
| 216 | + # Check either stdout or stderr for usage information |
| 217 | + combined_output = stdout.lower() + stderr.lower() |
| 218 | + self.assertTrue("usage:" in combined_output or |
| 219 | + "error:" in combined_output or |
| 220 | + "no script filename specified" in combined_output, |
| 221 | + "Expected usage information or error message not found") |
| 222 | + |
| 223 | + def test_run_module(self): |
| 224 | + """Test profiling a module with -m option.""" |
| 225 | + # Create a small module |
| 226 | + module_name = "test_profile_module" |
| 227 | + module_file = f"{module_name}.py" |
| 228 | + |
| 229 | + with open(module_file, "w") as f: |
| 230 | + f.write("print('Module executed')\n") |
| 231 | + |
| 232 | + self.addCleanup(unlink, module_file) |
| 233 | + |
| 234 | + returncode, stdout, stderr = self._run_profile_cli( |
| 235 | + '-m', module_name |
| 236 | + ) |
| 237 | + self.assertEqual(returncode, 0) |
| 238 | + self.assertIn("Module executed", stdout) |
| 239 | + self.assertIn("function calls", stdout) |
133 | 240 |
|
134 | 241 | def regenerate_expected_output(filename, cls):
|
135 | 242 | filename = filename.rstrip('co')
|
|
0 commit comments