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
14using namespace Fortran::runtime;
15using namespace Fortran::runtime::io;
16
17// Pads characters with whitespace when needed
18void 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
26struct InputTest : CrashHandlerFixture {};
27
28TEST(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
70TEST(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
111TEST(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.
135TEST(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
154using ParamTy = std::tuple<std::string, std::vector<int>>;
155
156struct SimpleListInputTest : testing::TestWithParam<ParamTy> {};
157
158TEST_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
188INSTANTIATE_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

source code of flang/unittests/Runtime/ListInputTest.cpp