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