290 lines · cpp
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-exception6//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 21using namespace lldb_private;22 23class TerminalTest : public ::testing::Test {24protected:25 SubsystemRAII<FileSystem> subsystems;26 PseudoTerminal m_pty;27 int m_fd;28 Terminal m_term;29 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 41TEST_F(TerminalTest, PtyIsATerminal) {42 EXPECT_EQ(m_term.IsATerminal(), true);43}44 45TEST_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 54TEST_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 66TEST_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 78TEST_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 it84 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 94TEST_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 value108#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#else114 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#endif119}120 121TEST_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 142TEST_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 parity150 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#endif158 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#endif166 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#endif194}195 196TEST_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 226TEST_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#else238 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#endif245}246 247TEST_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 change257 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 270TEST_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 change281 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