1 //===-- TerminalTest.cpp --------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
21 using namespace lldb_private
;
23 class TerminalTest
: public ::testing::Test
{
25 SubsystemRAII
<FileSystem
> subsystems
;
30 void SetUp() override
{
31 ASSERT_THAT_ERROR(m_pty
.OpenFirstAvailablePrimary(O_RDWR
| O_NOCTTY
),
33 ASSERT_THAT_ERROR(m_pty
.OpenSecondary(O_RDWR
| O_NOCTTY
),
35 m_fd
= m_pty
.GetSecondaryFileDescriptor();
37 m_term
.SetFileDescriptor(m_fd
);
41 TEST_F(TerminalTest
, PtyIsATerminal
) {
42 EXPECT_EQ(m_term
.IsATerminal(), true);
45 TEST_F(TerminalTest
, PipeIsNotATerminal
) {
47 ASSERT_EQ(pipe(pipefd
), 0);
48 Terminal pipeterm
{pipefd
[0]};
49 EXPECT_EQ(pipeterm
.IsATerminal(), false);
54 TEST_F(TerminalTest
, SetEcho
) {
55 struct termios terminfo
;
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);
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);
66 TEST_F(TerminalTest
, SetCanonical
) {
67 struct termios terminfo
;
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);
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);
78 TEST_F(TerminalTest
, SetRaw
) {
79 struct termios terminfo
;
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
),
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);
94 TEST_F(TerminalTest
, SetBaudRate
) {
95 struct termios terminfo
;
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
));
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
));
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
));
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")));
121 TEST_F(TerminalTest
, SetStopBits
) {
122 struct termios terminfo
;
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);
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);
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)")));
142 TEST_F(TerminalTest
, SetParity
) {
143 struct termios terminfo
;
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);
149 #if !defined(__linux__) // Linux pty devices do not support setting parity
150 ASSERT_THAT_ERROR(m_term
.SetParity(Terminal::Parity::Even
),
152 ASSERT_EQ(tcgetattr(m_fd
, &terminfo
), 0);
153 EXPECT_NE(terminfo
.c_cflag
& PARENB
, 0U);
154 EXPECT_EQ(terminfo
.c_cflag
& PARODD
, 0U);
156 EXPECT_EQ(terminfo
.c_cflag
& CMSPAR
, 0U);
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);
164 EXPECT_EQ(terminfo
.c_cflag
& CMSPAR
, 0U);
168 ASSERT_THAT_ERROR(m_term
.SetParity(Terminal::Parity::Space
),
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);
175 ASSERT_THAT_ERROR(m_term
.SetParity(Terminal::Parity::Mark
),
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__)
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")));
196 TEST_F(TerminalTest
, SetParityCheck
) {
197 struct termios terminfo
;
199 ASSERT_THAT_ERROR(m_term
.SetParityCheck(Terminal::ParityCheck::No
),
201 ASSERT_EQ(tcgetattr(m_fd
, &terminfo
), 0);
202 EXPECT_EQ(terminfo
.c_iflag
& (IGNPAR
| PARMRK
| INPCK
), 0U);
205 m_term
.SetParityCheck(Terminal::ParityCheck::ReplaceWithNUL
),
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);
211 ASSERT_THAT_ERROR(m_term
.SetParityCheck(Terminal::ParityCheck::Ignore
),
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);
218 ASSERT_THAT_ERROR(m_term
.SetParityCheck(Terminal::ParityCheck::Mark
),
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);
226 TEST_F(TerminalTest
, SetHardwareFlowControl
) {
228 struct termios terminfo
;
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);
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);
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());
247 TEST_F(TerminalTest
, SaveRestoreRAII
) {
248 struct termios orig_terminfo
;
249 struct termios terminfo
;
250 ASSERT_EQ(tcgetattr(m_fd
, &orig_terminfo
), 0);
253 TerminalState term_state
{m_term
};
254 terminfo
= orig_terminfo
;
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
);
262 ASSERT_EQ(tcsetattr(m_fd
, TCSANOW
, &terminfo
),
266 ASSERT_EQ(tcgetattr(m_fd
, &terminfo
), 0);
267 ASSERT_EQ(memcmp(&terminfo
, &orig_terminfo
, sizeof(terminfo
)), 0);
270 TEST_F(TerminalTest
, SaveRestore
) {
271 TerminalState term_state
;
273 struct termios orig_terminfo
;
274 struct termios terminfo
;
275 ASSERT_EQ(tcgetattr(m_fd
, &orig_terminfo
), 0);
277 term_state
.Save(m_term
, false);
278 terminfo
= orig_terminfo
;
280 // make an arbitrary change
281 cfsetispeed(&terminfo
, cfgetispeed(&orig_terminfo
) == B9600
? B4800
: B9600
);
282 cfsetospeed(&terminfo
, cfgetospeed(&orig_terminfo
) == B9600
? B4800
: B9600
);
284 ASSERT_EQ(tcsetattr(m_fd
, TCSANOW
, &terminfo
), 0);
286 term_state
.Restore();
287 ASSERT_EQ(tcgetattr(m_fd
, &terminfo
), 0);
288 ASSERT_EQ(memcmp(&terminfo
, &orig_terminfo
, sizeof(terminfo
)), 0);