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