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