Exemplo n.º 1
0
def test_init_launch_hostapp(setup_teardown, hostapp_args):  # noqa: F811
    with TSession(hostapp_args) as ts:
        print("Session is run")
        ts.send_request(REQUEST_INIT)
        ts.send_request(REQUEST_LAUNCH)
        # if we have this event, GDB successfully loaded and started the application:
        expectation = timeline.Event("initialized")
        result = ts.wait_for(expectation, timeout_s=5)
        assert result
    print("Session is stopped")
Exemplo n.º 2
0
    def _start_channel(self, stream):
        handlers = messaging.MessageHandlers(
            request=self._process_request,
            event=self._process_event,
            disconnect=self._process_disconnect,
        )
        self.channel = messaging.JsonMessageChannel(stream, handlers)
        self.channel.start()

        self.wait_for_next(
            timeline.Event(
                "output",
                {
                    "category": "telemetry",
                    "output": "ptvsd",
                    "data": {
                        "packageVersion": some.str
                    },
                },
            )
            & timeline.Event(
                "output",
                {
                    "category": "telemetry",
                    "output": "debugpy",
                    "data": {
                        "packageVersion": some.str
                    },
                },
            ))

        self.request(
            "initialize",
            {
                "pathFormat": "path",
                "clientID": self.client_id,
                "adapterID": "test",
                "linesStartAt1": True,
                "columnsStartAt1": True,
                "supportsVariableType": True,
                "supportsRunInTerminalRequest": True,
            },
        )
Exemplo n.º 3
0
def attach_listen(session, target, method, cwd=None, log_dir=None):
    log.info("Attaching {0} to {1} by socket using {2}.", session, target,
             method.upper())

    assert method in ("api", "cli")

    config = _attach_common_config(session, target, cwd)
    config["listen"] = {}
    config["listen"]["host"] = host = attach_listen.host
    config["listen"]["port"] = port = attach_listen.port

    if method == "cli":
        args = [
            os.path.dirname(debugpy.__file__),
            "--connect",
            compat.filename_str(host) + ":" + str(port),
        ]
        if log_dir is not None:
            args += ["--log-to", log_dir]
        if "subProcess" in config:
            args += ["--configure-subProcess", str(config["subProcess"])]
        debuggee_setup = None
    elif method == "api":
        args = []
        api_config = {k: v for k, v in config.items() if k in {"subProcess"}}
        debuggee_setup = """
import debugpy
if {log_dir!r}:
    debugpy.log_to({log_dir!r})
debugpy.configure({api_config!r})
debugpy.connect({address!r})
"""
        debuggee_setup = fmt(debuggee_setup,
                             address=(host, port),
                             log_dir=log_dir,
                             api_config=api_config)
    else:
        raise ValueError
    args += target.cli(session.spawn_debuggee.env)

    try:
        del config["subProcess"]
    except KeyError:
        pass

    def spawn_debuggee(occ):
        assert occ.body == some.dict.containing({"host": host, "port": port})
        session.spawn_debuggee(args, cwd=cwd, setup=debuggee_setup)

    session.timeline.when(timeline.Event("debugpyWaitingForServer"),
                          spawn_debuggee)
    session.spawn_adapter(
        args=[] if log_dir is None else ["--log-dir", log_dir])
    return session.request_attach()
Exemplo n.º 4
0
    def disconnect(self, force=False):
        if self.channel is None:
            return

        try:
            if not force:
                self.request("disconnect")
                self.timeline.wait_until_realized(timeline.Event("terminated"))
        except messaging.JsonIOError:
            pass
        finally:
            try:
                self.channel.close()
            except Exception:
                pass
            self.channel.wait()
            self.channel = None
Exemplo n.º 5
0
    def wait_for_exit(self):
        if self.debuggee is not None:
            try:
                self.debuggee.wait()
            except Exception:
                pass
            finally:
                watchdog.unregister_spawn(self.debuggee.pid, self.debuggee_id)

        self.timeline.wait_until_realized(timeline.Event("terminated"))

        # FIXME: "exited" event is not properly reported in attach scenarios at the
        # moment, so the exit code is only checked if it's present.
        if self.start_request.command == "launch":
            assert self.exit_code is not None
        if self.debuggee is not None and self.exit_code is not None:
            assert self.debuggee.returncode == self.exit_code
        return self.exit_code
Exemplo n.º 6
0
    def _request_start(self, method):
        self.config.normalize()
        start_request = self.send_request(method, self.config)

        # Depending on whether it's "noDebug" or not, we either get the "initialized"
        # event, or an immediate response to our request.
        self.timeline.wait_until_realized(
            timeline.Event("initialized") | timeline.Response(start_request),
            freeze=True,
        )

        if start_request.response is not None:
            # It was an immediate response - configuration is not possible. Just get
            # the "process" event, and return to caller.
            return self.wait_for_process()

        # We got "initialized" - now we need to yield to the caller, so that it can
        # configure the session before it starts running.
        return self._ConfigurationContextManager(self)
Exemplo n.º 7
0
 def wait_for_terminated(self):
     self.timeline.wait_until_realized(timeline.Event("terminated"))
