class ApplicationTester(object): """ Eases the testing of console applications. """ def __init__(self, application: Application) -> None: self._application = application self._application.auto_exits(False) self._io = BufferedIO() self._status_code = 0 @property def application(self) -> Application: return self._application @property def io(self): # type: () -> BufferedIO return self._io @property def status_code(self): # type: () -> int return self._status_code def execute( self, args: Optional[str] = "", inputs: Optional[str] = None, interactive: Optional[bool] = None, verbosity: Optional[Verbosity] = None, decorated: bool = False, supports_utf8: bool = True, ) -> int: """ Executes the command """ self._io.clear() input = StringInput(args) self._io.set_input(input) self._io.decorated(decorated) self._io.output.set_supports_utf8(supports_utf8) self._io.error_output.set_supports_utf8(supports_utf8) if inputs is not None: self._io.input.set_stream(StringIO(inputs)) if interactive is not None: self._io.interactive(interactive) if verbosity is not None: self._io.set_verbosity(verbosity) self._status_code = self._application.run( self._io.input, self._io.output, self._io.error_output, ) return self._status_code
def test_render_debug_better_error_message_recursion_error(): io = BufferedIO() io.set_verbosity(Verbosity.DEBUG) try: recursion_error() except RecursionError as e: trace = ExceptionTrace(e) trace.render(io) expected = r"""^ Stack trace: \d+ {}:99 in test_render_debug_better_error_message_recursion_error 97\│ 98\│ try: → 99\│ recursion_error\(\) 100\│ except RecursionError as e: 101\│ trace = ExceptionTrace\(e\) ... Previous frame repeated \d+ times \s*\d+ {}:91 in recursion_error 89\│ 90\│ def recursion_error\(\): → 91\│ recursion_error\(\) 92\│ 93\│ RecursionError maximum recursion depth exceeded at {}:91 in recursion_error 87\│ assert re.match\(expected, io.fetch_output\(\)\) is not None 88\│ 89\│ 90\│ def recursion_error\(\): → 91\│ recursion_error\(\) 92\│ 93\│ 94\│ def test_render_debug_better_error_message_recursion_error\(\): 95\│ io = BufferedIO\(\) """.format( re.escape(trace._get_relative_file_path(__file__)), re.escape(trace._get_relative_file_path(__file__)), re.escape(trace._get_relative_file_path(__file__)), ) assert re.match(expected, io.fetch_output()) is not None
def test_render_can_ignore_given_files(): import os from .helpers import outer io = BufferedIO() io.set_verbosity(Verbosity.VERBOSE) def call(): def run(): outer() run() with pytest.raises(Exception) as e: call() trace = ExceptionTrace(e.value) helpers_file = os.path.join(os.path.dirname(__file__), "helpers.py") trace.ignore_files_in("^{}$".format(re.escape(helpers_file))) trace.render(io) expected = """ Stack trace: 2 {}:224 in test_render_can_ignore_given_files call() 1 {}:221 in call run() Exception Foo at {}:3 in inner 1│ def outer(): 2│ def inner(): → 3│ raise Exception("Foo") 4│ 5│ inner() 6│ """.format( trace._get_relative_file_path(__file__), trace._get_relative_file_path(__file__), trace._get_relative_file_path(helpers_file), ) assert expected == io.fetch_output()
def test_render_debug_better_error_message_recursion_error_with_multiple_duplicated_frames( ): io = BufferedIO() io.set_verbosity(Verbosity.VERBOSE) with pytest.raises(RecursionError) as e: first() trace = ExceptionTrace(e.value) trace.render(io) expected = r"... Previous 2 frames repeated \d+ times" assert re.search(expected, io.fetch_output()) is not None
def test_render_debug_better_error_message(): io = BufferedIO() io.set_verbosity(Verbosity.DEBUG) try: fail() except Exception as e: # Exception trace = ExceptionTrace(e) trace.render(io) expected = r"""^ Stack trace: 1 {}:52 in test_render_debug_better_error_message 50\│ 51\│ try: → 52\│ fail\(\) 53\│ except Exception as e: # Exception 54\│ trace = ExceptionTrace\(e\) Exception Failed at {}:12 in fail 8\│ from cleo.ui.exception_trace import ExceptionTrace 9\│ 10\│ 11\│ def fail\(\): → 12\│ raise Exception\("Failed"\) 13\│ 14\│ 15\│ def test_render_better_error_message\(\): 16\│ io = BufferedIO\(\) """.format( re.escape(trace._get_relative_file_path(__file__)), re.escape(trace._get_relative_file_path(__file__)), ) assert re.match(expected, io.fetch_output()) is not None
class CommandTester(object): """ Eases the testing of console commands. """ def __init__(self, command: Command) -> None: self._command = command self._io = BufferedIO() self._inputs = [] self._status_code = None @property def command(self) -> Command: return self._command @property def io(self) -> BufferedIO: return self._io @property def status_code(self): # type: () -> int return self._status_code def execute( self, args: Optional[str] = "", inputs: Optional[str] = None, interactive: Optional[bool] = None, verbosity: Optional[Verbosity] = None, decorated: Optional[bool] = None, supports_utf8: bool = True, ) -> int: """ Executes the command """ application = self._command.application input = StringInput(args) if application is not None and application.definition.has_argument("command"): name = self._command.name if " " in name: # If the command is namespaced we rearrange # the input to parse it as a single argument argv = [application.name, self._command.name] + input._tokens input = ArgvInput(argv) else: input = StringInput(name + " " + args) self._io.set_input(input) self._io.output.set_supports_utf8(supports_utf8) self._io.error_output.set_supports_utf8(supports_utf8) if inputs is not None: self._io.input.set_stream(StringIO(inputs)) if interactive is not None: self._io.interactive(interactive) if verbosity is not None: self._io.set_verbosity(verbosity) if decorated is not None: self._io.decorated(decorated) self._status_code = self._command.run(self._io) return self._status_code
def test_render_shows_ignored_files_if_in_debug_mode(): import os from .helpers import outer io = BufferedIO() io.set_verbosity(Verbosity.DEBUG) def call(): def run(): outer() run() with pytest.raises(Exception) as e: call() trace = ExceptionTrace(e.value) helpers_file = os.path.join(os.path.dirname(__file__), "helpers.py") trace.ignore_files_in("^{}$".format(re.escape(helpers_file))) trace.render(io) expected = """ Stack trace: 4 {}:276 in test_render_shows_ignored_files_if_in_debug_mode 274│ 275│ with pytest.raises(Exception) as e: → 276│ call() 277│ 278│ trace = ExceptionTrace(e.value) 3 {}:273 in call 271│ outer() 272│ → 273│ run() 274│ 275│ with pytest.raises(Exception) as e: 2 {}:271 in run 269│ def call(): 270│ def run(): → 271│ outer() 272│ 273│ run() 1 {}:5 in outer 3│ raise Exception("Foo") 4│ → 5│ inner() 6│ Exception Foo at {}:3 in inner 1│ def outer(): 2│ def inner(): → 3│ raise Exception("Foo") 4│ 5│ inner() 6│ """.format( trace._get_relative_file_path(__file__), trace._get_relative_file_path(__file__), trace._get_relative_file_path(__file__), trace._get_relative_file_path(helpers_file), trace._get_relative_file_path(helpers_file), ) assert expected == io.fetch_output()