[PowerPC] Collect some CallLowering arguments into a struct. [NFC]
[llvm-project.git] / lldb / unittests / Editline / EditlineTest.cpp
blob41f8e471256a485be1fbd0d8511d8c961563cd74
1 //===-- EditlineTest.cpp ----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Host/Config.h"
11 #if LLDB_ENABLE_LIBEDIT
13 #define EDITLINE_TEST_DUMP_OUTPUT 0
15 #include <stdio.h>
16 #include <unistd.h>
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20 #include <memory>
21 #include <thread>
23 #include "TestingSupport/SubsystemRAII.h"
24 #include "lldb/Host/Editline.h"
25 #include "lldb/Host/FileSystem.h"
26 #include "lldb/Host/Pipe.h"
27 #include "lldb/Host/PseudoTerminal.h"
28 #include "lldb/Utility/Status.h"
29 #include "lldb/Utility/StringList.h"
31 using namespace lldb_private;
33 namespace {
34 const size_t TIMEOUT_MILLIS = 5000;
37 class FilePointer {
38 public:
39 FilePointer() = delete;
41 FilePointer(const FilePointer &) = delete;
43 FilePointer(FILE *file_p) : _file_p(file_p) {}
45 ~FilePointer() {
46 if (_file_p != nullptr) {
47 const int close_result = fclose(_file_p);
48 EXPECT_EQ(0, close_result);
52 operator FILE *() { return _file_p; }
54 private:
55 FILE *_file_p;
58 /**
59 Wraps an Editline class, providing a simple way to feed
60 input (as if from the keyboard) and receive output from Editline.
62 class EditlineAdapter {
63 public:
64 EditlineAdapter();
66 void CloseInput();
68 bool IsValid() const { return _editline_sp != nullptr; }
70 lldb_private::Editline &GetEditline() { return *_editline_sp; }
72 bool SendLine(const std::string &line);
74 bool SendLines(const std::vector<std::string> &lines);
76 bool GetLine(std::string &line, bool &interrupted, size_t timeout_millis);
78 bool GetLines(lldb_private::StringList &lines, bool &interrupted,
79 size_t timeout_millis);
81 void ConsumeAllOutput();
83 private:
84 static bool IsInputComplete(lldb_private::Editline *editline,
85 lldb_private::StringList &lines, void *baton);
87 std::unique_ptr<lldb_private::Editline> _editline_sp;
89 PseudoTerminal _pty;
90 int _pty_master_fd;
91 int _pty_slave_fd;
93 std::unique_ptr<FilePointer> _el_slave_file;
96 EditlineAdapter::EditlineAdapter()
97 : _editline_sp(), _pty(), _pty_master_fd(-1), _pty_slave_fd(-1),
98 _el_slave_file() {
99 lldb_private::Status error;
101 // Open the first master pty available.
102 char error_string[256];
103 error_string[0] = '\0';
104 if (!_pty.OpenFirstAvailableMaster(O_RDWR, error_string,
105 sizeof(error_string))) {
106 fprintf(stderr, "failed to open first available master pty: '%s'\n",
107 error_string);
108 return;
111 // Grab the master fd. This is a file descriptor we will:
112 // (1) write to when we want to send input to editline.
113 // (2) read from when we want to see what editline sends back.
114 _pty_master_fd = _pty.GetMasterFileDescriptor();
116 // Open the corresponding slave pty.
117 if (!_pty.OpenSlave(O_RDWR, error_string, sizeof(error_string))) {
118 fprintf(stderr, "failed to open slave pty: '%s'\n", error_string);
119 return;
121 _pty_slave_fd = _pty.GetSlaveFileDescriptor();
123 _el_slave_file.reset(new FilePointer(fdopen(_pty_slave_fd, "rw")));
124 EXPECT_FALSE(nullptr == *_el_slave_file);
125 if (*_el_slave_file == nullptr)
126 return;
128 // Create an Editline instance.
129 _editline_sp.reset(new lldb_private::Editline("gtest editor", *_el_slave_file,
130 *_el_slave_file,
131 *_el_slave_file, false));
132 _editline_sp->SetPrompt("> ");
134 // Hookup our input complete callback.
135 _editline_sp->SetIsInputCompleteCallback(IsInputComplete, this);
138 void EditlineAdapter::CloseInput() {
139 if (_el_slave_file != nullptr)
140 _el_slave_file.reset(nullptr);
143 bool EditlineAdapter::SendLine(const std::string &line) {
144 // Ensure we're valid before proceeding.
145 if (!IsValid())
146 return false;
148 // Write the line out to the pipe connected to editline's input.
149 ssize_t input_bytes_written =
150 ::write(_pty_master_fd, line.c_str(),
151 line.length() * sizeof(std::string::value_type));
153 const char *eoln = "\n";
154 const size_t eoln_length = strlen(eoln);
155 input_bytes_written =
156 ::write(_pty_master_fd, eoln, eoln_length * sizeof(char));
158 EXPECT_NE(-1, input_bytes_written) << strerror(errno);
159 EXPECT_EQ(eoln_length * sizeof(char), size_t(input_bytes_written));
160 return eoln_length * sizeof(char) == size_t(input_bytes_written);
163 bool EditlineAdapter::SendLines(const std::vector<std::string> &lines) {
164 for (auto &line : lines) {
165 #if EDITLINE_TEST_DUMP_OUTPUT
166 printf("<stdin> sending line \"%s\"\n", line.c_str());
167 #endif
168 if (!SendLine(line))
169 return false;
171 return true;
174 // We ignore the timeout for now.
175 bool EditlineAdapter::GetLine(std::string &line, bool &interrupted,
176 size_t /* timeout_millis */) {
177 // Ensure we're valid before proceeding.
178 if (!IsValid())
179 return false;
181 _editline_sp->GetLine(line, interrupted);
182 return true;
185 bool EditlineAdapter::GetLines(lldb_private::StringList &lines,
186 bool &interrupted, size_t /* timeout_millis */) {
187 // Ensure we're valid before proceeding.
188 if (!IsValid())
189 return false;
191 _editline_sp->GetLines(1, lines, interrupted);
192 return true;
195 bool EditlineAdapter::IsInputComplete(lldb_private::Editline *editline,
196 lldb_private::StringList &lines,
197 void *baton) {
198 // We'll call ourselves complete if we've received a balanced set of braces.
199 int start_block_count = 0;
200 int brace_balance = 0;
202 for (const std::string &line : lines) {
203 for (auto ch : line) {
204 if (ch == '{') {
205 ++start_block_count;
206 ++brace_balance;
207 } else if (ch == '}')
208 --brace_balance;
212 return (start_block_count > 0) && (brace_balance == 0);
215 void EditlineAdapter::ConsumeAllOutput() {
216 FilePointer output_file(fdopen(_pty_master_fd, "r"));
218 int ch;
219 while ((ch = fgetc(output_file)) != EOF) {
220 #if EDITLINE_TEST_DUMP_OUTPUT
221 char display_str[] = {0, 0, 0};
222 switch (ch) {
223 case '\t':
224 display_str[0] = '\\';
225 display_str[1] = 't';
226 break;
227 case '\n':
228 display_str[0] = '\\';
229 display_str[1] = 'n';
230 break;
231 case '\r':
232 display_str[0] = '\\';
233 display_str[1] = 'r';
234 break;
235 default:
236 display_str[0] = ch;
237 break;
239 printf("<stdout> 0x%02x (%03d) (%s)\n", ch, ch, display_str);
240 // putc(ch, stdout);
241 #endif
245 class EditlineTestFixture : public ::testing::Test {
246 SubsystemRAII<FileSystem> subsystems;
247 EditlineAdapter _el_adapter;
248 std::shared_ptr<std::thread> _sp_output_thread;
250 public:
251 static void SetUpTestCase() {
252 // We need a TERM set properly for editline to work as expected.
253 setenv("TERM", "vt100", 1);
256 void SetUp() override {
257 // Validate the editline adapter.
258 EXPECT_TRUE(_el_adapter.IsValid());
259 if (!_el_adapter.IsValid())
260 return;
262 // Dump output.
263 _sp_output_thread =
264 std::make_shared<std::thread>([&] { _el_adapter.ConsumeAllOutput(); });
267 void TearDown() override {
268 _el_adapter.CloseInput();
269 if (_sp_output_thread)
270 _sp_output_thread->join();
273 EditlineAdapter &GetEditlineAdapter() { return _el_adapter; }
276 TEST_F(EditlineTestFixture, EditlineReceivesSingleLineText) {
277 // Send it some text via our virtual keyboard.
278 const std::string input_text("Hello, world");
279 EXPECT_TRUE(GetEditlineAdapter().SendLine(input_text));
281 // Verify editline sees what we put in.
282 std::string el_reported_line;
283 bool input_interrupted = false;
284 const bool received_line = GetEditlineAdapter().GetLine(
285 el_reported_line, input_interrupted, TIMEOUT_MILLIS);
287 EXPECT_TRUE(received_line);
288 EXPECT_FALSE(input_interrupted);
289 EXPECT_EQ(input_text, el_reported_line);
292 TEST_F(EditlineTestFixture, EditlineReceivesMultiLineText) {
293 // Send it some text via our virtual keyboard.
294 std::vector<std::string> input_lines;
295 input_lines.push_back("int foo()");
296 input_lines.push_back("{");
297 input_lines.push_back("printf(\"Hello, world\");");
298 input_lines.push_back("}");
299 input_lines.push_back("");
301 EXPECT_TRUE(GetEditlineAdapter().SendLines(input_lines));
303 // Verify editline sees what we put in.
304 lldb_private::StringList el_reported_lines;
305 bool input_interrupted = false;
307 EXPECT_TRUE(GetEditlineAdapter().GetLines(el_reported_lines,
308 input_interrupted, TIMEOUT_MILLIS));
309 EXPECT_FALSE(input_interrupted);
311 // Without any auto indentation support, our output should directly match our
312 // input.
313 std::vector<std::string> reported_lines;
314 for (const std::string &line : el_reported_lines)
315 reported_lines.push_back(line);
317 EXPECT_THAT(reported_lines, testing::ContainerEq(input_lines));
320 #endif