1 //===-- EditlineTest.cpp ----------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #ifndef LLDB_DISABLE_LIBEDIT
12 #define EDITLINE_TEST_DUMP_OUTPUT 0
20 #include "gtest/gtest.h"
22 #include "lldb/Core/Error.h"
23 #include "lldb/Core/StringList.h"
24 #include "lldb/Host/Editline.h"
25 #include "lldb/Host/Pipe.h"
26 #include "lldb/Utility/PseudoTerminal.h"
30 const size_t TIMEOUT_MILLIS = 5000;
37 FilePointer () = delete;
39 FilePointer (const FilePointer&) = delete;
41 FilePointer (FILE *file_p)
48 if (_file_p != nullptr)
50 const int close_result = fclose (_file_p);
51 EXPECT_EQ(0, close_result);
67 Wraps an Editline class, providing a simple way to feed
68 input (as if from the keyboard) and receive output from Editline.
82 return _editline_sp.get () != nullptr;
85 lldb_private::Editline&
92 SendLine (const std::string &line);
95 SendLines (const std::vector<std::string> &lines);
98 GetLine (std::string &line, bool &interrupted, size_t timeout_millis);
101 GetLines (lldb_private::StringList &lines, bool &interrupted, size_t timeout_millis);
110 lldb_private::Editline * editline,
111 lldb_private::StringList & lines,
114 std::unique_ptr<lldb_private::Editline> _editline_sp;
116 lldb_utility::PseudoTerminal _pty;
120 std::unique_ptr<FilePointer> _el_slave_file;
123 EditlineAdapter::EditlineAdapter () :
130 lldb_private::Error error;
132 // Open the first master pty available.
133 char error_string[256];
134 error_string[0] = '\0';
135 if (!_pty.OpenFirstAvailableMaster (O_RDWR, error_string, sizeof (error_string)))
137 fprintf(stderr, "failed to open first available master pty: '%s'\n", error_string);
141 // Grab the master fd. This is a file descriptor we will:
142 // (1) write to when we want to send input to editline.
143 // (2) read from when we want to see what editline sends back.
144 _pty_master_fd = _pty.GetMasterFileDescriptor();
146 // Open the corresponding slave pty.
147 if (!_pty.OpenSlave (O_RDWR, error_string, sizeof (error_string)))
149 fprintf(stderr, "failed to open slave pty: '%s'\n", error_string);
152 _pty_slave_fd = _pty.GetSlaveFileDescriptor();
154 _el_slave_file.reset (new FilePointer (fdopen (_pty_slave_fd, "rw")));
155 EXPECT_FALSE (nullptr == *_el_slave_file);
156 if (*_el_slave_file == nullptr)
159 // Create an Editline instance.
160 _editline_sp.reset (new lldb_private::Editline("gtest editor", *_el_slave_file, *_el_slave_file, *_el_slave_file, false));
161 _editline_sp->SetPrompt ("> ");
163 // Hookup our input complete callback.
164 _editline_sp->SetIsInputCompleteCallback(IsInputComplete, this);
168 EditlineAdapter::CloseInput ()
170 if (_el_slave_file != nullptr)
171 _el_slave_file.reset (nullptr);
175 EditlineAdapter::SendLine (const std::string &line)
177 // Ensure we're valid before proceeding.
181 // Write the line out to the pipe connected to editline's input.
182 ssize_t input_bytes_written =
183 ::write (_pty_master_fd,
185 line.length() * sizeof (std::string::value_type));
187 const char *eoln = "\n";
188 const size_t eoln_length = strlen(eoln);
189 input_bytes_written =
190 ::write (_pty_master_fd,
192 eoln_length * sizeof (char));
194 EXPECT_NE(-1, input_bytes_written) << strerror(errno);
195 EXPECT_EQ (eoln_length * sizeof (char), size_t(input_bytes_written));
196 return eoln_length * sizeof (char) == size_t(input_bytes_written);
200 EditlineAdapter::SendLines (const std::vector<std::string> &lines)
202 for (auto &line : lines)
204 #if EDITLINE_TEST_DUMP_OUTPUT
205 printf ("<stdin> sending line \"%s\"\n", line.c_str());
207 if (!SendLine (line))
213 // We ignore the timeout for now.
215 EditlineAdapter::GetLine (std::string &line, bool &interrupted, size_t /* timeout_millis */)
217 // Ensure we're valid before proceeding.
221 _editline_sp->GetLine (line, interrupted);
226 EditlineAdapter::GetLines (lldb_private::StringList &lines, bool &interrupted, size_t /* timeout_millis */)
228 // Ensure we're valid before proceeding.
232 _editline_sp->GetLines (1, lines, interrupted);
237 EditlineAdapter::IsInputComplete (
238 lldb_private::Editline * editline,
239 lldb_private::StringList & lines,
242 // We'll call ourselves complete if we've received a balanced set of braces.
243 int start_block_count = 0;
244 int brace_balance = 0;
246 for (size_t i = 0; i < lines.GetSize (); ++i)
248 for (auto ch : lines[i])
260 return (start_block_count > 0) && (brace_balance == 0);
264 EditlineAdapter::ConsumeAllOutput ()
266 FilePointer output_file (fdopen (_pty_master_fd, "r"));
269 while ((ch = fgetc(output_file)) != EOF)
271 #if EDITLINE_TEST_DUMP_OUTPUT
272 char display_str[] = { 0, 0, 0 };
276 display_str[0] = '\\';
277 display_str[1] = 't';
280 display_str[0] = '\\';
281 display_str[1] = 'n';
284 display_str[0] = '\\';
285 display_str[1] = 'r';
291 printf ("<stdout> 0x%02x (%03d) (%s)\n", ch, ch, display_str);
297 class EditlineTestFixture : public ::testing::Test
300 EditlineAdapter _el_adapter;
301 std::shared_ptr<std::thread> _sp_output_thread;
306 // We need a TERM set properly for editline to work as expected.
307 setenv("TERM", "vt100", 1);
309 // Validate the editline adapter.
310 EXPECT_TRUE(_el_adapter.IsValid());
311 if (!_el_adapter.IsValid())
315 _sp_output_thread.reset(new std::thread([&] { _el_adapter.ConsumeAllOutput(); }));
320 _el_adapter.CloseInput();
321 if (_sp_output_thread)
322 _sp_output_thread->join();
325 EditlineAdapter &GetEditlineAdapter() { return _el_adapter; }
328 TEST_F(EditlineTestFixture, EditlineReceivesSingleLineText)
330 // Send it some text via our virtual keyboard.
331 const std::string input_text ("Hello, world");
332 EXPECT_TRUE(GetEditlineAdapter().SendLine(input_text));
334 // Verify editline sees what we put in.
335 std::string el_reported_line;
336 bool input_interrupted = false;
337 const bool received_line = GetEditlineAdapter().GetLine(el_reported_line, input_interrupted, TIMEOUT_MILLIS);
339 EXPECT_TRUE (received_line);
340 EXPECT_FALSE (input_interrupted);
341 EXPECT_EQ (input_text, el_reported_line);
344 TEST_F(EditlineTestFixture, EditlineReceivesMultiLineText)
346 // Send it some text via our virtual keyboard.
347 std::vector<std::string> input_lines;
348 input_lines.push_back ("int foo()");
349 input_lines.push_back ("{");
350 input_lines.push_back ("printf(\"Hello, world\");");
351 input_lines.push_back ("}");
352 input_lines.push_back ("");
354 EXPECT_TRUE(GetEditlineAdapter().SendLines(input_lines));
356 // Verify editline sees what we put in.
357 lldb_private::StringList el_reported_lines;
358 bool input_interrupted = false;
360 EXPECT_TRUE(GetEditlineAdapter().GetLines(el_reported_lines, input_interrupted, TIMEOUT_MILLIS));
361 EXPECT_FALSE (input_interrupted);
363 // Without any auto indentation support, our output should directly match our input.
364 EXPECT_EQ (input_lines.size (), el_reported_lines.GetSize ());
365 if (input_lines.size () == el_reported_lines.GetSize ())
367 for (size_t i = 0; i < input_lines.size(); ++i)
368 EXPECT_EQ (input_lines[i], el_reported_lines[i]);