xref: /llvm-project/lldb/unittests/Host/posix/TerminalTest.cpp (revision 75e93ec720739291640005681f062b0e1e8e0f53)
1 //===-- TerminalTest.cpp --------------------------------------------------===//
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 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Host/FileSystem.h"
10 #include "lldb/Host/PseudoTerminal.h"
11 #include "lldb/Host/Terminal.h"
12 #include "llvm/Testing/Support/Error.h"
13 #include "TestingSupport/SubsystemRAII.h"
14 
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 
18 #include <termios.h>
19 #include <unistd.h>
20 
21 using namespace lldb_private;
22 
23 class TerminalTest : public ::testing::Test {
24 protected:
25   SubsystemRAII<FileSystem> subsystems;
26   PseudoTerminal m_pty;
27   int m_fd;
28   Terminal m_term;
29 
SetUp()30   void SetUp() override {
31     ASSERT_THAT_ERROR(m_pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY),
32                       llvm::Succeeded());
33     ASSERT_THAT_ERROR(m_pty.OpenSecondary(O_RDWR | O_NOCTTY),
34                       llvm::Succeeded());
35     m_fd = m_pty.GetSecondaryFileDescriptor();
36     ASSERT_NE(m_fd, -1);
37     m_term.SetFileDescriptor(m_fd);
38   }
39 };
40 
TEST_F(TerminalTest,PtyIsATerminal)41 TEST_F(TerminalTest, PtyIsATerminal) {
42   EXPECT_EQ(m_term.IsATerminal(), true);
43 }
44 
TEST_F(TerminalTest,PipeIsNotATerminal)45 TEST_F(TerminalTest, PipeIsNotATerminal) {
46   int pipefd[2];
47   ASSERT_EQ(pipe(pipefd), 0);
48   Terminal pipeterm{pipefd[0]};
49   EXPECT_EQ(pipeterm.IsATerminal(), false);
50   close(pipefd[0]);
51   close(pipefd[1]);
52 }
53 
TEST_F(TerminalTest,SetEcho)54 TEST_F(TerminalTest, SetEcho) {
55   struct termios terminfo;
56 
57   ASSERT_THAT_ERROR(m_term.SetEcho(true), llvm::Succeeded());
58   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
59   EXPECT_NE(terminfo.c_lflag & ECHO, 0U);
60 
61   ASSERT_THAT_ERROR(m_term.SetEcho(false), llvm::Succeeded());
62   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
63   EXPECT_EQ(terminfo.c_lflag & ECHO, 0U);
64 }
65 
TEST_F(TerminalTest,SetCanonical)66 TEST_F(TerminalTest, SetCanonical) {
67   struct termios terminfo;
68 
69   ASSERT_THAT_ERROR(m_term.SetCanonical(true), llvm::Succeeded());
70   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
71   EXPECT_NE(terminfo.c_lflag & ICANON, 0U);
72 
73   ASSERT_THAT_ERROR(m_term.SetCanonical(false), llvm::Succeeded());
74   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
75   EXPECT_EQ(terminfo.c_lflag & ICANON, 0U);
76 }
77 
TEST_F(TerminalTest,SetRaw)78 TEST_F(TerminalTest, SetRaw) {
79   struct termios terminfo;
80 
81   ASSERT_THAT_ERROR(m_term.SetRaw(), llvm::Succeeded());
82   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
83   // NB: cfmakeraw() on glibc disables IGNBRK, on FreeBSD sets it
84   EXPECT_EQ(terminfo.c_iflag &
85                 (BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON),
86             0U);
87   EXPECT_EQ(terminfo.c_oflag & OPOST, 0U);
88   EXPECT_EQ(terminfo.c_lflag & (ICANON | ECHO | ISIG | IEXTEN), 0U);
89   EXPECT_EQ(terminfo.c_cflag & (CSIZE | PARENB), 0U | CS8);
90   EXPECT_EQ(terminfo.c_cc[VMIN], 1);
91   EXPECT_EQ(terminfo.c_cc[VTIME], 0);
92 }
93 
TEST_F(TerminalTest,SetBaudRate)94 TEST_F(TerminalTest, SetBaudRate) {
95   struct termios terminfo;
96 
97   ASSERT_THAT_ERROR(m_term.SetBaudRate(38400), llvm::Succeeded());
98   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
99   EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B38400));
100   EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B38400));
101 
102   ASSERT_THAT_ERROR(m_term.SetBaudRate(115200), llvm::Succeeded());
103   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
104   EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B115200));
105   EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B115200));
106 
107   // uncommon value
108 #if defined(B153600)
109   ASSERT_THAT_ERROR(m_term.SetBaudRate(153600), llvm::Succeeded());
110   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
111   EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B153600));
112   EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B153600));
113 #else
114   ASSERT_THAT_ERROR(m_term.SetBaudRate(153600),
115                     llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
116                         &llvm::ErrorInfoBase::message,
117                         "baud rate 153600 unsupported by the platform")));
118 #endif
119 }
120 
TEST_F(TerminalTest,SetStopBits)121 TEST_F(TerminalTest, SetStopBits) {
122   struct termios terminfo;
123 
124   ASSERT_THAT_ERROR(m_term.SetStopBits(1), llvm::Succeeded());
125   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
126   EXPECT_EQ(terminfo.c_cflag & CSTOPB, 0U);
127 
128   ASSERT_THAT_ERROR(m_term.SetStopBits(2), llvm::Succeeded());
129   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
130   EXPECT_NE(terminfo.c_cflag & CSTOPB, 0U);
131 
132   ASSERT_THAT_ERROR(m_term.SetStopBits(0),
133                     llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
134                         &llvm::ErrorInfoBase::message,
135                         "invalid stop bit count: 0 (must be 1 or 2)")));
136   ASSERT_THAT_ERROR(m_term.SetStopBits(3),
137                     llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
138                         &llvm::ErrorInfoBase::message,
139                         "invalid stop bit count: 3 (must be 1 or 2)")));
140 }
141 
TEST_F(TerminalTest,SetParity)142 TEST_F(TerminalTest, SetParity) {
143   struct termios terminfo;
144 
145   ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::No), llvm::Succeeded());
146   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
147   EXPECT_EQ(terminfo.c_cflag & PARENB, 0U);
148 
149 #if !defined(__linux__) // Linux pty devices do not support setting parity
150   ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Even),
151                     llvm::Succeeded());
152   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
153   EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
154   EXPECT_EQ(terminfo.c_cflag & PARODD, 0U);
155 #if defined(CMSPAR)
156   EXPECT_EQ(terminfo.c_cflag & CMSPAR, 0U);
157 #endif
158 
159   ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Odd), llvm::Succeeded());
160   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
161   EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
162   EXPECT_NE(terminfo.c_cflag & PARODD, 0U);
163 #if defined(CMSPAR)
164   EXPECT_EQ(terminfo.c_cflag & CMSPAR, 0U);
165 #endif
166 
167 #if defined(CMSPAR)
168   ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Space),
169                     llvm::Succeeded());
170   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
171   EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
172   EXPECT_EQ(terminfo.c_cflag & PARODD, 0U);
173   EXPECT_NE(terminfo.c_cflag & CMSPAR, 0U);
174 
175   ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Mark),
176                     llvm::Succeeded());
177   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
178   EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
179   EXPECT_NE(terminfo.c_cflag & PARODD, 0U);
180   EXPECT_NE(terminfo.c_cflag & CMSPAR, 0U);
181 #endif // defined(CMSPAR)
182 #endif // !defined(__linux__)
183 
184 #if !defined(CMSPAR)
185   ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Space),
186                     llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
187                         &llvm::ErrorInfoBase::message,
188                         "space/mark parity is not supported by the platform")));
189   ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Mark),
190                     llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
191                         &llvm::ErrorInfoBase::message,
192                         "space/mark parity is not supported by the platform")));
193 #endif
194 }
195 
TEST_F(TerminalTest,SetParityCheck)196 TEST_F(TerminalTest, SetParityCheck) {
197   struct termios terminfo;
198 
199   ASSERT_THAT_ERROR(m_term.SetParityCheck(Terminal::ParityCheck::No),
200                     llvm::Succeeded());
201   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
202   EXPECT_EQ(terminfo.c_iflag & (IGNPAR | PARMRK | INPCK), 0U);
203 
204   ASSERT_THAT_ERROR(
205       m_term.SetParityCheck(Terminal::ParityCheck::ReplaceWithNUL),
206       llvm::Succeeded());
207   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
208   EXPECT_NE(terminfo.c_iflag & INPCK, 0U);
209   EXPECT_EQ(terminfo.c_iflag & (IGNPAR | PARMRK), 0U);
210 
211   ASSERT_THAT_ERROR(m_term.SetParityCheck(Terminal::ParityCheck::Ignore),
212                     llvm::Succeeded());
213   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
214   EXPECT_NE(terminfo.c_iflag & IGNPAR, 0U);
215   EXPECT_EQ(terminfo.c_iflag & PARMRK, 0U);
216   EXPECT_NE(terminfo.c_iflag & INPCK, 0U);
217 
218   ASSERT_THAT_ERROR(m_term.SetParityCheck(Terminal::ParityCheck::Mark),
219                     llvm::Succeeded());
220   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
221   EXPECT_EQ(terminfo.c_iflag & IGNPAR, 0U);
222   EXPECT_NE(terminfo.c_iflag & PARMRK, 0U);
223   EXPECT_NE(terminfo.c_iflag & INPCK, 0U);
224 }
225 
TEST_F(TerminalTest,SetHardwareFlowControl)226 TEST_F(TerminalTest, SetHardwareFlowControl) {
227 #if defined(CRTSCTS)
228   struct termios terminfo;
229 
230   ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(true), llvm::Succeeded());
231   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
232   EXPECT_NE(terminfo.c_cflag & CRTSCTS, 0U);
233 
234   ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(false), llvm::Succeeded());
235   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
236   EXPECT_EQ(terminfo.c_cflag & CRTSCTS, 0U);
237 #else
238   ASSERT_THAT_ERROR(
239       m_term.SetHardwareFlowControl(true),
240       llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
241           &llvm::ErrorInfoBase::message,
242           "hardware flow control is not supported by the platform")));
243   ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(false), llvm::Succeeded());
244 #endif
245 }
246 
TEST_F(TerminalTest,SaveRestoreRAII)247 TEST_F(TerminalTest, SaveRestoreRAII) {
248   struct termios orig_terminfo;
249   struct termios terminfo;
250   ASSERT_EQ(tcgetattr(m_fd, &orig_terminfo), 0);
251 
252   {
253     TerminalState term_state{m_term};
254     terminfo = orig_terminfo;
255 
256     // make an arbitrary change
257     cfsetispeed(&terminfo,
258                 cfgetispeed(&orig_terminfo) == B9600 ? B4800 : B9600);
259     cfsetospeed(&terminfo,
260                 cfgetospeed(&orig_terminfo) == B9600 ? B4800 : B9600);
261 
262     ASSERT_EQ(tcsetattr(m_fd, TCSANOW, &terminfo),
263               0);
264   }
265 
266   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
267   ASSERT_EQ(memcmp(&terminfo, &orig_terminfo, sizeof(terminfo)), 0);
268 }
269 
TEST_F(TerminalTest,SaveRestore)270 TEST_F(TerminalTest, SaveRestore) {
271   TerminalState term_state;
272 
273   struct termios orig_terminfo;
274   struct termios terminfo;
275   ASSERT_EQ(tcgetattr(m_fd, &orig_terminfo), 0);
276 
277   term_state.Save(m_term, false);
278   terminfo = orig_terminfo;
279 
280   // make an arbitrary change
281   cfsetispeed(&terminfo, cfgetispeed(&orig_terminfo) == B9600 ? B4800 : B9600);
282   cfsetospeed(&terminfo, cfgetospeed(&orig_terminfo) == B9600 ? B4800 : B9600);
283 
284   ASSERT_EQ(tcsetattr(m_fd, TCSANOW, &terminfo), 0);
285 
286   term_state.Restore();
287   ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
288   ASSERT_EQ(memcmp(&terminfo, &orig_terminfo, sizeof(terminfo)), 0);
289 }
290