Exemplo n.º 8
0
    def __init__(self, debug_config=None):
        assert Session.tmpdir is not None
        watchdog.start()

        self.id = next(Session._counter)
        log.info("Starting {0}", self)

        self.client_id = "vscode"

        self.debuggee = None
        """psutil.Popen instance for the debuggee process."""

        self.adapter = None
        """psutil.Popen instance for the adapter process."""

        self.adapter_endpoints = None
        """Name of the file that contains the adapter endpoints information.

        This file is generated by the adapter when it opens the listener sockets,
        and deleted by it when it exits.
        """

        self.channel = None
        """JsonMessageChannel to the adapter."""

        self.captured_output = {"stdout", "stderr"}
        """Before the debuggee is spawned, this is the set of stdio streams that
        should be captured once it is spawned.

        After it is spawned, this is a CapturedOutput object capturing those streams.
        """

        self.backchannel = None
        """The BackChannel object to talk to the debuggee.

        Must be explicitly created with open_backchannel().
        """

        self.scratchpad = comms.ScratchPad(self)
        """The ScratchPad object to talk to the debuggee."""

        self.start_request = None
        """The "launch" or "attach" request that started executing code in this session.
        """

        self.expected_exit_code = 0
        """The expected exit code for the debuggee process.

        If None, the debuggee is not expected to exit when the Session is closed.

        If not None, this is validated against both exit_code and debuggee.returncode.
        """

        self.exit_code = None
        """The actual exit code for the debuggee process, as received from DAP.
        """

        self.config = config.DebugConfig(
            debug_config if debug_config is not None else {
                "justMyCode": True,
                "name": "Test",
                "type": "python"
            })
        """The debug configuration for this session."""

        self.before_request = lambda command, arguments: None
        """Invoked for every outgoing request in this session, allowing any final
        tweaks to the request before it is sent.
        """

        self.log_dir = (None if log.log_dir is None else
                        py.path.local(log.log_dir) / str(self))
        """The log directory for this session. Passed via DEBUGPY_LOG_DIR to all spawned
        child processes.

        If set to None, DEBUGPY_LOG_DIR is not automatically added, but tests can still
        provide it manually.
        """

        self.tmpdir = Session.tmpdir / str(self)
        self.tmpdir.ensure(dir=True)

        self.timeline = timeline.Timeline(str(self))
        self.ignore_unobserved.extend([
            timeline.Event("module"),
            timeline.Event("continued"),
            timeline.Event("debugpyWaitingForServer"),
            timeline.Event("thread",
                           some.dict.containing({"reason": "started"})),
            timeline.Event("thread", some.dict.containing({"reason":
                                                           "exited"})),
            timeline.Event("output",
                           some.dict.containing({"category": "stdout"})),
            timeline.Event("output",
                           some.dict.containing({"category": "stderr"})),
            timeline.Event("output",
                           some.dict.containing({"category": "console"})),
        ])

        # Expose some common members of timeline directly - these should be the ones
        # that are the most straightforward to use, and are difficult to use incorrectly.
        # Conversely, most tests should restrict themselves to this subset of the API,
        # and avoid calling members of timeline directly unless there is a good reason.
        self.new = self.timeline.new
        self.observe = self.timeline.observe
        self.wait_for_next = self.timeline.wait_for_next
        self.proceed = self.timeline.proceed
        self.expect_new = self.timeline.expect_new
        self.expect_realized = self.timeline.expect_realized
        self.all_occurrences_of = self.timeline.all_occurrences_of
        self.observe_all = self.timeline.observe_all

        spawn_adapter = self.spawn_adapter
        self.spawn_adapter = lambda *args, **kwargs: spawn_adapter(
            *args, **kwargs)
        self.spawn_adapter.env = util.Env()

        spawn_debuggee = self.spawn_debuggee
        self.spawn_debuggee = lambda *args, **kwargs: spawn_debuggee(
            *args, **kwargs)
        self.spawn_debuggee.env = util.Env()
Exemplo n.º 9
0
 def wait_for_next_event(self, event, body=some.object, freeze=True):
     return self.timeline.wait_for_next(timeline.Event(event, body),
                                        freeze=freeze).body
Exemplo n.º 10
0
 def all_events(self, event, body=some.object):
     return [
         occ.body for occ in self.timeline.all_occurrences_of(
             timeline.Event(event, body))
     ]
Exemplo n.º 11
0
def test_evaluate_thread_locks(pyfile, target, run):
    @pyfile
    def code_to_debug():
        """
        The idea here is that a secondary thread does the processing of instructions,
        so, when all threads are stopped, doing an evaluation for:

        processor.process('xxx')

        would be locked until secondary threads start running.
        See: https://github.com/microsoft/debugpy/issues/157
        """

        import debuggee
        import threading
        from debugpy.common.compat import queue

        debuggee.setup()

        class EchoThread(threading.Thread):
            def __init__(self, queue):
                threading.Thread.__init__(self)
                self._queue = queue

            def run(self):
                while True:
                    obj = self._queue.get()
                    if obj == "finish":
                        break

                    print("processed", obj.value)
                    obj.event.set()

        class NotificationObject(object):
            def __init__(self, value):
                self.value = value
                self.event = threading.Event()

        class Processor(object):
            def __init__(self, queue):
                self._queue = queue

            def process(self, i):
                obj = NotificationObject(i)
                self._queue.put(obj)
                assert obj.event.wait()

            def finish(self):
                self._queue.put("finish")

        if __name__ == "__main__":
            q = queue.Queue()
            echo_thread = EchoThread(q)
            processor = Processor(q)
            echo_thread.start()

            processor.process(1)
            processor.process(2)  # @bp
            processor.process(3)
            processor.finish()

    with debug.Session() as session:

        # During the evaluation we'll actually have continued/stopped events because
        # we're letting threads run at that time. Let's ignore these in the test.
        session.ignore_unobserved.extend([timeline.Event("stopped")])

        session.config.env.update({"PYDEVD_UNBLOCK_THREADS_TIMEOUT": "0.5"})

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

        stop = session.wait_for_stop()

        evaluate = session.request(
            "evaluate",
            {
                "expression": "processor.process('foo')",
                "frameId": stop.frame_id
            },
        )
        assert evaluate == some.dict.containing({"result": "None"})

        session.request_continue()