1 //===-- flang/unittests/Runtime/ListInputTest.cpp ---------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "CrashHandlerFixture.h"
10 #include "../../runtime/io-error.h"
11 #include "flang/Runtime/descriptor.h"
12 #include "flang/Runtime/io-api.h"
14 using namespace Fortran::runtime
;
15 using namespace Fortran::runtime::io
;
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
));
22 std::memset(to
+ len
, ' ', n
- len
);
26 struct InputTest
: CrashHandlerFixture
{};
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 "};
36 // Use _two_ input buffers and _three_ output buffers. Note the `3*` in the
38 SetCharacter(inputBuffers
[j
++], maxInputBufferLength
,
39 "3*'abcdefghijklmnopqrstuvwxyzABC");
41 inputBuffers
[j
++], maxInputBufferLength
, "DEFGHIJKLMNOPQRSTUVWXYZ'");
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
);
49 auto *cookie
{IONAME(BeginInternalArrayListInput
)(whole
)};
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);
58 const auto status
{IONAME(EndIoStatement
)(cookie
)};
59 ASSERT_EQ(status
, 0) << "list-directed input failed, status "
60 << static_cast<int>(status
) << '\n';
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";
70 TEST(InputTest
, TestListInputIntegerList
) {
71 constexpr int numBuffers
{2};
72 constexpr int maxBufferLength
{32};
73 char buffer
[numBuffers
][maxBufferLength
];
75 SetCharacter(buffer
[j
++], maxBufferLength
, "1 2 2*3 ,");
76 SetCharacter(buffer
[j
++], maxBufferLength
, ",6,,8,2*");
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
);
84 auto *cookie
{IONAME(BeginInternalArrayListInput
)(whole
)};
86 constexpr int listInputLength
{10};
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
]);
99 const auto status
{IONAME(EndIoStatement
)(cookie
)};
100 ASSERT_EQ(status
, 0) << "list-directed input failed, status "
101 << static_cast<int>(status
) << '\n';
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';
111 TEST(InputTest
, TestListInputInvalidFormatWithSingleSuccess
) {
112 std::string formatBuffer
{"1, g"};
113 constexpr int numBuffers
{1};
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
);
122 auto *cookie
{IONAME(BeginInternalArrayListInput
)(whole
)};
125 // Perform _InputInteger_ once successfully
126 IONAME(InputInteger
)(cookie
, dummy
);
128 // Perform failing InputInteger
129 ASSERT_DEATH(IONAME(InputInteger
)(cookie
, dummy
),
130 "Bad character 'g' in INTEGER input field");
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};
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
);
146 auto *cookie
{IONAME(BeginInternalArrayListInput
)(whole
)};
149 // Perform failing InputInteger
150 ASSERT_DEATH(IONAME(InputInteger
)(cookie
, dummy
),
151 "Bad character 'g' in INTEGER input field");
154 using ParamTy
= std::tuple
<std::string
, std::vector
<int>>;
156 struct SimpleListInputTest
: testing::TestWithParam
<ParamTy
> {};
158 TEST_P(SimpleListInputTest
, TestListInput
) {
159 auto [formatBuffer
, expectedOutput
] = GetParam();
160 constexpr int numBuffers
{1};
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
);
168 auto *cookie
{IONAME(BeginInternalArrayListInput
)(whole
)};
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
]);
176 const auto status
{IONAME(EndIoStatement
)(cookie
)};
177 ASSERT_EQ(status
, 0) << "list-directed input failed, status "
178 << static_cast<int>(status
) << '\n';
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';
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})));