Пример #1
0
def test_module_events(pyfile, target, run):
    @pyfile
    def module2():
        def do_more_things():
            print("done")  # @bp

    @pyfile
    def module1():
        import module2

        def do_something():
            module2.do_more_things()

    @pyfile
    def test_code():
        import debuggee

        debuggee.setup()

        from module1 import do_something

        do_something()

    with debug.Session() as session:
        with run(session, target(test_code)):
            session.set_breakpoints(module2, all)

        session.wait_for_stop(
            "breakpoint", expected_frames=[some.dap.frame(module2, line="bp")]
        )

        # Stack trace after the stop will trigger module events, but they are only
        # sent after the trace response, so we need to wait for them separately.
        # The order isn't guaranteed, either, so just wait for any 3 modules.
        session.timeline.wait_until_realized(
            Event("module") >> Event("module") >> Event("module")
        )
        modules = {
            event.body["module"]["name"]: event.body["module"]["path"]
            for event in session.all_occurrences_of(Event("module"))
        }
        assert modules == some.dict.containing(
            {
                "__main__": some.path(test_code),
                "module1": some.path(module1),
                "module2": some.path(module2),
            }
        )

        session.request_continue()
Пример #2
0
def source(path, **kwargs):
    """Matches DAP Source objects.
    """
    if isinstance(path, py.path.local):
        path = some.path(path)
    d = {"path": path}
    d.update(kwargs)
    return some.dict.containing(d)
Пример #3
0
def test_exception_stack(pyfile, target, run, max_frames):
    @pyfile
    def code_to_debug():
        import debuggee

        debuggee.setup()

        def do_something(n):
            if n <= 0:
                raise ArithmeticError("bad code")  # @unhandled
            do_something2(n - 1)

        def do_something2(n):
            do_something(n - 1)

        do_something(100)

    with debug.Session() as session:
        session.expected_exit_code = some.int

        max_frames, (min_expected_lines, max_expected_lines) = {
            "all": (0, (100, 221)),
            "default": (None, (100, 221)),
            10: (10, (10, 21)),
        }[max_frames]
        if max_frames is not None:
            session.config["maxExceptionStackFrames"] = max_frames

        with run(session, target(code_to_debug)):
            session.request("setExceptionBreakpoints",
                            {"filters": ["uncaught"]})

        stop = session.wait_for_stop(
            "exception",
            expected_frames=[some.dap.frame(code_to_debug, line="unhandled")],
        )
        exc_info = session.request("exceptionInfo",
                                   {"threadId": stop.thread_id})
        expected_exc_info = some.dict.containing({
            "exceptionId":
            str_matching_ArithmeticError,
            "description":
            "bad code",
            "breakMode":
            "unhandled",
            "details":
            some.dict.containing({
                "typeName": str_matching_ArithmeticError,
                "message": "bad code",
                "source": some.path(code_to_debug),
            }),
        })
        assert expected_exc_info == exc_info
        stack_str = exc_info["details"]["stackTrace"]
        stack_line_count = len(stack_str.split("\n"))
        assert min_expected_lines <= stack_line_count <= max_expected_lines

        session.request_continue()
Пример #4
0
def test_vsc_exception_options_raise_with_except(pyfile, target, run, raised,
                                                 uncaught):
    @pyfile
    def code_to_debug():
        import debuggee

        debuggee.setup()

        def raise_with_except():
            try:
                raise ArithmeticError("bad code")  # @exc
            except Exception:
                pass

        raise_with_except()

    with debug.Session() as session:
        session.expected_exit_code = some.int
        with run(session, target(code_to_debug)):
            session.request("setExceptionBreakpoints",
                            {"filters": list({raised, uncaught} - {""})})

        expected = some.dict.containing({
            "exceptionId":
            str_matching_ArithmeticError,
            "description":
            "bad code",
            "breakMode":
            "always" if raised else "unhandled",
            "details":
            some.dict.containing({
                "typeName": str_matching_ArithmeticError,
                "message": "bad code",
                "source": some.path(code_to_debug),
            }),
        })

        if raised:
            stop = session.wait_for_stop(
                "exception",
                expected_text=str_matching_ArithmeticError,
                expected_description="bad code",
                expected_frames=[some.dap.frame(code_to_debug, line="exc")],
            )
            exc_info = session.request("exceptionInfo",
                                       {"threadId": stop.thread_id})
            assert exc_info == expected
            session.request_continue()

        if uncaught:
            # Exception is caught by try..except, so there should be no stop.
            pass
