brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.2 KiB · 2c7af27 Raw
433 lines · cpp
1//===-- unittests/Runtime/CharacterTest.cpp ---------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// Basic sanity tests of CHARACTER API; exhaustive testing will be done10// in Fortran.11 12#include "flang/Runtime/character.h"13#include "gtest/gtest.h"14#include "flang-rt/runtime/descriptor.h"15#include <cstring>16#include <functional>17#include <tuple>18#include <vector>19 20using namespace Fortran::runtime;21 22using CharacterTypes = ::testing::Types<char, char16_t, char32_t>;23 24// Helper for creating, allocating and filling up a descriptor with data from25// raw character literals, converted to the CHAR type used by the test.26template <typename CHAR>27OwningPtr<Descriptor> CreateDescriptor(const std::vector<SubscriptValue> &shape,28    const std::vector<const char *> &raw_strings) {29  std::size_t length{std::strlen(raw_strings[0])};30 31  OwningPtr<Descriptor> descriptor{Descriptor::Create(sizeof(CHAR), length,32      nullptr, shape.size(), nullptr, CFI_attribute_allocatable)};33  int rank{static_cast<int>(shape.size())};34  // Use a weird lower bound of 2 to flush out subscripting bugs35  for (int j{0}; j < rank; ++j) {36    descriptor->GetDimension(j).SetBounds(2, shape[j] + 1);37  }38  if (descriptor->Allocate(kNoAsyncObject) != 0) {39    return nullptr;40  }41 42  std::size_t offset = 0;43  for (const char *raw : raw_strings) {44    std::basic_string<CHAR> converted{raw, raw + length};45    std::copy(converted.begin(), converted.end(),46        descriptor->OffsetElement<CHAR>(offset * length * sizeof(CHAR)));47    ++offset;48  }49 50  return descriptor;51}52 53TEST(CharacterTests, AppendAndPad) {54  static constexpr int limitMax{8};55  static char buffer[limitMax];56  static std::size_t offset{0};57  for (std::size_t limit{0}; limit < limitMax; ++limit, offset = 0) {58    std::memset(buffer, 0, sizeof buffer);59 60    // Ensure appending characters does not overrun the limit61    offset = RTNAME(CharacterAppend1)(buffer, limit, offset, "abc", 3);62    offset = RTNAME(CharacterAppend1)(buffer, limit, offset, "DE", 2);63    ASSERT_LE(offset, limit) << "offset " << offset << ">" << limit;64 65    // Ensure whitespace padding does not overrun limit, the string is still66    // null-terminated, and string matches the expected value up to the limit.67    RTNAME(CharacterPad1)(buffer, limit, offset);68    EXPECT_EQ(buffer[limit], '\0')69        << "buffer[" << limit << "]='" << buffer[limit] << "'";70    buffer[limit] = buffer[limit] ? '\0' : buffer[limit];71    ASSERT_EQ(std::memcmp(buffer, "abcDE   ", limit), 0)72        << "buffer = '" << buffer << "'";73  }74}75 76TEST(CharacterTests, CharacterAppend1Overrun) {77  static constexpr int bufferSize{4};78  static constexpr std::size_t limit{2};79  static char buffer[bufferSize];80  static std::size_t offset{0};81  std::memset(buffer, 0, sizeof buffer);82  offset = RTNAME(CharacterAppend1)(buffer, limit, offset, "1234", bufferSize);83  ASSERT_EQ(offset, limit) << "CharacterAppend1 did not halt at limit = "84                           << limit << ", but at offset = " << offset;85}86 87// Test ADJUSTL() and ADJUSTR()88template <typename CHAR> struct AdjustLRTests : public ::testing::Test {};89TYPED_TEST_SUITE(AdjustLRTests, CharacterTypes, );90 91struct AdjustLRTestCase {92  const char *input, *output;93};94 95template <typename CHAR>96void RunAdjustLRTest(const char *which,97    const std::function<void(98        Descriptor &, const Descriptor &, const char *, int)> &adjust,99    const char *inputRaw, const char *outputRaw) {100  OwningPtr<Descriptor> input{CreateDescriptor<CHAR>({}, {inputRaw})};101  ASSERT_NE(input, nullptr);102  ASSERT_TRUE(input->IsAllocated());103 104  StaticDescriptor<1> outputStaticDescriptor;105  Descriptor &output{outputStaticDescriptor.descriptor()};106 107  adjust(output, *input, /*sourceFile=*/nullptr, /*sourceLine=*/0);108  std::basic_string<CHAR> got{109      output.OffsetElement<CHAR>(), std::strlen(inputRaw)};110  std::basic_string<CHAR> expect{outputRaw, outputRaw + std::strlen(outputRaw)};111  ASSERT_EQ(got, expect) << which << "('" << inputRaw112                         << "') for CHARACTER(kind=" << sizeof(CHAR) << ")";113}114 115TYPED_TEST(AdjustLRTests, AdjustL) {116  static std::vector<AdjustLRTestCase> testcases{117      {"     where should the spaces be?", "where should the spaces be?     "},118      {"   leading and trailing whitespaces   ",119          "leading and trailing whitespaces      "},120      {"shouldn't change", "shouldn't change"},121  };122 123  for (const auto &t : testcases) {124    RunAdjustLRTest<TypeParam>("Adjustl", RTNAME(Adjustl), t.input, t.output);125  }126}127 128TYPED_TEST(AdjustLRTests, AdjustR) {129  static std::vector<AdjustLRTestCase> testcases{130      {"where should the spaces be?   ", "   where should the spaces be?"},131      {" leading and trailing whitespaces ",132          "  leading and trailing whitespaces"},133      {"shouldn't change", "shouldn't change"},134  };135 136  for (const auto &t : testcases) {137    RunAdjustLRTest<TypeParam>("Adjustr", RTNAME(Adjustr), t.input, t.output);138  }139}140 141//------------------------------------------------------------------------------142/// Tests and infrastructure for character comparison functions143//------------------------------------------------------------------------------144 145template <typename CHAR>146using ComparisonFuncTy =147    std::function<int(const CHAR *, const CHAR *, std::size_t, std::size_t)>;148 149using ComparisonFuncsTy = std::tuple<ComparisonFuncTy<char>,150    ComparisonFuncTy<char16_t>, ComparisonFuncTy<char32_t>>;151 152// These comparison functions are the systems under test in the153// CharacterComparisonTests test cases.154static ComparisonFuncsTy comparisonFuncs{155    RTNAME(CharacterCompareScalar1),156    RTNAME(CharacterCompareScalar2),157    RTNAME(CharacterCompareScalar4),158};159 160// Types of _values_ over which comparison tests are parameterized161template <typename CHAR>162using ComparisonParametersTy =163    std::vector<std::tuple<const CHAR *, const CHAR *, int, int, int>>;164 165using ComparisonTestCasesTy = std::tuple<ComparisonParametersTy<char>,166    ComparisonParametersTy<char16_t>, ComparisonParametersTy<char32_t>>;167 168static ComparisonTestCasesTy comparisonTestCases{169    {170        std::make_tuple("abc", "abc", 3, 3, 0),171        std::make_tuple("abc", "def", 3, 3, -1),172        std::make_tuple("ab ", "abc", 3, 2, 0),173        std::make_tuple("abc", "abc", 2, 3, -1),174        std::make_tuple("ab\xff", "ab ", 3, 2, 1),175        std::make_tuple("ab ", "ab\xff", 2, 3, -1),176    },177    {178        std::make_tuple(u"abc", u"abc", 3, 3, 0),179        std::make_tuple(u"abc", u"def", 3, 3, -1),180        std::make_tuple(u"ab ", u"abc", 3, 2, 0),181        std::make_tuple(u"abc", u"abc", 2, 3, -1),182    },183    {184        std::make_tuple(U"abc", U"abc", 3, 3, 0),185        std::make_tuple(U"abc", U"def", 3, 3, -1),186        std::make_tuple(U"ab ", U"abc", 3, 2, 0),187        std::make_tuple(U"abc", U"abc", 2, 3, -1),188    }};189 190template <typename CHAR>191struct CharacterComparisonTests : public ::testing::Test {192  CharacterComparisonTests()193      : parameters{std::get<ComparisonParametersTy<CHAR>>(comparisonTestCases)},194        characterComparisonFunc{195            std::get<ComparisonFuncTy<CHAR>>(comparisonFuncs)} {}196  ComparisonParametersTy<CHAR> parameters;197  ComparisonFuncTy<CHAR> characterComparisonFunc;198};199 200TYPED_TEST_SUITE(CharacterComparisonTests, CharacterTypes, );201 202TYPED_TEST(CharacterComparisonTests, CompareCharacters) {203  for (auto &[x, y, xBytes, yBytes, expect] : this->parameters) {204    int cmp{this->characterComparisonFunc(x, y, xBytes, yBytes)};205    TypeParam buf[2][8];206    std::memset(buf, 0, sizeof buf);207    std::memcpy(buf[0], x, xBytes);208    std::memcpy(buf[1], y, yBytes);209    ASSERT_EQ(cmp, expect) << "compare '" << x << "'(" << xBytes << ") to '"210                           << y << "'(" << yBytes << "), got " << cmp211                           << ", should be " << expect << '\n';212 213    // Perform the same test with the parameters reversed and the difference214    // negated215    std::swap(x, y);216    std::swap(xBytes, yBytes);217    expect = -expect;218 219    cmp = this->characterComparisonFunc(x, y, xBytes, yBytes);220    std::memset(buf, 0, sizeof buf);221    std::memcpy(buf[0], x, xBytes);222    std::memcpy(buf[1], y, yBytes);223    ASSERT_EQ(cmp, expect) << "compare '" << x << "'(" << xBytes << ") to '"224                           << y << "'(" << yBytes << "'), got " << cmp225                           << ", should be " << expect << '\n';226  }227}228 229// Test MIN() and MAX()230struct ExtremumTestCase {231  std::vector<SubscriptValue> shape; // Empty = scalar, non-empty = array.232  std::vector<const char *> x, y, expect;233};234 235template <typename CHAR>236void RunExtremumTests(const char *which,237    std::function<void(Descriptor &, const Descriptor &, const char *, int)>238        function,239    const std::vector<ExtremumTestCase> &testCases) {240  std::stringstream traceMessage;241  traceMessage << which << " for CHARACTER(kind=" << sizeof(CHAR) << ")";242  SCOPED_TRACE(traceMessage.str());243 244  for (const auto &t : testCases) {245    OwningPtr<Descriptor> x = CreateDescriptor<CHAR>(t.shape, t.x);246    OwningPtr<Descriptor> y = CreateDescriptor<CHAR>(t.shape, t.y);247 248    ASSERT_NE(x, nullptr);249    ASSERT_TRUE(x->IsAllocated());250    ASSERT_NE(y, nullptr);251    ASSERT_TRUE(y->IsAllocated());252    function(*x, *y, __FILE__, __LINE__);253 254    std::size_t length = x->ElementBytes() / sizeof(CHAR);255    for (std::size_t i = 0; i < t.x.size(); ++i) {256      std::basic_string<CHAR> got{257          x->OffsetElement<CHAR>(i * x->ElementBytes()), length};258      std::basic_string<CHAR> expect{259          t.expect[i], t.expect[i] + std::strlen(t.expect[i])};260      EXPECT_EQ(expect, got) << "inputs: '" << t.x[i] << "','" << t.y[i] << "'";261    }262 263    x->Deallocate();264    y->Deallocate();265  }266}267 268template <typename CHAR> struct ExtremumTests : public ::testing::Test {};269TYPED_TEST_SUITE(ExtremumTests, CharacterTypes, );270 271TYPED_TEST(ExtremumTests, MinTests) {272  static std::vector<ExtremumTestCase> tests{{{}, {"a"}, {"z"}, {"a"}},273      {{1}, {"zaaa"}, {"aa"}, {"aa  "}},274      {{1, 1}, {"aaz"}, {"aaaaa"}, {"aaaaa"}},275      {{2, 3}, {"a", "b", "c", "d", "E", "f"},276          {"xa", "ya", "az", "dd", "Sz", "cc"},277          {"a ", "b ", "az", "d ", "E ", "cc"}}};278  RunExtremumTests<TypeParam>("MIN", RTNAME(CharacterMin), tests);279}280 281TYPED_TEST(ExtremumTests, MaxTests) {282  static std::vector<ExtremumTestCase> tests{283      {{}, {"a"}, {"z"}, {"z"}},284      {{1}, {"zaa"}, {"aaaaa"}, {"zaa  "}},285      {{1, 1, 1}, {"aaaaa"}, {"aazaa"}, {"aazaa"}},286  };287  RunExtremumTests<TypeParam>("MAX", RTNAME(CharacterMax), tests);288}289 290template <typename CHAR>291void RunAllocationTest(const char *xRaw, const char *yRaw) {292  OwningPtr<Descriptor> x = CreateDescriptor<CHAR>({}, {xRaw});293  OwningPtr<Descriptor> y = CreateDescriptor<CHAR>({}, {yRaw});294 295  ASSERT_NE(x, nullptr);296  ASSERT_TRUE(x->IsAllocated());297  ASSERT_NE(y, nullptr);298  ASSERT_TRUE(y->IsAllocated());299 300  void *old = x->raw().base_addr;301  RTNAME(CharacterMin)(*x, *y, __FILE__, __LINE__);302  EXPECT_EQ(old, x->raw().base_addr);303}304 305TYPED_TEST(ExtremumTests, NoReallocate) {306  // Test that we don't reallocate if the accumulator is already large enough.307  RunAllocationTest<TypeParam>("loooooong", "short");308}309 310// Test search functions INDEX(), SCAN(), and VERIFY()311 312template <typename CHAR>313using SearchFunction = std::function<std::size_t(314    const CHAR *, std::size_t, const CHAR *, std::size_t, bool)>;315template <template <typename> class FUNC>316using CharTypedFunctions =317    std::tuple<FUNC<char>, FUNC<char16_t>, FUNC<char32_t>>;318using SearchFunctions = CharTypedFunctions<SearchFunction>;319struct SearchTestCase {320  const char *x, *y;321  bool back;322  std::size_t expect;323};324 325template <typename CHAR>326void RunSearchTests(const char *which,327    const std::vector<SearchTestCase> &testCases,328    const SearchFunction<CHAR> &function) {329  for (const auto &t : testCases) {330    // Convert default character to desired kind331    std::size_t xLen{std::strlen(t.x)}, yLen{std::strlen(t.y)};332    std::basic_string<CHAR> x{t.x, t.x + xLen};333    std::basic_string<CHAR> y{t.y, t.y + yLen};334    auto got{function(x.data(), xLen, y.data(), yLen, t.back)};335    ASSERT_EQ(got, t.expect)336        << which << "('" << t.x << "','" << t.y << "',back=" << t.back337        << ") for CHARACTER(kind=" << sizeof(CHAR) << "): got " << got338        << ", expected " << t.expect;339  }340}341 342template <typename CHAR> struct SearchTests : public ::testing::Test {};343TYPED_TEST_SUITE(SearchTests, CharacterTypes, );344 345TYPED_TEST(SearchTests, IndexTests) {346  static SearchFunctions functions{347      RTNAME(Index1), RTNAME(Index2), RTNAME(Index4)};348  static std::vector<SearchTestCase> tests{349      {"", "", false, 1},350      {"", "", true, 1},351      {"a", "", false, 1},352      {"a", "", true, 2},353      {"", "a", false, 0},354      {"", "a", true, 0},355      {"aa", "a", false, 1},356      {"aa", "a", true, 2},357      {"aAA", "A", false, 2},358      {"Fortran that I ran", "that I ran", false, 9},359      {"Fortran that I ran", "that I ran", true, 9},360      {"Fortran that you ran", "that I ran", false, 0},361      {"Fortran that you ran", "that I ran", true, 0},362  };363  RunSearchTests(364      "INDEX", tests, std::get<SearchFunction<TypeParam>>(functions));365}366 367TYPED_TEST(SearchTests, ScanTests) {368  static SearchFunctions functions{RTNAME(Scan1), RTNAME(Scan2), RTNAME(Scan4)};369  static std::vector<SearchTestCase> tests{370      {"abc", "abc", false, 1},371      {"abc", "abc", true, 3},372      {"abc", "cde", false, 3},373      {"abc", "cde", true, 3},374      {"abc", "x", false, 0},375      {"", "x", false, 0},376  };377  RunSearchTests("SCAN", tests, std::get<SearchFunction<TypeParam>>(functions));378}379 380TYPED_TEST(SearchTests, VerifyTests) {381  static SearchFunctions functions{382      RTNAME(Verify1), RTNAME(Verify2), RTNAME(Verify4)};383  static std::vector<SearchTestCase> tests{384      {"abc", "abc", false, 0},385      {"abc", "abc", true, 0},386      {"abc", "cde", false, 1},387      {"abc", "cde", true, 2},388      {"abc", "x", false, 1},389      {"", "x", false, 0},390  };391  RunSearchTests(392      "VERIFY", tests, std::get<SearchFunction<TypeParam>>(functions));393}394 395// Test REPEAT()396template <typename CHAR> struct RepeatTests : public ::testing::Test {};397TYPED_TEST_SUITE(RepeatTests, CharacterTypes, );398 399struct RepeatTestCase {400  std::size_t ncopies;401  const char *input, *output;402};403 404template <typename CHAR>405void RunRepeatTest(406    std::size_t ncopies, const char *inputRaw, const char *outputRaw) {407  OwningPtr<Descriptor> input{CreateDescriptor<CHAR>({}, {inputRaw})};408  ASSERT_NE(input, nullptr);409  ASSERT_TRUE(input->IsAllocated());410 411  StaticDescriptor<1> outputStaticDescriptor;412  Descriptor &output{outputStaticDescriptor.descriptor()};413 414  RTNAME(Repeat)(output, *input, ncopies);415  std::basic_string<CHAR> got{416      output.OffsetElement<CHAR>(), output.ElementBytes() / sizeof(CHAR)};417  std::basic_string<CHAR> expect{outputRaw, outputRaw + std::strlen(outputRaw)};418  ASSERT_EQ(got, expect) << "'" << inputRaw << "' * " << ncopies419                         << "' for CHARACTER(kind=" << sizeof(CHAR) << ")";420}421 422TYPED_TEST(RepeatTests, Repeat) {423  static std::vector<RepeatTestCase> testcases{424      {1, "just one copy", "just one copy"},425      {5, "copy.", "copy.copy.copy.copy.copy."},426      {0, "no copies", ""},427  };428 429  for (const auto &t : testcases) {430    RunRepeatTest<TypeParam>(t.ncopies, t.input, t.output);431  }432}433