blob: 23b1c8f3a23bc75f3bb82692636287b9747ae1d1 [file] [edit]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Unit tests for tcflush.
///
//===----------------------------------------------------------------------===//
#include "hdr/fcntl_macros.h"
#include "hdr/poll_macros.h"
#include "hdr/sys_ioctl_macros.h"
#include "hdr/termios_macros.h"
#include "hdr/types/struct_pollfd.h"
#include "src/__support/libc_errno.h"
#include "src/fcntl/open.h"
#include "src/poll/poll.h"
#include "src/stdio/snprintf.h"
#include "src/sys/ioctl/ioctl.h"
#include "src/termios/tcflush.h"
#include "src/unistd/close.h"
#include "src/unistd/write.h"
#include "test/UnitTest/ErrnoCheckingTest.h"
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/Test.h"
using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
namespace {
bool has_pending_input(int fd) {
int pending = 0;
LIBC_NAMESPACE::ioctl(fd, FIONREAD, &pending);
return pending > 0;
}
} // namespace
// Each test gets a pseudoterminal with a line of input waiting on the
// subsidiary side. The descriptors are closed in TearDown, so no path out of
// a test or of the setup leaks them. When there is no pseudoterminal to use,
// have_pty stays false and the tests return without checking anything.
class LlvmLibcTcflushTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
public:
int manager = -1;
int subsidiary = -1;
bool have_pty = false;
void SetUp() override {
LIBC_NAMESPACE::testing::ErrnoCheckingTest::SetUp();
have_pty = open_pseudoterminal_with_input();
if (have_pty)
ASSERT_ERRNO_SUCCESS();
else
LIBC_NAMESPACE::libc_errno = 0;
}
void TearDown() override {
if (subsidiary >= 0)
LIBC_NAMESPACE::close(subsidiary);
if (manager >= 0)
LIBC_NAMESPACE::close(manager);
LIBC_NAMESPACE::testing::ErrnoCheckingTest::TearDown();
}
private:
bool open_pseudoterminal_with_input() {
manager = LIBC_NAMESPACE::open("/dev/ptmx", O_RDWR | O_NOCTTY);
if (manager < 0)
return false;
int unlock = 0;
unsigned int number = 0;
if (LIBC_NAMESPACE::ioctl(manager, TIOCSPTLCK, &unlock) != 0 ||
LIBC_NAMESPACE::ioctl(manager, TIOCGPTN, &number) != 0)
return false;
char path[32];
LIBC_NAMESPACE::snprintf(path, sizeof(path), "/dev/pts/%u", number);
subsidiary = LIBC_NAMESPACE::open(path, O_RDWR | O_NOCTTY);
if (subsidiary < 0)
return false;
if (LIBC_NAMESPACE::write(manager, "input\n", 6) != 6)
return false;
// The terminal hands input to the subsidiary side in the background, and a
// line is readable once the newline has been through.
struct pollfd pfd = {subsidiary, POLLIN, 0};
return LIBC_NAMESPACE::poll(&pfd, 1, 5000) == 1;
}
};
TEST_F(LlvmLibcTcflushTest, OutputFlushKeepsInput) {
if (!have_pty)
return;
ASSERT_TRUE(has_pending_input(subsidiary));
ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCOFLUSH), Succeeds(0));
ASSERT_TRUE(has_pending_input(subsidiary));
}
TEST_F(LlvmLibcTcflushTest, InputFlushDropsInput) {
if (!have_pty)
return;
ASSERT_TRUE(has_pending_input(subsidiary));
ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCIFLUSH), Succeeds(0));
ASSERT_FALSE(has_pending_input(subsidiary));
}
TEST_F(LlvmLibcTcflushTest, BothFlushDropsInput) {
if (!have_pty)
return;
ASSERT_TRUE(has_pending_input(subsidiary));
ASSERT_THAT(LIBC_NAMESPACE::tcflush(subsidiary, TCIOFLUSH), Succeeds(0));
ASSERT_FALSE(has_pending_input(subsidiary));
}