Пример #5
0
def test_flask_exception_no_multiproc(start_flask, exc_type):
    exc_line = lines.app_py["exc_" + exc_type]

    with debug.Session() as session:
        with start_flask(session):
            session.request(
                "setExceptionBreakpoints", {"filters": ["raised", "uncaught"]}
            )

        with flask_server:
            flask_server.get("/" + exc_type)
            stopped = session.wait_for_stop(
                "exception",
                expected_frames=[
                    some.dap.frame(
                        some.dap.source(paths.app_py),
                        line=exc_line,
                        name="bad_route_" + exc_type,
                    )
                ],
            ).body

            assert stopped == some.dict.containing(
                {
                    "reason": "exception",
                    "text": some.str.ending_with("ArithmeticError"),
                    "description": "Hello",
                }
            )

            exception_info = session.request(
                "exceptionInfo", {"threadId": stopped["threadId"]}
            )

            assert exception_info == {
                "exceptionId": some.str.ending_with("ArithmeticError"),
                "breakMode": "always",
                "description": "Hello",
                "details": {
                    "message": "Hello",
                    "typeName": some.str.ending_with("ArithmeticError"),
                    "source": some.path(paths.app_py),
                    "stackTrace": some.str,
                },
            }

            session.request_continue()
Пример #6
0
def test_breakpoint_in_nonexistent_file(pyfile, target, run):
    @pyfile
    def code_to_debug():
        import debug_me  # noqa

    with debug.Session() as session:
        with run(session, target(code_to_debug)):
            breakpoints = session.set_breakpoints("nonexistent_file.py", [1])
            assert breakpoints == [{
                "verified":
                False,
                "message":
                "Breakpoint in file that does not exist.",
                "source":
                some.dict.containing(
                    {"path": some.path("nonexistent_file.py")}),
                "line":
                1,
            }]
Пример #7
0
def test_with_dot_remote_root(pyfile, long_tmpdir, target, run):
    @pyfile
    def code_to_debug():
        import os
        import debuggee
        from debuggee import backchannel

        debuggee.setup()
        backchannel.send(os.path.abspath(__file__))
        print("done")  # @bp

    dir_local = long_tmpdir.mkdir("local")
    dir_remote = long_tmpdir.mkdir("remote")

    path_local = dir_local / "code_to_debug.py"
    path_remote = dir_remote / "code_to_debug.py"

    code_to_debug.copy(path_local)
    code_to_debug.copy(path_remote)

    with debug.Session() as session:
        session.config["pathMappings"] = [{
            "localRoot": dir_local,
            "remoteRoot": "."
        }]

        backchannel = session.open_backchannel()
        with run(session, target(path_remote), cwd=dir_remote):
            # Set breakpoints using local path. This tests that local paths are
            # mapped to remote paths.
            session.set_breakpoints(path_local, all)

        actual_path_remote = backchannel.receive()
        assert some.path(actual_path_remote) == path_remote

        session.wait_for_stop(
            "breakpoint",
            expected_frames=[
                some.dap.frame(some.dap.source(path_local), line="bp")
            ],
        )

        session.request_continue()
Пример #8
0
def test_justmycode_frames(pyfile, target, run, jmc):
    @pyfile
    def code_to_debug():
        import debuggee

        debuggee.setup()

        import this  # @bp

        assert this

    with debug.Session() as session:
        session.config["justMyCode"] = bool(jmc)

        with run(session, target(code_to_debug)):
            session.set_breakpoints(code_to_debug, all)

        stop = session.wait_for_stop(
            "breakpoint",
            expected_frames=[some.dap.frame(code_to_debug, "bp")])
        if jmc:
            assert len(stop.frames) == 1
        else:
            assert len(stop.frames) >= 1

        session.request("stepIn", {"threadId": stop.thread_id})

        # With JMC, it should step out of the function, remaining in the same file.
        # Without JMC, it should step into stdlib.
        expected_path = some.path(code_to_debug)
        if not jmc:
            expected_path = ~expected_path
        session.wait_for_stop(
            "step",
            expected_frames=[
                some.dap.frame(some.dap.source(expected_path), some.int)
            ],
        )

        session.request_continue()
