|
17 | 17 | import contextlib |
18 | 18 | import copy |
19 | 19 | import enum |
| 20 | +import importlib |
20 | 21 | import io |
21 | 22 | import os |
| 23 | +import pdb |
22 | 24 | import re |
23 | 25 | import subprocess |
24 | 26 | import sys |
|
30 | 32 | from absl.testing import _bazelize_command |
31 | 33 | from absl.testing import absltest |
32 | 34 | from absl.testing import flagsaver |
| 35 | +from absl.testing import parameterized |
33 | 36 | from absl.tests import app_test_helper |
34 | 37 |
|
35 | 38 |
|
@@ -118,6 +121,89 @@ def test_register_and_parse_flags_with_usage_exits_on_second_run(self): |
118 | 121 | app._register_and_parse_flags_with_usage() |
119 | 122 |
|
120 | 123 |
|
| 124 | +class _MyDebuggerModule: |
| 125 | + """Imitates some aspects of the `pdb` interface.""" |
| 126 | + |
| 127 | + def post_mortem(self): |
| 128 | + pass |
| 129 | + |
| 130 | + def runcall(self, *args, **kwargs): |
| 131 | + del args, kwargs |
| 132 | + |
| 133 | + |
| 134 | +class _IncompleteDebuggerModule: |
| 135 | + """Provides `post_mortem` but not `runcall`.""" |
| 136 | + |
| 137 | + def post_mortem(self): |
| 138 | + pass |
| 139 | + |
| 140 | + |
| 141 | +def _pythonbreakpoint_variable(value): |
| 142 | + """Returns a context manager setting the $PYTHONBREAKPOINT env. variable.""" |
| 143 | + new_environment = {} if value is None else {'PYTHONBREAKPOINT': value} |
| 144 | + return mock.patch.object(os, 'environ', new_environment) |
| 145 | + |
| 146 | + |
| 147 | +class PythonBreakpointTest(parameterized.TestCase): |
| 148 | + |
| 149 | + def setUp(self): |
| 150 | + super().setUp() |
| 151 | + |
| 152 | + importlib_import_module = importlib.import_module |
| 153 | + |
| 154 | + def my_import_module(module, *args, **kwargs): |
| 155 | + if module == 'my.debugger': |
| 156 | + return _MyDebuggerModule() |
| 157 | + if module == 'incomplete.debugger': |
| 158 | + return _IncompleteDebuggerModule() |
| 159 | + if module == '0': |
| 160 | + # `PYTHONBREAKPOINT=0` is a special value. |
| 161 | + raise ValueError('Should not try to import module `0`') |
| 162 | + return importlib_import_module(module, *args, **kwargs) |
| 163 | + |
| 164 | + self._mock_import_module = self.enter_context( |
| 165 | + mock.patch.object(importlib, 'import_module', my_import_module) |
| 166 | + ) |
| 167 | + |
| 168 | + @parameterized.named_parameters( |
| 169 | + ('no_variable', None), |
| 170 | + ('empty_variable', ''), |
| 171 | + ('explicit_pdb', 'pdb.set_trace'), |
| 172 | + ('unimportable_module', 'unimportable.module.set_trace'), |
| 173 | + ('opt_out', '0'), |
| 174 | + ) |
| 175 | + def test_python_breakpoint_pdb(self, value): |
| 176 | + with _pythonbreakpoint_variable(value): |
| 177 | + self.assertIs(app._get_debugger_module_with_function('runcall'), pdb) |
| 178 | + self.assertIs(app._get_debugger_module_with_function('post_mortem'), pdb) |
| 179 | + |
| 180 | + def test_my_debugger(self): |
| 181 | + with _pythonbreakpoint_variable('my.debugger.set_trace'): |
| 182 | + debugger_with_runcall = app._get_debugger_module_with_function('runcall') |
| 183 | + self.assertIsInstance(debugger_with_runcall, _MyDebuggerModule) |
| 184 | + self.assertTrue(hasattr(debugger_with_runcall, 'runcall')) |
| 185 | + |
| 186 | + debugger_with_post_mortem = app._get_debugger_module_with_function( |
| 187 | + 'post_mortem' |
| 188 | + ) |
| 189 | + self.assertIsInstance(debugger_with_post_mortem, _MyDebuggerModule) |
| 190 | + self.assertTrue(hasattr(debugger_with_post_mortem, 'post_mortem')) |
| 191 | + |
| 192 | + def test_incomplete_debugger(self): |
| 193 | + with _pythonbreakpoint_variable('incomplete.debugger.set_trace'): |
| 194 | + debugger_with_runcall = app._get_debugger_module_with_function('runcall') |
| 195 | + self.assertIs(debugger_with_runcall, pdb) |
| 196 | + self.assertTrue(hasattr(debugger_with_runcall, 'runcall')) |
| 197 | + |
| 198 | + debugger_with_post_mortem = app._get_debugger_module_with_function( |
| 199 | + 'post_mortem' |
| 200 | + ) |
| 201 | + self.assertIsInstance( |
| 202 | + debugger_with_post_mortem, _IncompleteDebuggerModule |
| 203 | + ) |
| 204 | + self.assertTrue(hasattr(debugger_with_post_mortem, 'post_mortem')) |
| 205 | + |
| 206 | + |
121 | 207 | class FunctionalTests(absltest.TestCase): |
122 | 208 | """Functional tests that use runs app_test_helper.""" |
123 | 209 |
|
|
0 commit comments