xref: /llvm-project/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py (revision b43e003d90f0a7c045e9961a80013fc9c6d6e9b3)
1"""
2Test lldb-dap runInTerminal reverse request
3"""
4
5
6import dap_server
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10import lldbdap_testcase
11import time
12import os
13import subprocess
14import shutil
15import json
16from threading import Thread
17
18
19class TestDAP_runInTerminal(lldbdap_testcase.DAPTestCaseBase):
20    def readPidMessage(self, fifo_file):
21        with open(fifo_file, "r") as file:
22            self.assertIn("pid", file.readline())
23
24    def sendDidAttachMessage(self, fifo_file):
25        with open(fifo_file, "w") as file:
26            file.write(json.dumps({"kind": "didAttach"}) + "\n")
27
28    def readErrorMessage(self, fifo_file):
29        with open(fifo_file, "r") as file:
30            return file.readline()
31
32    def isTestSupported(self):
33        # For some strange reason, this test fails on python3.6
34        if not (sys.version_info.major == 3 and sys.version_info.minor >= 7):
35            return False
36        try:
37            # We skip this test for debug builds because it takes too long parsing lldb's own
38            # debug info. Release builds are fine.
39            # Checking the size of the lldb-dap binary seems to be a decent proxy for a quick
40            # detection. It should be far less than 1 MB in Release builds.
41            if os.path.getsize(os.environ["LLDBDAP_EXEC"]) < 1000000:
42                return True
43        except:
44            return False
45
46    @skipIfWindows
47    @skipIf(archs=no_match(["x86_64"]))
48    def test_runInTerminal(self):
49        if not self.isTestSupported():
50            return
51        """
52            Tests the "runInTerminal" reverse request. It makes sure that the IDE can
53            launch the inferior with the correct environment variables and arguments.
54        """
55        program = self.getBuildArtifact("a.out")
56        source = "main.c"
57        self.build_and_launch(
58            program, runInTerminal=True, args=["foobar"], env=["FOO=bar"]
59        )
60
61        self.assertEqual(
62            len(self.dap_server.reverse_requests),
63            1,
64            "make sure we got a reverse request",
65        )
66
67        request = self.dap_server.reverse_requests[0]
68        self.assertIn(self.lldbDAPExec, request["arguments"]["args"])
69        self.assertIn(program, request["arguments"]["args"])
70        self.assertIn("foobar", request["arguments"]["args"])
71        self.assertIn("FOO", request["arguments"]["env"])
72
73        breakpoint_line = line_number(source, "// breakpoint")
74
75        self.set_source_breakpoints(source, [breakpoint_line])
76        self.continue_to_next_stop()
77
78        # We verify we actually stopped inside the loop
79        counter = int(self.dap_server.get_local_variable_value("counter"))
80        self.assertGreater(counter, 0)
81
82        # We verify we were able to set the launch arguments
83        argc = int(self.dap_server.get_local_variable_value("argc"))
84        self.assertEqual(argc, 2)
85
86        argv1 = self.dap_server.request_evaluate("argv[1]")["body"]["result"]
87        self.assertIn("foobar", argv1)
88
89        # We verify we were able to set the environment
90        env = self.dap_server.request_evaluate("foo")["body"]["result"]
91        self.assertIn("bar", env)
92
93    @skipIf(archs=no_match(["x86_64"]))
94    def test_runInTerminalWithObjectEnv(self):
95        if not self.isTestSupported():
96            return
97        """
98            Tests the "runInTerminal" reverse request. It makes sure that the IDE can
99            launch the inferior with the correct environment variables using an object.
100        """
101        program = self.getBuildArtifact("a.out")
102        self.build_and_launch(program, runInTerminal=True, env={"FOO": "BAR"})
103
104        self.assertEqual(
105            len(self.dap_server.reverse_requests),
106            1,
107            "make sure we got a reverse request",
108        )
109
110        request = self.dap_server.reverse_requests[0]
111        request_envs = request["arguments"]["env"]
112
113        self.assertIn("FOO", request_envs)
114        self.assertEqual("BAR", request_envs["FOO"])
115
116    @skipIfWindows
117    @skipIf(archs=no_match(["x86_64"]))
118    def test_runInTerminalInvalidTarget(self):
119        if not self.isTestSupported():
120            return
121        self.build_and_create_debug_adaptor()
122        response = self.launch(
123            "INVALIDPROGRAM",
124            runInTerminal=True,
125            args=["foobar"],
126            env=["FOO=bar"],
127            expectFailure=True,
128        )
129        self.assertFalse(response["success"])
130        self.assertIn(
131            "Could not create a target for a program 'INVALIDPROGRAM': 'INVALIDPROGRAM' does not exist",
132            response["message"],
133        )
134
135    @skipIfWindows
136    @skipIf(archs=no_match(["x86_64"]))
137    def test_missingArgInRunInTerminalLauncher(self):
138        if not self.isTestSupported():
139            return
140        proc = subprocess.run(
141            [self.lldbDAPExec, "--launch-target", "INVALIDPROGRAM"],
142            capture_output=True,
143            universal_newlines=True,
144        )
145        self.assertNotEqual(proc.returncode, 0)
146        self.assertIn(
147            '"--launch-target" requires "--comm-file" to be specified', proc.stderr
148        )
149
150    @skipIfWindows
151    @skipIf(archs=no_match(["x86_64"]))
152    def test_FakeAttachedRunInTerminalLauncherWithInvalidProgram(self):
153        if not self.isTestSupported():
154            return
155        comm_file = os.path.join(self.getBuildDir(), "comm-file")
156        os.mkfifo(comm_file)
157
158        proc = subprocess.Popen(
159            [
160                self.lldbDAPExec,
161                "--comm-file",
162                comm_file,
163                "--launch-target",
164                "INVALIDPROGRAM",
165            ],
166            universal_newlines=True,
167            stderr=subprocess.PIPE,
168        )
169
170        self.readPidMessage(comm_file)
171        self.sendDidAttachMessage(comm_file)
172        self.assertIn("No such file or directory", self.readErrorMessage(comm_file))
173
174        _, stderr = proc.communicate()
175        self.assertIn("No such file or directory", stderr)
176
177    @skipIfWindows
178    @skipIf(archs=no_match(["x86_64"]))
179    def test_FakeAttachedRunInTerminalLauncherWithValidProgram(self):
180        if not self.isTestSupported():
181            return
182        comm_file = os.path.join(self.getBuildDir(), "comm-file")
183        os.mkfifo(comm_file)
184
185        proc = subprocess.Popen(
186            [
187                self.lldbDAPExec,
188                "--comm-file",
189                comm_file,
190                "--launch-target",
191                "echo",
192                "foo",
193            ],
194            universal_newlines=True,
195            stdout=subprocess.PIPE,
196        )
197
198        self.readPidMessage(comm_file)
199        self.sendDidAttachMessage(comm_file)
200
201        stdout, _ = proc.communicate()
202        self.assertIn("foo", stdout)
203
204    @skipIfWindows
205    @skipIf(archs=no_match(["x86_64"]))
206    def test_FakeAttachedRunInTerminalLauncherAndCheckEnvironment(self):
207        if not self.isTestSupported():
208            return
209        comm_file = os.path.join(self.getBuildDir(), "comm-file")
210        os.mkfifo(comm_file)
211
212        proc = subprocess.Popen(
213            [self.lldbDAPExec, "--comm-file", comm_file, "--launch-target", "env"],
214            universal_newlines=True,
215            stdout=subprocess.PIPE,
216            env={**os.environ, "FOO": "BAR"},
217        )
218
219        self.readPidMessage(comm_file)
220        self.sendDidAttachMessage(comm_file)
221
222        stdout, _ = proc.communicate()
223        self.assertIn("FOO=BAR", stdout)
224
225    @skipIfWindows
226    @skipIf(archs=no_match(["x86_64"]))
227    def test_NonAttachedRunInTerminalLauncher(self):
228        if not self.isTestSupported():
229            return
230        comm_file = os.path.join(self.getBuildDir(), "comm-file")
231        os.mkfifo(comm_file)
232
233        proc = subprocess.Popen(
234            [
235                self.lldbDAPExec,
236                "--comm-file",
237                comm_file,
238                "--launch-target",
239                "echo",
240                "foo",
241            ],
242            universal_newlines=True,
243            stderr=subprocess.PIPE,
244            env={**os.environ, "LLDB_DAP_RIT_TIMEOUT_IN_MS": "1000"},
245        )
246
247        self.readPidMessage(comm_file)
248
249        _, stderr = proc.communicate()
250        self.assertIn("Timed out trying to get messages from the debug adaptor", stderr)
251