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    @skipIfWindows
37    @skipIfRemote
38    @skipIf(archs=no_match(['x86_64']))
39    def test_runInTerminal(self):
40        '''
41            Tests the "runInTerminal" reverse request. It makes sure that the IDE can
42            launch the inferior with the correct environment variables and arguments.
43        '''
44        if "debug" in str(os.environ["LLDBVSCODE_EXEC"]).lower():
45            # We skip this test for debug builds because it takes too long parsing lldb's own
46            # debug info. Release builds are fine.
47            # Checking this environment variable seems to be a decent proxy for a quick
48            # detection
49            return
50        program = self.getBuildArtifact("a.out")
51        source = 'main.c'
52        self.build_and_launch(
53            program, stopOnEntry=True, runInTerminal=True, args=["foobar"],
54            env=["FOO=bar"])
55
56        breakpoint_line = line_number(source, '// breakpoint')
57
58        self.set_source_breakpoints(source, [breakpoint_line])
59        self.continue_to_next_stop()
60
61        # We verify we actually stopped inside the loop
62        counter = int(self.vscode.get_local_variable_value('counter'))
63        self.assertTrue(counter > 0)
64
65        # We verify we were able to set the launch arguments
66        argc = int(self.vscode.get_local_variable_value('argc'))
67        self.assertEqual(argc, 2)
68
69        argv1 = self.vscode.request_evaluate('argv[1]')['body']['result']
70        self.assertIn('foobar', argv1)
71
72        # We verify we were able to set the environment
73        env = self.vscode.request_evaluate('foo')['body']['result']
74        self.assertIn('bar', env)
75
76    @skipIfWindows
77    @skipIfRemote
78    @skipIf(archs=no_match(['x86_64']))
79    def test_runInTerminalInvalidTarget(self):
80        self.build_and_create_debug_adaptor()
81        response = self.launch(
82            "INVALIDPROGRAM", stopOnEntry=True, runInTerminal=True, args=["foobar"], env=["FOO=bar"], expectFailure=True)
83        self.assertFalse(response['success'])
84        self.assertIn("Could not create a target for a program 'INVALIDPROGRAM': unable to find executable",
85            response['message'])
86
87    @skipIfWindows
88    @skipIfRemote
89    @skipIf(archs=no_match(['x86_64']))
90    def test_missingArgInRunInTerminalLauncher(self):
91        proc = subprocess.run([self.lldbVSCodeExec,  "--launch-target", "INVALIDPROGRAM"],
92            capture_output=True, universal_newlines=True)
93        self.assertTrue(proc.returncode != 0)
94        self.assertIn('"--launch-target" requires "--comm-file" to be specified', proc.stderr)
95
96    @skipIfWindows
97    @skipIfRemote
98    @skipIf(archs=no_match(['x86_64']))
99    def test_FakeAttachedRunInTerminalLauncherWithInvalidProgram(self):
100        comm_file = os.path.join(self.getBuildDir(), "comm-file")
101        os.mkfifo(comm_file)
102
103        proc = subprocess.Popen(
104            [self.lldbVSCodeExec, "--comm-file", comm_file, "--launch-target", "INVALIDPROGRAM"],
105            universal_newlines=True, stderr=subprocess.PIPE)
106
107        self.readPidMessage(comm_file)
108        self.sendDidAttachMessage(comm_file)
109        self.assertIn("No such file or directory", self.readErrorMessage(comm_file))
110
111        _, stderr = proc.communicate()
112        self.assertIn("No such file or directory", stderr)
113
114    @skipIfWindows
115    @skipIfRemote
116    @skipIf(archs=no_match(['x86_64']))
117    def test_FakeAttachedRunInTerminalLauncherWithValidProgram(self):
118        comm_file = os.path.join(self.getBuildDir(), "comm-file")
119        os.mkfifo(comm_file)
120
121        proc = subprocess.Popen(
122            [self.lldbVSCodeExec, "--comm-file", comm_file, "--launch-target", "echo", "foo"],
123            universal_newlines=True, stdout=subprocess.PIPE)
124
125        self.readPidMessage(comm_file)
126        self.sendDidAttachMessage(comm_file)
127
128        stdout, _ = proc.communicate()
129        self.assertIn("foo", stdout)
130
131    @skipIfWindows
132    @skipIfRemote
133    @skipIf(archs=no_match(['x86_64']))
134    def test_FakeAttachedRunInTerminalLauncherAndCheckEnvironment(self):
135        comm_file = os.path.join(self.getBuildDir(), "comm-file")
136        os.mkfifo(comm_file)
137
138        proc = subprocess.Popen(
139            [self.lldbVSCodeExec, "--comm-file", comm_file, "--launch-target", "env"],
140            universal_newlines=True, stdout=subprocess.PIPE,
141            env={**os.environ, "FOO": "BAR"})
142
143        self.readPidMessage(comm_file)
144        self.sendDidAttachMessage(comm_file)
145
146        stdout, _ = proc.communicate()
147        self.assertIn("FOO=BAR", stdout)
148
149    @skipIfWindows
150    @skipIfRemote
151    @skipIf(archs=no_match(['x86_64']))
152    def test_NonAttachedRunInTerminalLauncher(self):
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", "echo", "foo"],
158            universal_newlines=True, stderr=subprocess.PIPE,
159            env={**os.environ, "LLDB_VSCODE_RIT_TIMEOUT_IN_MS": "1000"})
160
161        self.readPidMessage(comm_file)
162
163        _, stderr = proc.communicate()
164        self.assertIn("Timed out trying to get messages from the debug adaptor", stderr)
165