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