1ffc67bb3SDavid Spickett //===-- flang/unittests/Runtime/ListInputTest.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 #include "CrashHandlerFixture.h" 10ffc67bb3SDavid Spickett #include "../../runtime/io-error.h" 11ffc67bb3SDavid Spickett #include "flang/Runtime/descriptor.h" 12*c91ba043SMichael Kruse #include "flang/Runtime/io-api-consts.h" 13ffc67bb3SDavid Spickett 14ffc67bb3SDavid Spickett using namespace Fortran::runtime; 15ffc67bb3SDavid Spickett using namespace Fortran::runtime::io; 16ffc67bb3SDavid Spickett 17ffc67bb3SDavid Spickett // Pads characters with whitespace when needed 18ffc67bb3SDavid Spickett void SetCharacter(char *to, std::size_t n, const char *from) { 19ffc67bb3SDavid Spickett auto len{std::strlen(from)}; 20ffc67bb3SDavid Spickett std::memcpy(to, from, std::min(len, n)); 21ffc67bb3SDavid Spickett if (len < n) { 22ffc67bb3SDavid Spickett std::memset(to + len, ' ', n - len); 23ffc67bb3SDavid Spickett } 24ffc67bb3SDavid Spickett } 25ffc67bb3SDavid Spickett 26ffc67bb3SDavid Spickett struct InputTest : CrashHandlerFixture {}; 27ffc67bb3SDavid Spickett 28ffc67bb3SDavid Spickett TEST(InputTest, TestListInputAlphabet) { 29ffc67bb3SDavid Spickett constexpr int numInputBuffers{2}; 30ffc67bb3SDavid Spickett constexpr int maxInputBufferLength{32}; 31ffc67bb3SDavid Spickett char inputBuffers[numInputBuffers][maxInputBufferLength]; 32ffc67bb3SDavid Spickett const char expectedOutput[]{ 33ffc67bb3SDavid Spickett "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "}; 34ffc67bb3SDavid Spickett int j{0}; 35ffc67bb3SDavid Spickett 36ffc67bb3SDavid Spickett // Use _two_ input buffers and _three_ output buffers. Note the `3*` in the 37ffc67bb3SDavid Spickett // _inputBuffers_. 38ffc67bb3SDavid Spickett SetCharacter(inputBuffers[j++], maxInputBufferLength, 39ffc67bb3SDavid Spickett "3*'abcdefghijklmnopqrstuvwxyzABC"); 40ffc67bb3SDavid Spickett SetCharacter( 41ffc67bb3SDavid Spickett inputBuffers[j++], maxInputBufferLength, "DEFGHIJKLMNOPQRSTUVWXYZ'"); 42ffc67bb3SDavid Spickett 43ffc67bb3SDavid Spickett StaticDescriptor<1> staticDescriptor; 44ffc67bb3SDavid Spickett Descriptor &whole{staticDescriptor.descriptor()}; 45ffc67bb3SDavid Spickett SubscriptValue extent[]{numInputBuffers}; 46ffc67bb3SDavid Spickett whole.Establish(TypeCode{CFI_type_char}, maxInputBufferLength, &inputBuffers, 47ffc67bb3SDavid Spickett 1, extent, CFI_attribute_pointer); 48ffc67bb3SDavid Spickett whole.Check(); 49ffc67bb3SDavid Spickett auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; 50ffc67bb3SDavid Spickett 51ffc67bb3SDavid Spickett constexpr int numOutputBuffers{3}; 52ffc67bb3SDavid Spickett constexpr int outputBufferLength{54}; 53ffc67bb3SDavid Spickett char outputBuffers[numOutputBuffers][outputBufferLength]{}; 54ffc67bb3SDavid Spickett for (j = 0; j < numOutputBuffers; ++j) { 55ffc67bb3SDavid Spickett IONAME(InputAscii)(cookie, outputBuffers[j], outputBufferLength - 1); 56ffc67bb3SDavid Spickett } 57ffc67bb3SDavid Spickett 58ffc67bb3SDavid Spickett const auto status{IONAME(EndIoStatement)(cookie)}; 59ffc67bb3SDavid Spickett ASSERT_EQ(status, 0) << "list-directed input failed, status " 60ffc67bb3SDavid Spickett << static_cast<int>(status) << '\n'; 61ffc67bb3SDavid Spickett 62ffc67bb3SDavid Spickett // Verify results that the _two_ ascii inputs result in _three_ alphabets 63ffc67bb3SDavid Spickett for (j = 0; j < numOutputBuffers; ++j) { 64ffc67bb3SDavid Spickett ASSERT_EQ(std::strcmp(outputBuffers[j], expectedOutput), 0) 65ffc67bb3SDavid Spickett << "wanted outputBuffers[" << j << "]=" << expectedOutput << ", got '" 66ffc67bb3SDavid Spickett << outputBuffers[j] << "'\n"; 67ffc67bb3SDavid Spickett } 68ffc67bb3SDavid Spickett } 69ffc67bb3SDavid Spickett 70ffc67bb3SDavid Spickett TEST(InputTest, TestListInputIntegerList) { 71ffc67bb3SDavid Spickett constexpr int numBuffers{2}; 72ffc67bb3SDavid Spickett constexpr int maxBufferLength{32}; 73ffc67bb3SDavid Spickett char buffer[numBuffers][maxBufferLength]; 74ffc67bb3SDavid Spickett int j{0}; 75ffc67bb3SDavid Spickett SetCharacter(buffer[j++], maxBufferLength, "1 2 2*3 ,"); 76ffc67bb3SDavid Spickett SetCharacter(buffer[j++], maxBufferLength, ",6,,8,2*"); 77ffc67bb3SDavid Spickett 78ffc67bb3SDavid Spickett StaticDescriptor<1> staticDescriptor; 79ffc67bb3SDavid Spickett Descriptor &whole{staticDescriptor.descriptor()}; 80ffc67bb3SDavid Spickett SubscriptValue extent[]{numBuffers}; 81ffc67bb3SDavid Spickett whole.Establish(TypeCode{CFI_type_char}, maxBufferLength, &buffer, 1, extent, 82ffc67bb3SDavid Spickett CFI_attribute_pointer); 83ffc67bb3SDavid Spickett whole.Check(); 84ffc67bb3SDavid Spickett auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; 85ffc67bb3SDavid Spickett 86ffc67bb3SDavid Spickett constexpr int listInputLength{10}; 87ffc67bb3SDavid Spickett 88ffc67bb3SDavid Spickett // Negative numbers will be overwritten by _expectedOutput_, and positive 89ffc67bb3SDavid Spickett // numbers will not be as their indices are "Null values" of the Fortran 2018 90ffc67bb3SDavid Spickett // standard 13.10.3.2 in the format strings _buffer_ 91ffc67bb3SDavid Spickett std::int64_t actualOutput[listInputLength]{ 92ffc67bb3SDavid Spickett -1, -2, -3, -4, 5, -6, 7, -8, 9, 10}; 93ffc67bb3SDavid Spickett const std::int64_t expectedOutput[listInputLength]{ 94ffc67bb3SDavid Spickett 1, 2, 3, 3, 5, 6, 7, 8, 9, 10}; 95ffc67bb3SDavid Spickett for (j = 0; j < listInputLength; ++j) { 96ffc67bb3SDavid Spickett IONAME(InputInteger)(cookie, actualOutput[j]); 97ffc67bb3SDavid Spickett } 98ffc67bb3SDavid Spickett 99ffc67bb3SDavid Spickett const auto status{IONAME(EndIoStatement)(cookie)}; 100ffc67bb3SDavid Spickett ASSERT_EQ(status, 0) << "list-directed input failed, status " 101ffc67bb3SDavid Spickett << static_cast<int>(status) << '\n'; 102ffc67bb3SDavid Spickett 103ffc67bb3SDavid Spickett // Verify the calls to _InputInteger_ resulted in _expectedOutput_ 104ffc67bb3SDavid Spickett for (j = 0; j < listInputLength; ++j) { 105ffc67bb3SDavid Spickett ASSERT_EQ(actualOutput[j], expectedOutput[j]) 106ffc67bb3SDavid Spickett << "wanted actualOutput[" << j << "]==" << expectedOutput[j] << ", got " 107ffc67bb3SDavid Spickett << actualOutput[j] << '\n'; 108ffc67bb3SDavid Spickett } 109ffc67bb3SDavid Spickett } 110ffc67bb3SDavid Spickett 111ffc67bb3SDavid Spickett TEST(InputTest, TestListInputInvalidFormatWithSingleSuccess) { 112ffc67bb3SDavid Spickett std::string formatBuffer{"1, g"}; 113ffc67bb3SDavid Spickett constexpr int numBuffers{1}; 114ffc67bb3SDavid Spickett 115ffc67bb3SDavid Spickett StaticDescriptor<1> staticDescriptor; 116ffc67bb3SDavid Spickett Descriptor &whole{staticDescriptor.descriptor()}; 117ffc67bb3SDavid Spickett SubscriptValue extent[]{numBuffers}; 118ffc67bb3SDavid Spickett whole.Establish(TypeCode{CFI_type_char}, formatBuffer.size(), 119ffc67bb3SDavid Spickett formatBuffer.data(), 1, extent, CFI_attribute_pointer); 120ffc67bb3SDavid Spickett whole.Check(); 121ffc67bb3SDavid Spickett 122ffc67bb3SDavid Spickett auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; 123ffc67bb3SDavid Spickett std::int64_t dummy; 124ffc67bb3SDavid Spickett 125ffc67bb3SDavid Spickett // Perform _InputInteger_ once successfully 126ffc67bb3SDavid Spickett IONAME(InputInteger)(cookie, dummy); 127ffc67bb3SDavid Spickett 128ffc67bb3SDavid Spickett // Perform failing InputInteger 129ffc67bb3SDavid Spickett ASSERT_DEATH(IONAME(InputInteger)(cookie, dummy), 130ffc67bb3SDavid Spickett "Bad character 'g' in INTEGER input field"); 131ffc67bb3SDavid Spickett } 132ffc67bb3SDavid Spickett 133ffc67bb3SDavid Spickett // Same test as _TestListInputInvalidFormatWithSingleSuccess_, however no 134ffc67bb3SDavid Spickett // successful call to _InputInteger_ is performed first. 135ffc67bb3SDavid Spickett TEST(InputTest, TestListInputInvalidFormat) { 136ffc67bb3SDavid Spickett std::string formatBuffer{"g"}; 137ffc67bb3SDavid Spickett constexpr int numBuffers{1}; 138ffc67bb3SDavid Spickett 139ffc67bb3SDavid Spickett StaticDescriptor<1> staticDescriptor; 140ffc67bb3SDavid Spickett Descriptor &whole{staticDescriptor.descriptor()}; 141ffc67bb3SDavid Spickett SubscriptValue extent[]{numBuffers}; 142ffc67bb3SDavid Spickett whole.Establish(TypeCode{CFI_type_char}, formatBuffer.size(), 143ffc67bb3SDavid Spickett formatBuffer.data(), 1, extent, CFI_attribute_pointer); 144ffc67bb3SDavid Spickett whole.Check(); 145ffc67bb3SDavid Spickett 146ffc67bb3SDavid Spickett auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; 147ffc67bb3SDavid Spickett std::int64_t dummy; 148ffc67bb3SDavid Spickett 149ffc67bb3SDavid Spickett // Perform failing InputInteger 150ffc67bb3SDavid Spickett ASSERT_DEATH(IONAME(InputInteger)(cookie, dummy), 151ffc67bb3SDavid Spickett "Bad character 'g' in INTEGER input field"); 152ffc67bb3SDavid Spickett } 153ffc67bb3SDavid Spickett 154ffc67bb3SDavid Spickett using ParamTy = std::tuple<std::string, std::vector<int>>; 155ffc67bb3SDavid Spickett 156ffc67bb3SDavid Spickett struct SimpleListInputTest : testing::TestWithParam<ParamTy> {}; 157ffc67bb3SDavid Spickett 158ffc67bb3SDavid Spickett TEST_P(SimpleListInputTest, TestListInput) { 159ffc67bb3SDavid Spickett auto [formatBuffer, expectedOutput] = GetParam(); 160ffc67bb3SDavid Spickett constexpr int numBuffers{1}; 161ffc67bb3SDavid Spickett 162ffc67bb3SDavid Spickett StaticDescriptor<1> staticDescriptor; 163ffc67bb3SDavid Spickett Descriptor &whole{staticDescriptor.descriptor()}; 164ffc67bb3SDavid Spickett SubscriptValue extent[]{numBuffers}; 165ffc67bb3SDavid Spickett whole.Establish(TypeCode{CFI_type_char}, formatBuffer.size(), 166ffc67bb3SDavid Spickett formatBuffer.data(), 1, extent, CFI_attribute_pointer); 167ffc67bb3SDavid Spickett whole.Check(); 168ffc67bb3SDavid Spickett auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; 169ffc67bb3SDavid Spickett 170ffc67bb3SDavid Spickett const auto listInputLength{expectedOutput.size()}; 171ffc67bb3SDavid Spickett std::vector<std::int64_t> actualOutput(listInputLength); 172ffc67bb3SDavid Spickett for (std::size_t j = 0; j < listInputLength; ++j) { 173ffc67bb3SDavid Spickett IONAME(InputInteger)(cookie, actualOutput[j]); 174ffc67bb3SDavid Spickett } 175ffc67bb3SDavid Spickett 176ffc67bb3SDavid Spickett const auto status{IONAME(EndIoStatement)(cookie)}; 177ffc67bb3SDavid Spickett ASSERT_EQ(status, 0) << "list-directed input failed, status " 178ffc67bb3SDavid Spickett << static_cast<int>(status) << '\n'; 179ffc67bb3SDavid Spickett 180ffc67bb3SDavid Spickett // Verify the calls to _InputInteger_ resulted in _expectedOutput_ 181ffc67bb3SDavid Spickett for (std::size_t j = 0; j < listInputLength; ++j) { 182ffc67bb3SDavid Spickett ASSERT_EQ(actualOutput[j], expectedOutput[j]) 183ffc67bb3SDavid Spickett << "wanted actualOutput[" << j << "]==" << expectedOutput[j] << ", got " 184ffc67bb3SDavid Spickett << actualOutput[j] << '\n'; 185ffc67bb3SDavid Spickett } 186ffc67bb3SDavid Spickett } 187ffc67bb3SDavid Spickett 188ffc67bb3SDavid Spickett INSTANTIATE_TEST_SUITE_P(SimpleListInputTestInstantiation, SimpleListInputTest, 189ffc67bb3SDavid Spickett testing::Values(std::make_tuple("", std::vector<int>{}), 190ffc67bb3SDavid Spickett std::make_tuple("0", std::vector<int>{}), 191ffc67bb3SDavid Spickett std::make_tuple("1", std::vector<int>{1}), 192ffc67bb3SDavid Spickett std::make_tuple("1, 2", std::vector<int>{1, 2}), 193ffc67bb3SDavid Spickett std::make_tuple("3*2", std::vector<int>{2, 2, 2}))); 194