diff --git a/lldb/unittests/Host/CMakeLists.txt b/lldb/unittests/Host/CMakeLists.txt index 14c4fe4d0b8a6..ae6afd592e547 100644 --- a/lldb/unittests/Host/CMakeLists.txt +++ b/lldb/unittests/Host/CMakeLists.txt @@ -22,6 +22,12 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android") ) endif() +if (LLDB_ENABLE_TERMIOS) + list(APPEND FILES + posix/TerminalTest.cpp + ) +endif() + add_lldb_unittest(HostTests ${FILES} LINK_LIBS diff --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp new file mode 100644 index 0000000000000..ecdb548021643 --- /dev/null +++ b/lldb/unittests/Host/posix/TerminalTest.cpp @@ -0,0 +1,126 @@ +//===-- TerminalTest.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/Terminal.h" +#include "llvm/Testing/Support/Error.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include + +using namespace lldb_private; + +class TerminalTest : public ::testing::Test { +protected: + PseudoTerminal m_pty; + + void SetUp() override { + EXPECT_THAT_ERROR(m_pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY), + llvm::Succeeded()); + } +}; + +TEST_F(TerminalTest, PtyIsATerminal) { + Terminal term{m_pty.GetPrimaryFileDescriptor()}; + EXPECT_EQ(term.IsATerminal(), true); +} + +TEST_F(TerminalTest, PipeIsNotATerminal) { + int pipefd[2]; + ASSERT_EQ(pipe(pipefd), 0); + Terminal pipeterm{pipefd[0]}; + EXPECT_EQ(pipeterm.IsATerminal(), false); + close(pipefd[0]); + close(pipefd[1]); +} + +TEST_F(TerminalTest, SetEcho) { + struct termios terminfo; + Terminal term{m_pty.GetPrimaryFileDescriptor()}; + + ASSERT_EQ(term.SetEcho(true), true); + ASSERT_EQ(tcgetattr(m_pty.GetPrimaryFileDescriptor(), &terminfo), 0); + EXPECT_NE(terminfo.c_lflag & ECHO, 0U); + + ASSERT_EQ(term.SetEcho(false), true); + ASSERT_EQ(tcgetattr(m_pty.GetPrimaryFileDescriptor(), &terminfo), 0); + EXPECT_EQ(terminfo.c_lflag & ECHO, 0U); +} + +TEST_F(TerminalTest, SetCanonical) { + struct termios terminfo; + Terminal term{m_pty.GetPrimaryFileDescriptor()}; + + ASSERT_EQ(term.SetCanonical(true), true); + ASSERT_EQ(tcgetattr(m_pty.GetPrimaryFileDescriptor(), &terminfo), 0); + EXPECT_NE(terminfo.c_lflag & ICANON, 0U); + + ASSERT_EQ(term.SetCanonical(false), true); + ASSERT_EQ(tcgetattr(m_pty.GetPrimaryFileDescriptor(), &terminfo), 0); + EXPECT_EQ(terminfo.c_lflag & ICANON, 0U); +} + +TEST_F(TerminalTest, SaveRestoreRAII) { + struct termios orig_terminfo; + struct termios terminfo; + ASSERT_EQ(tcgetattr(m_pty.GetPrimaryFileDescriptor(), &orig_terminfo), 0); + + Terminal term{m_pty.GetPrimaryFileDescriptor()}; + + { + TerminalState term_state{term}; + terminfo = orig_terminfo; + + // make some arbitrary changes + terminfo.c_iflag ^= IGNPAR | INLCR; + terminfo.c_oflag ^= OPOST | OCRNL; + terminfo.c_cflag ^= PARENB | PARODD; + terminfo.c_lflag ^= ICANON | ECHO; + terminfo.c_cc[VEOF] ^= 8; + terminfo.c_cc[VEOL] ^= 4; + cfsetispeed(&terminfo, B9600); + cfsetospeed(&terminfo, B9600); + + ASSERT_EQ(tcsetattr(m_pty.GetPrimaryFileDescriptor(), TCSANOW, &terminfo), + 0); + } + + ASSERT_EQ(tcgetattr(m_pty.GetPrimaryFileDescriptor(), &terminfo), 0); + ASSERT_EQ(memcmp(&terminfo, &orig_terminfo, sizeof(terminfo)), 0); +} + +TEST_F(TerminalTest, SaveRestore) { + TerminalState term_state; + + struct termios orig_terminfo; + struct termios terminfo; + ASSERT_EQ(tcgetattr(m_pty.GetPrimaryFileDescriptor(), &orig_terminfo), 0); + + Terminal term{m_pty.GetPrimaryFileDescriptor()}; + term_state.Save(term, false); + terminfo = orig_terminfo; + + // make some arbitrary changes + terminfo.c_iflag ^= IGNPAR | INLCR; + terminfo.c_oflag ^= OPOST | OCRNL; + terminfo.c_cflag ^= PARENB | PARODD; + terminfo.c_lflag ^= ICANON | ECHO; + terminfo.c_cc[VEOF] ^= 8; + terminfo.c_cc[VEOL] ^= 4; + cfsetispeed(&terminfo, B9600); + cfsetospeed(&terminfo, B9600); + + ASSERT_EQ(tcsetattr(m_pty.GetPrimaryFileDescriptor(), TCSANOW, &terminfo), 0); + + term_state.Restore(); + ASSERT_EQ(tcgetattr(m_pty.GetPrimaryFileDescriptor(), &terminfo), 0); + ASSERT_EQ(memcmp(&terminfo, &orig_terminfo, sizeof(terminfo)), 0); +}