1"""
2Test lldb-vscode runInTerminal reverse request
3"""
4
5
6import unittest2
7import vscode
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11import lldbvscode_testcase
12import time
13import os
14import subprocess
15import shutil
16import json
17from threading import Thread
18
19
20class TestVSCode_runInTerminal(lldbvscode_testcase.VSCodeTestCaseBase):
21
22    mydir = TestBase.compute_mydir(__file__)
23
24    def readPidMessage(self, fifo_file):
25        with open(fifo_file, "r") as file:
26            self.assertIn("pid", file.readline())
27
28    def sendDidAttachMessage(self, fifo_file):
29        with open(fifo_file, "w") as file:
30            file.write(json.dumps({"kind": "didAttach"}) + "\n")
31
32    def readErrorMessage(self, fifo_file):
33        with open(fifo_file, "r") as file:
34            return file.readline()
35
36    def isTestSupported(self):
37        # For some strange reason, this test fails on python3.6
38        if not (sys.version_info.major == 3 and sys.version_info.minor >= 7):
39            return False
40        try:
41            # We skip this test for debug builds because it takes too long parsing lldb's own
42            # debug info. Release builds are fine.
43            # Checking the size of the lldb-vscode binary seems to be a decent proxy for a quick
44            # detection. It should be far less than 1 MB in Release builds.
45            if os.path.getsize(os.environ["LLDBVSCODE_EXEC"]) < 1000000:
46                return True
47        except:
48            return False
49
50    @skipIfWindows
51    @skipIfRemote
52    @skipIf(archs=no_match(['x86_64']))
53    def test_runInTerminal(self):
54        if not self.isTestSupported():
55            return
56        '''
57            Tests the "runInTerminal" reverse request. It makes sure that the IDE can
58            launch the inferior with the correct environment variables and arguments.
59        '''
60        program = self.getBuildArtifact("a.out")
61        source = 'main.c'
62        self.build_and_launch(
63            program, stopOnEntry=True, runInTerminal=True, args=["foobar"],
64            env=["FOO=bar"])
65
66        breakpoint_line = line_number(source, '// breakpoint')
67
68        self.set_source_breakpoints(source, [breakpoint_line])
69        self.continue_to_next_stop()
70
71        # We verify we actually stopped inside the loop
72        counter = int(self.vscode.get_local_variable_value('counter'))
73        self.assertTrue(counter > 0)
74
75        # We verify we were able to set the launch arguments
76        argc = int(self.vscode.get_local_variable_value('argc'))
77        self.assertEqual(argc, 2)
78
79        argv1 = self.vscode.request_evaluate('argv[1]')['body']['result']
80        self.assertIn('foobar', argv1)
81
82        # We verify we were able to set the environment
83        env = self.vscode.request_evaluate('foo')['body']['result']
84        self.assertIn('bar', env)
85
86    @skipIfWindows
87    @skipIfRemote
88    @skipIf(archs=no_match(['x86_64']))
89    def test_runInTerminalInvalidTarget(self):
90        if not self.isTestSupported():
91            return
92        self.build_and_create_debug_adaptor()
93        response = self.launch(
94            "INVALIDPROGRAM", stopOnEntry=True, runInTerminal=True, args=["foobar"], env=["FOO=bar"], expectFailure=True)
95        self.assertFalse(response['success'])
96        self.assertIn("Could not create a target for a program 'INVALIDPROGRAM': unable to find executable",
97            response['message'])
98
99    @skipIfWindows
100    @skipIfRemote
101    @skipIf(archs=no_match(['x86_64']))
102    def test_missingArgInRunInTerminalLauncher(self):
103        if not self.isTestSupported():
104            return
105        proc = subprocess.run([self.lldbVSCodeExec,  "--launch-target", "INVALIDPROGRAM"],
106            capture_output=True, universal_newlines=True)
107        self.assertTrue(proc.returncode != 0)
108        self.assertIn('"--launch-target" requires "--comm-file" to be specified', proc.stderr)
109
110    @skipIfWindows
111    @skipIfRemote
112    @skipIf(archs=no_match(['x86_64']))
113    def test_FakeAttachedRunInTerminalLauncherWithInvalidProgram(self):
114        if not self.isTestSupported():
115            return
116        comm_file = os.path.join(self.getBuildDir(), "comm-file")
117        os.mkfifo(comm_file)
118
119        proc = subprocess.Popen(
120            [self.lldbVSCodeExec, "--comm-file", comm_file, "--launch-target", "INVALIDPROGRAM"],
121            universal_newlines=True, stderr=subprocess.PIPE)
122
123        self.readPidMessage(comm_file)
124        self.sendDidAttachMessage(comm_file)
125        self.assertIn("No such file or directory", self.readErrorMessage(comm_file))
126
127        _, stderr = proc.communicate()
128        self.assertIn("No such file or directory", stderr)
129
130    @skipIfWindows
131    @skipIfRemote
132    @skipIf(archs=no_match(['x86_64']))
133    def test_FakeAttachedRunInTerminalLauncherWithValidProgram(self):
134        if not self.isTestSupported():
135            return
136        comm_file = os.path.join(self.getBuildDir(), "comm-file")
137        os.mkfifo(comm_file)
138
139        proc = subprocess.Popen(
140            [self.lldbVSCodeExec, "--comm-file", comm_file, "--launch-target", "echo", "foo"],
141            universal_newlines=True, stdout=subprocess.PIPE)
142
143        self.readPidMessage(comm_file)
144        self.sendDidAttachMessage(comm_file)
145
146        stdout, _ = proc.communicate()
147        self.assertIn("foo", stdout)
148
149    @skipIfWindows
150    @skipIfRemote
151    @skipIf(archs=no_match(['x86_64']))
152    def test_FakeAttachedRunInTerminalLauncherAndCheckEnvironment(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            [self.lldbVSCodeExec, "--comm-file", comm_file, "--launch-target", "env"],
160            universal_newlines=True, stdout=subprocess.PIPE,
161            env={**os.environ, "FOO": "BAR"})
162
163        self.readPidMessage(comm_file)
164        self.sendDidAttachMessage(comm_file)
165
166        stdout, _ = proc.communicate()
167        self.assertIn("FOO=BAR", stdout)
168
169    @skipIfWindows
170    @skipIfRemote
171    @skipIf(archs=no_match(['x86_64']))
172    def test_NonAttachedRunInTerminalLauncher(self):
173        if not self.isTestSupported():
174            return
175        comm_file = os.path.join(self.getBuildDir(), "comm-file")
176        os.mkfifo(comm_file)
177
178        proc = subprocess.Popen(
179            [self.lldbVSCodeExec, "--comm-file", comm_file, "--launch-target", "echo", "foo"],
180            universal_newlines=True, stderr=subprocess.PIPE,
181            env={**os.environ, "LLDB_VSCODE_RIT_TIMEOUT_IN_MS": "1000"})
182
183        self.readPidMessage(comm_file)
184
185        _, stderr = proc.communicate()
186        self.assertIn("Timed out trying to get messages from the debug adaptor", stderr)
187