Пример #9
0
def test_with_path_mappings(pyfile, long_tmpdir, target, run):
    @pyfile
    def code_to_debug():
        import debuggee
        import os
        import sys
        from debuggee import backchannel

        debuggee.setup()
        backchannel.send(os.path.abspath(__file__))
        call_me_back_dir = backchannel.receive()
        sys.path.insert(0, call_me_back_dir)

        import call_me_back

        def call_func():
            print("break here")  # @bp

        call_me_back.call_me_back(call_func)  # @call_me_back
        print("done")

    dir_local = long_tmpdir.mkdir("local")
    dir_remote = long_tmpdir.mkdir("remote")

    path_local = dir_local / "code_to_debug.py"
    path_remote = dir_remote / "code_to_debug.py"

    code_to_debug.copy(path_local)
    code_to_debug.copy(path_remote)

    call_me_back_dir = test_data / "call_me_back"
    call_me_back_py = call_me_back_dir / "call_me_back.py"

    with debug.Session() as session:
        session.config["pathMappings"] = [{
            "localRoot": dir_local,
            "remoteRoot": dir_remote
        }]

        backchannel = session.open_backchannel()
        with run(session, target(path_remote)):
            # Set breakpoints using local path. This tests that local paths are
            # mapped to remote paths.
            session.set_breakpoints(path_local, ["bp"])

        actual_path_remote = backchannel.receive()
        assert some.path(actual_path_remote) == path_remote
        backchannel.send(call_me_back_dir)

        stop = session.wait_for_stop(
            "breakpoint",
            expected_frames=[
                some.dap.frame(
                    # Mapped files should not have a sourceReference, so that the IDE
                    # doesn't try to fetch them instead of opening the local file.
                    some.dap.source(path_local, sourceReference=0),
                    line="bp",
                ),
                some.dap.frame(
                    # Unmapped files should have a sourceReference, since there's no
                    # local file for the IDE to open.
                    some.dap.source(call_me_back_py,
                                    sourceReference=some.int.not_equal_to(0)),
                    line="callback",
                ),
                some.dap.frame(
                    # Mapped files should not have a sourceReference, so that the IDE
                    # doesn't try to fetch them instead of opening the local file.
                    some.dap.source(path_local, sourceReference=0),
                    line="call_me_back",
                ),
            ],
        )

        srcref = stop.frames[1]["source"]["sourceReference"]

        try:
            session.request("source", {"sourceReference": 0})
        except Exception as exc:
            assert "Source unavailable" in str(exc)
        else:
            pytest.fail("sourceReference=0 should not be valid")

        source = session.request("source", {"sourceReference": srcref})
        assert "def call_me_back(callback):" in source["content"]

        session.request_continue()
Пример #10
0
def test_vsc_exception_options_raise_without_except(pyfile, target, run,
                                                    raised, uncaught):
    @pyfile
    def code_to_debug():
        import debuggee

        debuggee.setup()

        def raise_without_except():
            raise ArithmeticError("bad code")  # @exc

        raise_without_except()

    with debug.Session() as session:
        session.ignore_unobserved.append(Event("stopped"))
        session.expected_exit_code = some.int

        with run(session, target(code_to_debug)):
            session.request("setExceptionBreakpoints",
                            {"filters": list({raised, uncaught} - {""})})

        expected_exc_info = some.dict.containing({
            "exceptionId":
            str_matching_ArithmeticError,
            "description":
            "bad code",
            "breakMode":
            "always" if raised else "unhandled",
            "details":
            some.dict.containing({
                "typeName": str_matching_ArithmeticError,
                "message": "bad code",
                "source": some.path(code_to_debug),
            }),
        })

        if raised:
            stop = session.wait_for_stop(
                "exception",
                expected_frames=[some.dap.frame(code_to_debug, line="exc")])
            exc_info = session.request("exceptionInfo",
                                       {"threadId": stop.thread_id})
            assert expected_exc_info == exc_info
            session.request_continue()

            # NOTE: debugger stops at each frame if raised and is uncaught
            # This behavior can be changed by updating 'notify_on_handled_exceptions'
            # setting we send to pydevd to notify only once. In our test code, we have
            # two frames, hence two stops.
            session.wait_for_stop("exception")
            session.request_continue()

        if uncaught:
            stop = session.wait_for_stop(
                "exception",
                expected_frames=[some.dap.frame(code_to_debug, line="exc")])
            expected_exc_info = some.dict.containing({
                "exceptionId":
                str_matching_ArithmeticError,
                "description":
                "bad code",
                "breakMode":
                "unhandled",  # Only difference from previous expected is breakMode.
                "details":
                some.dict.containing({
                    "typeName": str_matching_ArithmeticError,
                    "message": "bad code",
                    "source": some.path(code_to_debug),
                }),
            })
            exc_info = session.request("exceptionInfo",
                                       {"threadId": stop.thread_id})
            assert expected_exc_info == exc_info
            session.request_continue()