Beispiel #1
0
def test_render_verbose():
    io = BufferedIO()
    io.set_verbosity(VERBOSE)

    try:
        raise Exception("Failed")
    except Exception as e:
        trace = ExceptionTrace(e)

    trace.render(io)

    msg = "'Failed'"
    if PY38:
        msg = '"Failed"'

    expected = """\

[Exception]
Failed

Traceback (most recent call last):
  File "{}", line 30, in test_render_verbose
    raise Exception({})

""".format(__file__, msg)
    assert expected == io.fetch_output()
Beispiel #2
0
def test_render_better_error_message():
    io = BufferedIO()

    try:
        raise Exception("Failed")
    except Exception as e:
        trace = ExceptionTrace(e)

    trace.render(io)

    expected = """\

  Exception

  Failed

  at {}:45 in test_render_better_error_message
       41│ def test_render_better_error_message():
       42│     io = BufferedIO()
       43│ 
       44│     try:
    →  45│         raise Exception("Failed")
       46│     except Exception as e:
       47│         trace = ExceptionTrace(e)
       48│ 
       49│     trace.render(io)
""".format(trace._get_relative_file_path(__file__))
    assert expected == io.fetch_output()
Beispiel #3
0
def test_render_supports_solutions():
    from crashtest.contracts.provides_solution import ProvidesSolution
    from crashtest.contracts.base_solution import BaseSolution
    from crashtest.solution_providers.solution_provider_repository import (
        SolutionProviderRepository,
    )

    class CustomError(ProvidesSolution, Exception):
        @property
        def solution(self):
            solution = BaseSolution("Solution Title.", "Solution Description")
            solution.documentation_links.append("https://example.com")
            solution.documentation_links.append("https://example2.com")

            return solution

    io = BufferedIO()

    def call():
        raise CustomError("Error with solution")

    with pytest.raises(CustomError) as e:
        call()

    trace = ExceptionTrace(
        e.value, solution_provider_repository=SolutionProviderRepository()
    )

    trace.render(io)

    expected = """
  CustomError

  Error with solution

  at {}:433 in call
      429│ 
      430│     io = BufferedIO()
      431│ 
      432│     def call():
    → 433│         raise CustomError("Error with solution")
      434│ 
      435│     with pytest.raises(CustomError) as e:
      436│         call()
      437│ 

  • Solution Title: Solution Description
    https://example.com,
    https://example2.com
""".format(
        trace._get_relative_file_path(__file__),
    )

    assert expected == io.fetch_output()
Beispiel #4
0
def test_render_falls_back_on_ascii_symbols():
    from crashtest.contracts.base_solution import BaseSolution
    from crashtest.contracts.provides_solution import ProvidesSolution
    from crashtest.solution_providers.solution_provider_repository import (
        SolutionProviderRepository, )

    class CustomError(ProvidesSolution, Exception):
        @property
        def solution(self):
            solution = BaseSolution("Solution Title.", "Solution Description")
            solution.documentation_links.append("https://example.com")
            solution.documentation_links.append("https://example2.com")

            return solution

    io = BufferedIO(supports_utf8=False)

    def call():
        raise CustomError("Error with solution")

    with pytest.raises(CustomError) as e:
        call()

    trace = ExceptionTrace(
        e.value, solution_provider_repository=SolutionProviderRepository())

    trace.render(io)

    expected = """
  CustomError

  Error with solution

  at {}:493 in call
      489| 
      490|     io = BufferedIO(supports_utf8=False)
      491| 
      492|     def call():
    > 493|         raise CustomError("Error with solution")
      494| 
      495|     with pytest.raises(CustomError) as e:
      496|         call()
      497| 

  * Solution Title: Solution Description
    https://example.com,
    https://example2.com
""".format(trace._get_relative_file_path(__file__), )

    assert expected == io.fetch_output()
Beispiel #5
0
def test_render_debug_better_error_message_recursion_error_with_multiple_duplicated_frames(
):
    io = BufferedIO()
    io.set_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
Beispiel #6
0
def test_render():
    io = BufferedIO()

    try:
        raise Exception("Failed")
    except Exception as e:
        trace = ExceptionTrace(e)

    trace.render(io)

    expected = """\

[Exception]
Failed
"""
    assert expected == io.fetch_output()
Beispiel #7
0
def test_render_legacy_error_message():
    io = BufferedIO()

    try:
        raise Exception("Failed")
    except Exception as e:
        trace = ExceptionTrace(e)

    trace.render(io)

    expected = """\

Exception

Failed
"""
    assert expected == io.fetch_output()
