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/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt --- a/flang/unittests/Runtime/CMakeLists.txt +++ b/flang/unittests/Runtime/CMakeLists.txt @@ -46,8 +46,3 @@ RuntimeTesting FortranRuntime ) - -add_flang_nongtest_unittest(character - RuntimeTesting - FortranRuntime -) diff --git a/flang/unittests/Runtime/character.cpp b/flang/unittests/Runtime/character.cpp deleted file mode 100644 --- a/flang/unittests/Runtime/character.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Basic sanity tests of CHARACTER API; exhaustive testing will be done -// in Fortran. - -#include "../../runtime/character.h" -#include "testing.h" -#include - -using namespace Fortran::runtime; - -static void AppendAndPad(std::size_t 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); - if (xLen > limit) { - Fail() << "xLen " << xLen << ">" << limit << '\n'; - } - if (x[limit]) { - Fail() << "x[" << limit << "]='" << x[limit] << "'\n"; - x[limit] = '\0'; - } - if (std::memcmp(x, "abcDE ", limit)) { - Fail() << "x = '" << x << "'\n"; - } -} - -static void TestCharCompare(const char *x, const char *y, std::size_t xBytes, - std::size_t yBytes, int expect) { - int cmp{RTNAME(CharacterCompareScalar1)(x, y, xBytes, yBytes)}; - if (cmp != expect) { - char buf[2][8]; - std::memset(buf, 0, sizeof buf); - std::memcpy(buf[0], x, xBytes); - std::memcpy(buf[1], y, yBytes); - Fail() << "compare '" << buf[0] << "'(" << xBytes << ") to '" << buf[1] - << "'(" << yBytes << "), got " << cmp << ", should be " << expect - << '\n'; - } -} - -static void Compare(const char *x, const char *y, std::size_t xBytes, - std::size_t yBytes, int expect) { - TestCharCompare(x, y, xBytes, yBytes, expect); - TestCharCompare(y, x, yBytes, xBytes, -expect); -} - -static void Scan( - const char *str, const char *set, bool back, std::size_t expect) { - auto res{RTNAME(Scan1)(str, std::strlen(str), set, std::strlen(set), back)}; - if (res != expect) { - Fail() << "Scan(" << str << ',' << set << ",back=" << back << "): got " - << res << ", should be " << expect << '\n'; - } -} - -static void Verify( - const char *str, const char *set, bool back, std::size_t expect) { - auto res{RTNAME(Verify1)(str, std::strlen(str), set, std::strlen(set), back)}; - if (res != expect) { - Fail() << "Verify(" << str << ',' << set << ",back=" << back << "): got " - << res << ", should be " << expect << '\n'; - } -} - -int main() { - StartTests(); - for (std::size_t j{0}; j < 8; ++j) { - AppendAndPad(j); - } - Compare("abc", "abc", 3, 3, 0); - Compare("abc", "def", 3, 3, -1); - Compare("ab ", "abc", 3, 2, 0); - Compare("abc", "abc", 2, 3, -1); - Scan("abc", "abc", false, 1); - Scan("abc", "abc", true, 3); - Scan("abc", "cde", false, 3); - Scan("abc", "cde", true, 3); - Scan("abc", "x", false, 0); - Scan("", "x", false, 0); - Verify("abc", "abc", false, 0); - Verify("abc", "abc", true, 0); - Verify("abc", "cde", false, 1); - Verify("abc", "cde", true, 2); - Verify("abc", "x", false, 1); - Verify("", "x", false, 0); - return EndTests(); -} 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,8 @@ +add_flang_unittest(FlangRuntimeTests + CharacterTest.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,282 @@ +//===-- flang/unittests/RuntimeGTest/CharacterTest.cpp ----------*- C++ -*-===// +// +// 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 "../../runtime/character.h" +#include "gtest/gtest.h" +#include +#include +#include +#include + +using namespace Fortran::runtime; + +TEST(CharacterTests, AppendAndPad) { + static constexpr int limitMax{8}; + static char buffer[limitMax]; + static std::size_t offset{0}; + for (std::size_t limit{0}; limit < limitMax; ++limit, offset = 0) { + std::memset(buffer, 0, sizeof buffer); + + // Ensure appending characters does not overrun the limit + offset = RTNAME(CharacterAppend1)(buffer, limit, offset, "abc", 3); + offset = RTNAME(CharacterAppend1)(buffer, limit, offset, "DE", 2); + ASSERT_LE(offset, limit) << "offset " << offset << ">" << limit; + + // Ensure whitespace padding does not overrun limit, the string is still + // null-terminated, and string matches the expected value up to the limit. + RTNAME(CharacterPad1)(buffer, limit, offset); + EXPECT_EQ(buffer[limit], '\0') + << "buffer[" << limit << "]='" << buffer[limit] << "'"; + buffer[limit] = buffer[limit] ? '\0' : buffer[limit]; + ASSERT_EQ(std::memcmp(buffer, "abcDE ", limit), 0) + << "buffer = '" << buffer << "'"; + } +} + +TEST(CharacterTests, CharacterAppend1Overrun) { + static constexpr int bufferSize{4}; + static constexpr std::size_t limit{2}; + static char buffer[bufferSize]; + static std::size_t offset{0}; + std::memset(buffer, 0, sizeof buffer); + offset = RTNAME(CharacterAppend1)(buffer, limit, offset, "1234", bufferSize); + ASSERT_EQ(offset, limit) << "CharacterAppend1 did not halt at limit = " + << limit << ", but at offset = " << offset; +} + +//------------------------------------------------------------------------------ +/// Tests and infrastructure for character comparison functions +//------------------------------------------------------------------------------ + +template +using ComparisonFuncTy = + std::function; + +using ComparisonFuncsTy = std::tuple, + ComparisonFuncTy, ComparisonFuncTy>; + +// These comparison functions are the systems under test in the +// CharacterComparisonTests test cases. +static ComparisonFuncsTy comparisonFuncs{ + RTNAME(CharacterCompareScalar1), + RTNAME(CharacterCompareScalar2), + RTNAME(CharacterCompareScalar4), +}; + +// Types of _values_ over which comparison tests are parameterized +template +using ComparisonParametersTy = + std::vector>; + +using ComparisonTestCasesTy = std::tuple, + ComparisonParametersTy, ComparisonParametersTy>; + +static ComparisonTestCasesTy comparisonTestCases{ + { + 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), + }, + { + std::make_tuple(u"abc", u"abc", 3, 3, 0), + std::make_tuple(u"abc", u"def", 3, 3, -1), + std::make_tuple(u"ab ", u"abc", 3, 2, 0), + std::make_tuple(u"abc", u"abc", 2, 3, -1), + }, + { + std::make_tuple(U"abc", U"abc", 3, 3, 0), + std::make_tuple(U"abc", U"def", 3, 3, -1), + std::make_tuple(U"ab ", U"abc", 3, 2, 0), + std::make_tuple(U"abc", U"abc", 2, 3, -1), + }}; + +template +struct CharacterComparisonTests : public ::testing::Test { + CharacterComparisonTests() + : parameters{std::get>(comparisonTestCases)}, + characterComparisonFunc{ + std::get>(comparisonFuncs)} {} + ComparisonParametersTy parameters; + ComparisonFuncTy characterComparisonFunc; +}; + +using CharacterTypes = ::testing::Types; +TYPED_TEST_CASE(CharacterComparisonTests, CharacterTypes); + +TYPED_TEST(CharacterComparisonTests, CompareCharacters) { + for (auto &[x, y, xBytes, yBytes, expect] : this->parameters) { + int cmp{this->characterComparisonFunc(x, y, xBytes, yBytes)}; + TypeParam 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 '" << x << "'(" << xBytes << ") to '" + << y << "'(" << yBytes << "), got " << cmp + << ", should be " << expect << '\n'; + + // Perform the same test with the parameters reversed and the difference + // negated + std::swap(x, y); + std::swap(xBytes, yBytes); + expect = -expect; + + cmp = this->characterComparisonFunc(x, y, xBytes, yBytes); + std::memset(buf, 0, sizeof buf); + std::memcpy(buf[0], x, xBytes); + std::memcpy(buf[1], y, yBytes); + ASSERT_EQ(cmp, expect) << "compare '" << x << "'(" << xBytes << ") to '" + << y << "'(" << yBytes << "), got " << cmp + << ", should be " << expect << '\n'; + } +} + +//------------------------------------------------------------------------------ +/// Tests and infrastructure for Scan functions +//------------------------------------------------------------------------------ + +template +using ScanFuncTy = std::function; + +using ScanFuncsTy = + std::tuple, ScanFuncTy, ScanFuncTy>; + +// These functions are the systems under test in CharacterScanTests test cases. +static ScanFuncsTy scanFuncs{ + RTNAME(Scan1), + RTNAME(Scan2), + RTNAME(Scan4), +}; + +// Types of _values_ over which tests are parameterized +template +using ScanParametersTy = + std::vector>; + +using ScanTestCasesTy = std::tuple, + ScanParametersTy, ScanParametersTy>; + +static ScanTestCasesTy scanTestCases{ + { + std::make_tuple("abc", "abc", false, 1), + std::make_tuple("abc", "abc", true, 3), + std::make_tuple("abc", "cde", false, 3), + std::make_tuple("abc", "cde", true, 3), + std::make_tuple("abc", "x", false, 0), + std::make_tuple("", "x", false, 0), + }, + { + std::make_tuple(u"abc", u"abc", false, 1), + std::make_tuple(u"abc", u"abc", true, 3), + std::make_tuple(u"abc", u"cde", false, 3), + std::make_tuple(u"abc", u"cde", true, 3), + std::make_tuple(u"abc", u"x", false, 0), + std::make_tuple(u"", u"x", false, 0), + }, + { + std::make_tuple(U"abc", U"abc", false, 1), + std::make_tuple(U"abc", U"abc", true, 3), + std::make_tuple(U"abc", U"cde", false, 3), + std::make_tuple(U"abc", U"cde", true, 3), + std::make_tuple(U"abc", U"x", false, 0), + std::make_tuple(U"", U"x", false, 0), + }}; + +template struct CharacterScanTests : public ::testing::Test { + CharacterScanTests() + : parameters{std::get>(scanTestCases)}, + characterScanFunc{std::get>(scanFuncs)} {} + ScanParametersTy parameters; + ScanFuncTy characterScanFunc; +}; + +// Type-parameterized over the same character types as CharacterComparisonTests +TYPED_TEST_CASE(CharacterScanTests, CharacterTypes); + +TYPED_TEST(CharacterScanTests, ScanCharacters) { + for (auto const &[str, set, back, expect] : this->parameters) { + auto res{ + this->characterScanFunc(str, std::char_traits::length(str), + set, std::char_traits::length(set), back)}; + ASSERT_EQ(res, expect) << "Scan(" << str << ',' << set << ",back=" << back + << "): got " << res << ", should be " << expect; + } +} + +//------------------------------------------------------------------------------ +/// Tests and infrastructure for Verify functions +//------------------------------------------------------------------------------ +template +using VerifyFuncTy = std::function; + +using VerifyFuncsTy = std::tuple, VerifyFuncTy, + VerifyFuncTy>; + +// These functions are the systems under test in CharacterVerifyTests test cases +static VerifyFuncsTy verifyFuncs{ + RTNAME(Verify1), + RTNAME(Verify2), + RTNAME(Verify4), +}; + +// Types of _values_ over which tests are parameterized +template +using VerifyParametersTy = + std::vector>; + +using VerifyTestCasesTy = std::tuple, + VerifyParametersTy, VerifyParametersTy>; + +static VerifyTestCasesTy verifyTestCases{ + { + std::make_tuple("abc", "abc", false, 0), + std::make_tuple("abc", "abc", true, 0), + std::make_tuple("abc", "cde", false, 1), + std::make_tuple("abc", "cde", true, 2), + std::make_tuple("abc", "x", false, 1), + std::make_tuple("", "x", false, 0), + }, + { + std::make_tuple(u"abc", u"abc", false, 0), + std::make_tuple(u"abc", u"abc", true, 0), + std::make_tuple(u"abc", u"cde", false, 1), + std::make_tuple(u"abc", u"cde", true, 2), + std::make_tuple(u"abc", u"x", false, 1), + std::make_tuple(u"", u"x", false, 0), + }, + { + std::make_tuple(U"abc", U"abc", false, 0), + std::make_tuple(U"abc", U"abc", true, 0), + std::make_tuple(U"abc", U"cde", false, 1), + std::make_tuple(U"abc", U"cde", true, 2), + std::make_tuple(U"abc", U"x", false, 1), + std::make_tuple(U"", U"x", false, 0), + }}; + +template struct CharacterVerifyTests : public ::testing::Test { + CharacterVerifyTests() + : parameters{std::get>(verifyTestCases)}, + characterVerifyFunc{std::get>(verifyFuncs)} {} + VerifyParametersTy parameters; + VerifyFuncTy characterVerifyFunc; +}; + +// Type-parameterized over the same character types as CharacterComparisonTests +TYPED_TEST_CASE(CharacterVerifyTests, CharacterTypes); + +TYPED_TEST(CharacterVerifyTests, VerifyCharacters) { + for (auto const &[str, set, back, expect] : this->parameters) { + auto res{ + this->characterVerifyFunc(str, std::char_traits::length(str), + set, std::char_traits::length(set), back)}; + ASSERT_EQ(res, expect) << "Verify(" << str << ',' << set << ",back=" << back + << "): got " << res << ", should be " << expect; + } +}