xref: /llvm-project/flang/unittests/Runtime/CharacterTest.cpp (revision c870632ef6162fbdccaad8cd09420728220ad344)
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