102 changes: 102 additions & 0 deletions libc/test/UnitTest/LibcDeathTestExecutors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//===-- Implementation of libc death test executors -----------------------===//
//
// 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 "LibcTest.h"

#include "test/UnitTest/ExecuteFunction.h"
#include "test/UnitTest/TestLogger.h"

#include <cassert>

namespace __llvm_libc {
namespace testing {

bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line) {
testutils::ProcessStatus Result = testutils::invoke_in_subprocess(Func, 500);

if (const char *error = Result.get_error()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n" << error << '\n';
return false;
}

if (Result.timed_out()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Process timed out after " << 500 << " milliseconds.\n";
return false;
}

if (Result.exited_normally()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Expected " << LHSStr
<< " to be killed by a signal\nBut it exited normally!\n";
return false;
}

int KilledBy = Result.get_fatal_signal();
assert(KilledBy != 0 && "Not killed by any signal");
if (Signal == -1 || KilledBy == Signal)
return true;

using testutils::signal_as_string;
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< " Expected: " << LHSStr << '\n'
<< "To be killed by signal: " << Signal << '\n'
<< " Which is: " << signal_as_string(Signal) << '\n'
<< " But it was killed by: " << KilledBy << '\n'
<< " Which is: " << signal_as_string(KilledBy) << '\n';
return false;
}

bool Test::testProcessExits(testutils::FunctionCaller *Func, int ExitCode,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line) {
testutils::ProcessStatus Result = testutils::invoke_in_subprocess(Func, 500);

if (const char *error = Result.get_error()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n" << error << '\n';
return false;
}

if (Result.timed_out()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Process timed out after " << 500 << " milliseconds.\n";
return false;
}

if (!Result.exited_normally()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Expected " << LHSStr << '\n'
<< "to exit with exit code " << ExitCode << '\n'
<< "But it exited abnormally!\n";
return false;
}

int ActualExit = Result.get_exit_code();
if (ActualExit == ExitCode)
return true;

Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Expected exit code of: " << LHSStr << '\n'
<< " Which is: " << ActualExit << '\n'
<< " To be equal to: " << RHSStr << '\n'
<< " Which is: " << ExitCode << '\n';
return false;
}

} // namespace testing
} // namespace __llvm_libc
108 changes: 6 additions & 102 deletions libc/test/UnitTest/LibcTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,11 @@
#include "src/__support/CPP/string.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/UInt128.h"
#include "test/UnitTest/ExecuteFunction.h"
#include "test/UnitTest/TestLogger.h"
#include <cassert>

