diff --git a/flang/unittests/CMakeLists.txt b/flang/unittests/CMakeLists.txt --- a/flang/unittests/CMakeLists.txt +++ b/flang/unittests/CMakeLists.txt @@ -40,6 +40,7 @@ add_subdirectory(Decimal) add_subdirectory(Evaluate) add_subdirectory(Runtime) +add_subdirectory(RuntimeGTest) if (FLANG_BUILD_NEW_DRIVER) add_subdirectory(Frontend) diff --git a/flang/unittests/RuntimeGTest/CMakeLists.txt b/flang/unittests/RuntimeGTest/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/flang/unittests/RuntimeGTest/CMakeLists.txt @@ -0,0 +1,9 @@ +add_flang_unittest(FlangRuntimeTests + CharacterTest.cpp + RuntimeTesting.cpp +) + +target_link_libraries(FlangRuntimeTests + PRIVATE + FortranRuntime +) diff --git a/flang/unittests/RuntimeGTest/CharacterTest.cpp b/flang/unittests/RuntimeGTest/CharacterTest.cpp new file mode 100644 --- /dev/null +++ b/flang/unittests/RuntimeGTest/CharacterTest.cpp @@ -0,0 +1,74 @@ +// Basic sanity tests of CHARACTER API; exhaustive testing will be done +// in Fortran. + +#include "../../runtime/character.h" +#include "RuntimeTesting.h" +#include "gtest/gtest.h" +#include +#include +#include + +using namespace Fortran::runtime; + +struct CharacterTests : RuntimeTestFixture {}; + +TEST_F(CharacterTests, AppendAndPad) { + for (std::size_t limit{0}; limit < 8; ++limit) { + char x[8]; + std::size_t xLen{0}; + std::memset(x, 0, sizeof x); + xLen = RTNAME(CharacterAppend1)(x, limit, xLen, "abc", 3); + xLen = RTNAME(CharacterAppend1)(x, limit, xLen, "DE", 2); + RTNAME(CharacterPad1)(x, limit, xLen); + ASSERT_LE(xLen, limit) << "xLen " << xLen << ">" << limit; + if (x[limit]) { + EXPECT_TRUE(false) << "x[" << limit << "]='" << x[limit] << "'\n"; + x[limit] = '\0'; + } + ASSERT_FALSE(std::memcmp(x, "abcDE ", limit)) << "x = '" << x << "'"; + } +} + +using ParamT = std::tuple; + +struct CharacterComparisonTestsFixture + : public RuntimeTestFixture, + public ::testing::WithParamInterface { + void SetUp() { + RuntimeTestFixture::SetUp(); + std::tie(x, y, xBytes, yBytes, expect) = GetParam(); + } + void SwapParams() { + std::swap(x, y); + std::swap(xBytes, yBytes); + expect = -expect; + } + void DoCharacterComparison() { + int cmp{RTNAME(CharacterCompareScalar1)(x, y, xBytes, yBytes)}; + char buf[2][8]; + std::memset(buf, 0, sizeof buf); + std::memcpy(buf[0], x, xBytes); + std::memcpy(buf[1], y, yBytes); + ASSERT_EQ(cmp, expect) << "compare '" << buf[0] << "'(" << xBytes + << ") to '" << buf[1] << "'(" << yBytes << "), got " + << cmp << ", should be " << expect << '\n'; + } + const char *x; + const char *y; + int xBytes; + int yBytes; + int expect; +}; + +TEST_P(CharacterComparisonTestsFixture, CompareCharacters) { + DoCharacterComparison(); + SwapParams(); + DoCharacterComparison(); +} + +INSTANTIATE_TEST_CASE_P(CharacterComparisonTests, + CharacterComparisonTestsFixture, + ::testing::Values(std::make_tuple("abc", "abc", 3, 3, 0), + std::make_tuple("abc", "def", 3, 3, -1), + std::make_tuple("ab ", "abc", 3, 2, 0), + std::make_tuple("abc", "abc", 2, 3, -1)), ); diff --git a/flang/unittests/RuntimeGTest/RuntimeTesting.h b/flang/unittests/RuntimeGTest/RuntimeTesting.h new file mode 100644 --- /dev/null +++ b/flang/unittests/RuntimeGTest/RuntimeTesting.h @@ -0,0 +1,19 @@ +#ifndef LLVM_FLANG_UNITTESTS_RUNTIMEGTEST_RUNTIMETESTING_H +#define LLVM_FLANG_UNITTESTS_RUNTIMEGTEST_RUNTIMETESTING_H + +#include "gtest/gtest.h" +#include + +struct RuntimeTestFixture : ::testing::Test { + RuntimeTestFixture(); + void SetUp(); + void TearDown(); + +private: + bool IsCrashHandlerRegistered; +}; + +// Defines a CHARACTER object with padding when needed +void SetCharacter(char *, std::size_t, const char *); + +#endif // LLVM_FLANG_UNITTESTS_RUNTIMEGTEST_RUNTIMETESTING_H diff --git a/flang/unittests/RuntimeGTest/RuntimeTesting.cpp b/flang/unittests/RuntimeGTest/RuntimeTesting.cpp new file mode 100644 --- /dev/null +++ b/flang/unittests/RuntimeGTest/RuntimeTesting.cpp @@ -0,0 +1,43 @@ +#include "RuntimeTesting.h" +#include "../../runtime/terminator.h" +#include +#include +#include +#include +#include + +static int failures{0}; + +// Override the Fortran runtime's Crash() for testing purposes +static void CatchCrash( + const char *sourceFile, int sourceLine, const char *message, va_list &ap) { + char buffer[1000]; + std::vsnprintf(buffer, sizeof buffer, message, ap); + va_end(ap); + llvm::errs() << (sourceFile ? sourceFile : "unknown source file") << '(' + << sourceLine << "): CRASH: " << buffer << '\n'; + failures++; +} + +RuntimeTestFixture::RuntimeTestFixture() : IsCrashHandlerRegistered{false} {} + +void RuntimeTestFixture::SetUp() { + if (not IsCrashHandlerRegistered) + Fortran::runtime::Terminator::RegisterCrashHandler(CatchCrash); + IsCrashHandlerRegistered = true; +} + +void RuntimeTestFixture::TearDown() { + ASSERT_EQ(failures, 0) + << "Found " << failures << " runtime crashes in teardown phase of test " + << ::testing::UnitTest::GetInstance()->current_test_info()->name(); + failures = 0; +} + +void SetCharacter(char *to, std::size_t n, const char *from) { + auto len{std::strlen(from)}; + std::memcpy(to, from, std::min(len, n)); + if (len < n) { + std::memset(to + len, ' ', n - len); + } +}