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,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,131 @@ +//===-- 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file flang/unittests/RuntimeGTest/CharacterTest.cpp +/// +/// \brief Basic sanity tests of CHAR API; exhaustive testing will be done +/// in Fortran. Similar testing infrastructure also exists in the parallel +/// directory `flang/unittests/Runtime` while we port those tests to use GTest. +/// +//===----------------------------------------------------------------------===// + +#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 x[limitMax]; + static std::size_t xLen{0}; + for (std::size_t limit{0}; limit < limitMax; ++limit, xLen = 0) { + std::memset(x, 0, sizeof x); + xLen = RTNAME(CharacterAppend1)(x, limit, xLen, "abc", 3); + xLen = RTNAME(CharacterAppend1)(x, limit, xLen, "DE", 2); + ASSERT_LE(xLen, limit) << "xLen " << xLen << ">" << limit; + RTNAME(CharacterPad1)(x, limit, xLen); + EXPECT_EQ(x[limit], '\0') << "x[" << limit << "]='" << x[limit] << "'"; + x[limit] = x[limit] ? '\0' : x[limit]; + ASSERT_EQ(std::memcmp(x, "abcDE ", limit), 0) << "x = '" << x << "'"; + } +} + +template +using CHARCOMP_FUNC = + std::function; + +// Maps character types to character comparison functions +template CHARCOMP_FUNC CreatecharacterComparisonFunc(); + +template <> CHARCOMP_FUNC CreatecharacterComparisonFunc() { + return RTNAME(CharacterCompareScalar1); +} + +template <> CHARCOMP_FUNC CreatecharacterComparisonFunc() { + return RTNAME(CharacterCompareScalar2); +} + +template <> CHARCOMP_FUNC CreatecharacterComparisonFunc() { + return RTNAME(CharacterCompareScalar4); +} + +// Types of _values_ over which tests are parameterized +template +using PARAMETER = std::tuple; + +template std::vector> CreateParameters(); + +template <> std::vector> CreateParameters() { + return { + 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), + }; +} + +template <> std::vector> CreateParameters() { + return { + 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 <> std::vector> CreateParameters() { + return { + 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(CreateParameters()), + characterComparisonFunc(CreatecharacterComparisonFunc()) {} + std::vector> parameters; + CHARCOMP_FUNC characterComparisonFunc; +}; + +using CHARACTERS = ::testing::Types; +TYPED_TEST_CASE(CharacterComparisonTests, CHARACTERS); + +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'; + } +}