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.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(s: from)}; |
20 | std::memcpy(dest: to, src: from, n: std::min(a: len, b: n)); |
21 | if (len < n) { |
22 | std::memset(s: to + len, c: ' ', n: 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(to: inputBuffers[j++], n: maxInputBufferLength, |
39 | from: "3*'abcdefghijklmnopqrstuvwxyzABC" ); |
40 | SetCharacter( |
41 | to: inputBuffers[j++], n: maxInputBufferLength, from: "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(to: buffer[j++], n: maxBufferLength, from: "1 2 2*3 ," ); |
76 | SetCharacter(to: buffer[j++], n: maxBufferLength, from: ",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 | |