Beispiel #8
0
def test_render_debug_better_error_message():
    io = BufferedIO()
    io.set_verbosity(DEBUG)

    try:
        fail()
    except Exception as e:  # Exception
        trace = ExceptionTrace(e)

    trace.render(io)

    expected = r"""^
  Stack trace:

  1  {}:113 in test_render_debug_better_error_message
      111\│ 
      112\│     try:
    → 113\│         fail\(\)
      114\│     except Exception as e:  # Exception
      115\│         trace = ExceptionTrace\(e\)

  Exception

  Failed

  at {}:15 in fail
       11\│ from clikit.utils._compat import PY38
       12\│ 
       13\│ 
       14\│ def fail\(\):
    →  15\│     raise Exception\("Failed"\)
       16\│ 
       17\│ 
       18\│ @pytest.mark.skipif\(PY36, reason="Legacy error messages are Python <3.6 only"\)
       19\│ def test_render_legacy_error_message\(\):
""".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
Beispiel #9
0
def test_render_can_ignore_given_files():
    import os

    from .helpers import outer

    io = BufferedIO()
    io.set_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  {}:297 in test_render_can_ignore_given_files
     call()

  1  {}:294 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()
Beispiel #10
0
def test_render_debug_better_error_message_recursion_error():
    io = BufferedIO()
    io.set_verbosity(DEBUG)

    try:
        recursion_error()
    except RecursionError as e:
        trace = ExceptionTrace(e)

    trace.render(io)

    expected = r"""^
  Stack trace:

  \d+  {}:163 in test_render_debug_better_error_message_recursion_error
        161\│ 
        162\│     try:
      → 163\│         recursion_error\(\)
        164\│     except RecursionError as e:
        165\│         trace = ExceptionTrace\(e\)

  ...  Previous frame repeated \d+ times

  \s*\d+  {}:152 in recursion_error
        150\│ 
        151\│ def recursion_error\(\):
      → 152\│     recursion_error\(\)
        153\│ 
        154\│ 

  RecursionError

  maximum recursion depth exceeded

  at {}:152 in recursion_error
      148\│     assert re.match\(expected, io.fetch_output\(\)\) is not None
      149\│ 
      150\│ 
      151\│ def recursion_error\(\):
    → 152\│     recursion_error\(\)
      153\│ 
      154\│ 
      155\│ @pytest.mark.skipif\(
      156\│     not PY36, reason="Better error messages are only available for Python \^3\.6"
""".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
Beispiel #11
0
    def _execute_operation(self, operation):
        try:
            if self.supports_fancy_output():
                if id(operation) not in self._sections:
                    if self._should_write_operation(operation):
                        with self._lock:
                            self._sections[id(operation)] = self._io.section()
                            self._sections[id(operation)].write_line(
                                "  <fg=blue;options=bold>•</> {message}: <fg=blue>Pending...</>"
                                .format(message=self.get_operation_message(
                                    operation), ), )
            else:
                if self._should_write_operation(operation):
                    if not operation.skipped:
                        self._io.write_line(
                            "  <fg=blue;options=bold>•</> {message}".format(
                                message=self.get_operation_message(
                                    operation), ), )
                    else:
                        self._io.write_line(
                            "  <fg=default;options=bold,dark>•</> {message}: "
                            "<fg=default;options=bold,dark>Skipped</> "
                            "<fg=default;options=dark>for the following reason:</> "
                            "<fg=default;options=bold,dark>{reason}</>".format(
                                message=self.get_operation_message(operation),
                                reason=operation.skip_reason,
                            ))

            try:
                result = self._do_execute_operation(operation)
            except EnvCommandError as e:
                if e.e.returncode == -2:
                    result = -2
                else:
                    raise

            # If we have a result of -2 it means a KeyboardInterrupt
            # in the any python subprocess, so we raise a KeyboardInterrupt
            # error to be picked up by the error handler.
            if result == -2:
                raise KeyboardInterrupt
        except Exception as e:
            try:
                from clikit.ui.components.exception_trace import ExceptionTrace

                if not self.supports_fancy_output():
                    io = self._io
                else:
                    message = "  <error>•</error> {message}: <error>Failed</error>".format(
                        message=self.get_operation_message(operation,
                                                           error=True), )
                    self._write(operation, message)
                    io = self._sections.get(id(operation), self._io)

                with self._lock:
                    trace = ExceptionTrace(e)
                    trace.render(io)
                    io.write_line("")
            finally:
                with self._lock:
                    self._shutdown = True
        except KeyboardInterrupt:
            try:
                message = "  <warning>•</warning> {message}: <warning>Cancelled</warning>".format(
                    message=self.get_operation_message(operation,
                                                       warning=True), )
                if not self.supports_fancy_output():
                    self._io.write_line(message)
                else:
                    self._write(operation, message)
            finally:
                with self._lock:
                    self._shutdown = True
Beispiel #12
0
def test_render_shows_ignored_files_if_in_debug_mode():
    import os

    from .helpers import outer

    io = BufferedIO()
    io.set_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  {}:352 in test_render_shows_ignored_files_if_in_debug_mode
      350│ 
      351│     with pytest.raises(Exception) as e:
    → 352│         call()
      353│ 
      354│     trace = ExceptionTrace(e.value)

  3  {}:349 in call
      347│             outer()
      348│ 
    → 349│         run()
      350│ 
      351│     with pytest.raises(Exception) as e:

  2  {}:347 in run
      345│     def call():
      346│         def run():
    → 347│             outer()
      348│ 
      349│         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()