Index: lldb/unittests/Host/CMakeLists.txt =================================================================== --- lldb/unittests/Host/CMakeLists.txt +++ lldb/unittests/Host/CMakeLists.txt @@ -14,6 +14,7 @@ SocketTestUtilities.cpp XMLTest.cpp ) +set (EXTRA_LIBS) if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android") list(APPEND FILES @@ -22,10 +23,25 @@ ) endif() +# TODO: support more systems +if (LLDB_ENABLE_TERMIOS AND + CMAKE_SYSTEM_NAME MATCHES "Darwin|FreeBSD|Linux|NetBSD|OpenBSD") + list(APPEND FILES + TerminalTest.cpp + ) + + if (NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") + list(APPEND EXTRA_LIBS + util + ) + endif() +endif() + add_lldb_unittest(HostTests ${FILES} LINK_LIBS lldbHost lldbUtilityHelpers LLVMTestingSupport + ${EXTRA_LIBS} ) Index: lldb/unittests/Host/TerminalTest.cpp =================================================================== --- /dev/null +++ lldb/unittests/Host/TerminalTest.cpp @@ -0,0 +1,135 @@ +//===-- 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/Terminal.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#if defined(__linux__) +#include +#elif defined(__FreeBSD__) +#include +#else // Darwin, NetBSD, OpenBSD +#include +#endif +#include +#include + +using namespace lldb_private; + +class TerminalTest : public ::testing::Test { +protected: + int m_master; + int m_slave; + + void SetUp() override { + ASSERT_EQ(openpty(&m_master, &m_slave, nullptr, nullptr, nullptr), 0); + } + + void TearDown() override { + close(m_slave); + close(m_master); + } +}; + +TEST_F(TerminalTest, PtyIsATerminal) { + Terminal term{m_slave}; + 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_slave}; + + ASSERT_EQ(term.SetEcho(true), true); + ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0); + EXPECT_NE(terminfo.c_lflag & ECHO, 0U); + + ASSERT_EQ(term.SetEcho(false), true); + ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0); + EXPECT_EQ(terminfo.c_lflag & ECHO, 0U); +} + +TEST_F(TerminalTest, SetCanonical) { + struct termios terminfo; + Terminal term{m_slave}; + + ASSERT_EQ(term.SetCanonical(true), true); + ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0); + EXPECT_NE(terminfo.c_lflag & ICANON, 0U); + + ASSERT_EQ(term.SetCanonical(false), true); + ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0); + EXPECT_EQ(terminfo.c_lflag & ICANON, 0U); +} + +TEST_F(TerminalTest, SaveRestoreRAII) { + struct termios orig_terminfo; + struct termios terminfo; + ASSERT_EQ(tcgetattr(m_slave, &orig_terminfo), 0); + + Terminal term{m_slave}; + + { + 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_slave, TCSANOW, &terminfo), 0); + } + + ASSERT_EQ(tcgetattr(m_slave, &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_slave, &orig_terminfo), 0); + + Terminal term{m_slave}; + 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_slave, TCSANOW, &terminfo), 0); + + term_state.Restore(); + ASSERT_EQ(tcgetattr(m_slave, &terminfo), 0); + ASSERT_EQ(memcmp(&terminfo, &orig_terminfo, sizeof(terminfo)), 0); +}