blob: 482258d25cd246218eecf4b46d14c07e6793b069 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/starnix/tests/syscalls/cpp/test_helper.h"
#include <errno.h>
#include <fcntl.h>
#include <lib/fit/defer.h>
#include <sys/resource.h>
#include <unistd.h>
#include <cstring>
#include <thread>
#include <gtest/gtest.h>
namespace {
TEST(TestHelperTest, DetectFailingChildren) {
test_helper::ForkHelper helper;
helper.RunInForkedProcess([] { FAIL() << "Expected failure"; });
EXPECT_FALSE(helper.WaitForChildren());
}
TEST(ScopedTestDirTest, DoesntLeakFileDescriptors) {
test_helper::ForkHelper helper;
helper.RunInForkedProcess([] {
struct rlimit limit;
ASSERT_EQ(getrlimit(RLIMIT_NOFILE, &limit), 0) << "getrlimit: " << std::strerror(errno);
auto cleanup = fit::defer([limit]() {
EXPECT_EQ(setrlimit(RLIMIT_NOFILE, &limit), 0) << "setrlimit: " << std::strerror(errno);
});
struct rlimit new_limit = {.rlim_cur = 100, .rlim_max = limit.rlim_max};
ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &new_limit), 0) << "setrlimit: " << std::strerror(errno);
for (size_t i = 0; i <= new_limit.rlim_cur; i++) {
test_helper::ScopedTempDir scoped_dir;
fbl::unique_fd mem_fd(test_helper::MemFdCreate("try_create", O_WRONLY));
EXPECT_TRUE(mem_fd.is_valid()) << "memfd_create: " << std::strerror(errno);
}
});
EXPECT_TRUE(helper.WaitForChildren());
}
TEST(EventFdSemTest, WaitFromThread) {
test_helper::EventFdSem sem(0);
std::thread t1([&sem]() { SAFE_SYSCALL(sem.Wait()); });
SAFE_SYSCALL(sem.Notify(1));
t1.join();
}
TEST(EventFdSemTest, WaitFromFork) {
test_helper::EventFdSem sem(0);
test_helper::ForkHelper helper;
helper.RunInForkedProcess([&sem]() { SAFE_SYSCALL(sem.Wait()); });
SAFE_SYSCALL(sem.Notify(1));
EXPECT_TRUE(helper.WaitForChildren());
}
TEST(EventFdSemTest, CheckWaitThreaded) {
test_helper::EventFdSem sem(0);
std::atomic<bool> flag = false;
std::thread t1([&sem, &flag]() {
SAFE_SYSCALL(sem.Wait());
flag = true;
});
EXPECT_EQ(flag, false);
SAFE_SYSCALL(sem.Notify(1));
t1.join();
EXPECT_EQ(flag, true);
}
TEST(EventFdSemTest, NotifyFromThread) {
test_helper::EventFdSem sem(0);
std::thread t1([&sem]() { SAFE_SYSCALL(sem.Notify(1)); });
t1.join();
SAFE_SYSCALL(sem.Wait());
}
TEST(EventFdSemTest, NotifyFromFork) {
test_helper::EventFdSem sem(0);
test_helper::ForkHelper helper;
helper.RunInForkedProcess([&sem]() { SAFE_SYSCALL(sem.Notify(1)); });
EXPECT_TRUE(helper.WaitForChildren());
SAFE_SYSCALL(sem.Wait());
}
TEST(TestHelperTest, WaitForChildrenWithResults) {
test_helper::ForkHelper helper;
pid_t failing_child = helper.RunInForkedProcess([] { exit(1); });
pid_t succeeding_child = helper.RunInForkedProcess([] { exit(0); });
helper.ExpectExitValue(0);
auto results = helper.WaitForChildrenWithResults();
ASSERT_EQ(results.size(), 2u);
bool found_failing = false;
bool found_succeeding = false;
for (const auto& result : results) {
if (result.subprocess_id == failing_child) {
EXPECT_FALSE(result.determined_result);
EXPECT_EQ(result.subprocess_exit_status, 1);
found_failing = true;
} else if (result.subprocess_id == succeeding_child) {
EXPECT_TRUE(result.determined_result);
EXPECT_EQ(result.subprocess_exit_status, 0);
found_succeeding = true;
}
}
EXPECT_TRUE(found_failing);
EXPECT_TRUE(found_succeeding);
}
TEST(TestHelperTest, WaitForChild) {
test_helper::ForkHelper helper;
pid_t failing_child = helper.RunInForkedProcess([] { exit(1); });
pid_t succeeding_child = helper.RunInForkedProcess([] { exit(0); });
helper.ExpectExitValue(0);
auto failing_result = helper.WaitForChild(failing_child);
EXPECT_FALSE(failing_result.determined_result);
EXPECT_EQ(failing_result.subprocess_exit_status, 1);
auto succeeding_result = helper.WaitForChild(succeeding_child);
EXPECT_TRUE(succeeding_result.determined_result);
EXPECT_EQ(succeeding_result.subprocess_exit_status, 0);
}
TEST(TestHelperTest, WaitForChildMultipleTimes) {
test_helper::ForkHelper helper;
pid_t child1 = helper.RunInForkedProcess([] { exit(1); });
pid_t child2 = helper.RunInForkedProcess([] { exit(0); });
pid_t child3 = helper.RunInForkedProcess([] { exit(2); });
helper.ExpectExitValue(0);
test_helper::ForkResult result1 = helper.WaitForChild(child1);
EXPECT_FALSE(result1.determined_result);
EXPECT_EQ(result1.subprocess_exit_status, 1);
test_helper::ForkResult result2 = helper.WaitForChild(child2);
EXPECT_TRUE(result2.determined_result);
EXPECT_EQ(result2.subprocess_exit_status, 0);
test_helper::ForkResult result3 = helper.WaitForChild(child3);
EXPECT_FALSE(result3.determined_result);
EXPECT_EQ(result3.subprocess_exit_status, 2);
}
TEST(TestHelperTest, WaitForChildThenWaitForChildren) {
test_helper::ForkHelper helper;
helper.RunInForkedProcess([] { exit(0); });
pid_t second_child = helper.RunInForkedProcess([] { exit(20); });
helper.RunInForkedProcess([] { exit(0); });
helper.ExpectExitValue(0);
// Wait for the second child specifically.
test_helper::ForkResult second_result = helper.WaitForChild(second_child);
EXPECT_FALSE(second_result.determined_result);
EXPECT_EQ(second_result.subprocess_exit_status, 20);
EXPECT_EQ(second_result.subprocess_id, second_child);
// Wait for the remaining children. The children not explicitly waited for will be reaped here.
EXPECT_TRUE(helper.WaitForChildren());
}
TEST(TestHelperTest, MultipleChildrenPassing) {
test_helper::ForkHelper helper;
for (int i = 0; i < 5; i++) {
helper.RunInForkedProcess([]() { exit(0); });
}
helper.ExpectExitValue(0);
auto results = helper.WaitForChildrenWithResults();
EXPECT_EQ(results.size(), 5u);
for (const auto& result : results) {
EXPECT_TRUE(result.determined_result);
}
}
TEST(TestHelperTest, MultipleChildrenFailing) {
test_helper::ForkHelper helper;
helper.ExpectExitValue(0);
for (int i = 0; i < 5; i++) {
helper.RunInForkedProcess([i]() { exit(i + 1); });
}
auto results = helper.WaitForChildrenWithResults();
EXPECT_EQ(results.size(), 5u);
for (const auto& result : results) {
EXPECT_FALSE(result.determined_result);
}
}
} // namespace