namespace __llvm_libc {
namespace testing {

// This need not be a class as all it has is a single read-write state variable.
// But, we make it class as then its implementation can be hidden from the
// header file.
class RunContext {
public:
enum RunResult { Result_Pass = 1, Result_Fail = 2 };

RunResult status() const { return Status; }

void markFail() { Status = Result_Fail; }

private:
RunResult Status = Result_Pass;
};

namespace internal {

// When the value is UInt128 or __uint128_t, show its hexadecimal digits.
Expand Down Expand Up @@ -149,6 +132,12 @@ bool test(RunContext *Ctx, TestCondition Cond, ValType LHS, ValType RHS,
Test *Test::Start = nullptr;
Test *Test::End = nullptr;

int argc = 0;
char **argv = nullptr;
char **envp = nullptr;

using internal::RunContext;

void Test::addTest(Test *T) {
if (End == nullptr) {
Start = T;
Expand Down Expand Up @@ -326,90 +315,5 @@ bool Test::testMatch(bool MatchResult, MatcherBase &Matcher, const char *LHSStr,
return false;
}

#ifdef ENABLE_SUBPROCESS_TESTS

bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line) {
testutils::ProcessStatus Result = testutils::invoke_in_subprocess(Func, 500);

if (const char *error = Result.get_error()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n" << error << '\n';
return false;
}

if (Result.timed_out()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Process timed out after " << 500 << " milliseconds.\n";
return false;
}

if (Result.exited_normally()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Expected " << LHSStr
<< " to be killed by a signal\nBut it exited normally!\n";
return false;
}

int KilledBy = Result.get_fatal_signal();
assert(KilledBy != 0 && "Not killed by any signal");
if (Signal == -1 || KilledBy == Signal)
return true;

using testutils::signal_as_string;
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< " Expected: " << LHSStr << '\n'
<< "To be killed by signal: " << Signal << '\n'
<< " Which is: " << signal_as_string(Signal) << '\n'
<< " But it was killed by: " << KilledBy << '\n'
<< " Which is: " << signal_as_string(KilledBy) << '\n';
return false;
}

bool Test::testProcessExits(testutils::FunctionCaller *Func, int ExitCode,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line) {
testutils::ProcessStatus Result = testutils::invoke_in_subprocess(Func, 500);

if (const char *error = Result.get_error()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n" << error << '\n';
return false;
}

if (Result.timed_out()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Process timed out after " << 500 << " milliseconds.\n";
return false;
}

if (!Result.exited_normally()) {
Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Expected " << LHSStr << '\n'
<< "to exit with exit code " << ExitCode << '\n'
<< "But it exited abnormally!\n";
return false;
}

int ActualExit = Result.get_exit_code();
if (ActualExit == ExitCode)
return true;

Ctx->markFail();
tlog << File << ":" << Line << ": FAILURE\n"
<< "Expected exit code of: " << LHSStr << '\n'
<< " Which is: " << ActualExit << '\n'
<< " To be equal to: " << RHSStr << '\n'
<< " Which is: " << ExitCode << '\n';
return false;
}

#endif // ENABLE_SUBPROCESS_TESTS
} // namespace testing
} // namespace __llvm_libc
22 changes: 18 additions & 4 deletions libc/test/UnitTest/LibcTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
namespace __llvm_libc {
namespace testing {

class RunContext;

// Only the following conditions are supported. Notice that we do not have
// a TRUE or FALSE condition. That is because, C library funtions do not
// return boolean values, but use integral return values to indicate true or
Expand All @@ -42,6 +40,18 @@ enum TestCondition {

namespace internal {

class RunContext {
public:
enum RunResult { Result_Pass = 1, Result_Fail = 2 };

RunResult status() const { return Status; }

void markFail() { Status = Result_Fail; }

private:
RunResult Status = Result_Pass;
};

template <typename ValType>
bool test(RunContext *Ctx, TestCondition Cond, ValType LHS, ValType RHS,
const char *LHSStr, const char *RHSStr, const char *File,
Expand All @@ -65,9 +75,9 @@ template <typename T> struct Matcher : public MatcherBase {
class Test {
private:
Test *Next = nullptr;
RunContext *Ctx = nullptr;
internal::RunContext *Ctx = nullptr;

void setContext(RunContext *C) { Ctx = C; }
void setContext(internal::RunContext *C) { Ctx = C; }

public:
virtual ~Test() {}
Expand Down Expand Up @@ -161,6 +171,10 @@ class Test {
static Test *End;
};

extern int argc;
extern char **argv;
extern char **envp;

namespace internal {

constexpr bool same_prefix(char const *lhs, char const *rhs, int const len) {
Expand Down
6 changes: 5 additions & 1 deletion libc/test/UnitTest/LibcTestMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ static const char *getTestFilter(int argc, char *argv[]) {
return argc > 1 ? argv[1] : nullptr;
}

int main(int argc, char *argv[]) {
extern "C" int main(int argc, char **argv, char **envp) {
__llvm_libc::testing::argc = argc;
__llvm_libc::testing::argv = argv;
__llvm_libc::testing::envp = envp;

const char *TestFilter = getTestFilter(argc, argv);
return __llvm_libc::testing::Test::runTests(TestFilter);
}
66 changes: 33 additions & 33 deletions libc/test/src/ctype/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,159 +1,159 @@
add_custom_target(libc_ctype_unittests)
add_custom_target(libc-ctype-tests)

add_libc_unittest(
add_libc_test(
isalnum_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isalnum_test.cpp
DEPENDS
libc.src.ctype.isalnum
)

add_libc_unittest(
add_libc_test(
isalpha_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isalpha_test.cpp
DEPENDS
libc.src.ctype.isalpha
)

add_libc_unittest(
add_libc_test(
isascii_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isascii_test.cpp
DEPENDS
libc.src.ctype.isascii
)

add_libc_unittest(
add_libc_test(
isblank_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isblank_test.cpp
DEPENDS
libc.src.ctype.isblank
)

add_libc_unittest(
add_libc_test(
iscntrl_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
iscntrl_test.cpp
DEPENDS
libc.src.ctype.iscntrl
)

add_libc_unittest(
add_libc_test(
isdigit_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isdigit_test.cpp
DEPENDS
libc.src.ctype.isdigit
)

add_libc_unittest(
add_libc_test(
isgraph_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isgraph_test.cpp
DEPENDS
libc.src.ctype.isgraph
)

add_libc_unittest(
add_libc_test(
islower_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
islower_test.cpp
DEPENDS
libc.src.ctype.islower
)

add_libc_unittest(
add_libc_test(
isprint_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isprint_test.cpp
DEPENDS
libc.src.ctype.isprint
)

add_libc_unittest(
add_libc_test(
ispunct_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
ispunct_test.cpp
DEPENDS
libc.src.ctype.ispunct
)

add_libc_unittest(
add_libc_test(
isspace_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isspace_test.cpp
DEPENDS
libc.src.ctype.isspace
)

add_libc_unittest(
add_libc_test(
isupper_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isupper_test.cpp
DEPENDS
libc.src.ctype.isupper
)

add_libc_unittest(
add_libc_test(
isxdigit_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
isxdigit_test.cpp
DEPENDS
libc.src.ctype.isxdigit
)

add_libc_unittest(
add_libc_test(
toascii_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
toascii_test.cpp
DEPENDS
libc.src.ctype.toascii
)

add_libc_unittest(
add_libc_test(
tolower_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
tolower_test.cpp
DEPENDS
libc.src.ctype.tolower
)

add_libc_unittest(
add_libc_test(
toupper_test
SUITE
libc_ctype_unittests
libc-ctype-tests
SRCS
toupper_test.cpp
DEPENDS
Expand Down