1 //===-- flang/unittests/Runtime/Namelist.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 "../../runtime/namelist.h"
10 #include "CrashHandlerFixture.h"
12 #include "flang/Runtime/descriptor.h"
13 #include "flang/Runtime/io-api.h"
18 #include <gtest/gtest.h>
23 using namespace Fortran::runtime
;
24 using namespace Fortran::runtime::io
;
26 struct NamelistTests
: CrashHandlerFixture
{};
28 static void ClearDescriptorStorage(const Descriptor
&descriptor
) {
29 std::memset(descriptor
.raw().base_addr
, 0,
30 descriptor
.Elements() * descriptor
.ElementBytes());
33 TEST(NamelistTests
, BasicSanity
) {
34 static constexpr int numLines
{12};
35 static constexpr int lineLength
{32};
36 static char buffer
[numLines
][lineLength
];
37 StaticDescriptor
<1, true> statDescs
[1];
38 Descriptor
&internalDesc
{statDescs
[0].descriptor()};
39 SubscriptValue extent
[]{numLines
};
40 internalDesc
.Establish(TypeCode
{CFI_type_char
}, /*elementBytes=*/lineLength
,
41 &buffer
, 1, extent
, CFI_attribute_pointer
);
43 std::vector
<int> ints
;
44 for (int j
{0}; j
< 20; ++j
) {
45 ints
.push_back(j
% 2 == 0 ? (1 << j
) : -(1 << j
));
47 std::vector
<double> reals
{0.0, -0.0, std::numeric_limits
<double>::infinity(),
48 -std::numeric_limits
<double>::infinity(),
49 std::numeric_limits
<double>::quiet_NaN(),
50 std::numeric_limits
<double>::max(), std::numeric_limits
<double>::lowest(),
51 std::numeric_limits
<double>::epsilon()};
52 std::vector
<std::uint8_t> logicals
;
53 logicals
.push_back(false);
54 logicals
.push_back(true);
55 logicals
.push_back(false);
56 std::vector
<std::complex<float>> complexes
;
57 complexes
.push_back(std::complex<float>{123.0, -0.5});
58 std::vector
<std::string
> characters
;
59 characters
.emplace_back("aBcDeFgHiJkLmNoPqRsTuVwXyZ");
60 characters
.emplace_back("0123456789'\"..............");
61 // Copy the data into new descriptors
62 OwningPtr
<Descriptor
> intDesc
{
63 MakeArray
<TypeCategory::Integer
, static_cast<int>(sizeof(int))>(
64 std::vector
<int>{5, 4}, std::move(ints
))};
65 OwningPtr
<Descriptor
> realDesc
{
66 MakeArray
<TypeCategory::Real
, static_cast<int>(sizeof(double))>(
67 std::vector
<int>{4, 2}, std::move(reals
))};
68 OwningPtr
<Descriptor
> logicalDesc
{
69 MakeArray
<TypeCategory::Logical
, static_cast<int>(sizeof(std::uint8_t))>(
70 std::vector
<int>{3}, std::move(logicals
))};
71 OwningPtr
<Descriptor
> complexDesc
{
72 MakeArray
<TypeCategory::Complex
, static_cast<int>(sizeof(float))>(
73 std::vector
<int>{}, std::move(complexes
))};
74 OwningPtr
<Descriptor
> characterDesc
{MakeArray
<TypeCategory::Character
, 1>(
75 std::vector
<int>{2}, std::move(characters
), characters
[0].size())};
76 // Create a NAMELIST group
77 static constexpr int items
{5};
78 const NamelistGroup::Item itemArray
[items
]{{"ints", *intDesc
},
79 {"reals", *realDesc
}, {"logicals", *logicalDesc
},
80 {"complexes", *complexDesc
}, {"characters", *characterDesc
}};
81 const NamelistGroup group
{"group1", items
, itemArray
};
82 // Do an internal NAMELIST write and check results
83 auto outCookie1
{IONAME(BeginInternalArrayListOutput
)(
84 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
85 ASSERT_TRUE(IONAME(SetDelim
)(outCookie1
, "APOSTROPHE", 10));
86 ASSERT_TRUE(IONAME(OutputNamelist
)(outCookie1
, group
));
87 auto outStatus1
{IONAME(EndIoStatement
)(outCookie1
)};
88 ASSERT_EQ(outStatus1
, 0) << "Failed namelist output sanity, status "
89 << static_cast<int>(outStatus1
);
91 static const std::string expect
{"&GROUP1 INTS= 1 -2 4 -8 16 -32 "
92 " 64 -128 256 -512 1024 -2048 "
93 " 4096 -8192 16384 -32768 65536 "
94 " -131072 262144 -524288,REALS= "
95 " 0. -0. Inf -Inf NaN "
96 " 1.7976931348623157E+308 "
97 " -1.7976931348623157E+308 "
98 " 2.220446049250313E-16,LOGICALS="
99 "F T F,COMPLEXES= (123.,-.5), "
100 " CHARACTERS= 'aBcDeFgHiJkLmNoPqR"
101 "sTuVwXyZ' '0123456789''\"........"
103 std::string got
{buffer
[0], sizeof buffer
};
104 EXPECT_EQ(got
, expect
);
106 // Clear the arrays, read them back, write out again, and compare
107 ClearDescriptorStorage(*intDesc
);
108 ClearDescriptorStorage(*realDesc
);
109 ClearDescriptorStorage(*logicalDesc
);
110 ClearDescriptorStorage(*complexDesc
);
111 ClearDescriptorStorage(*characterDesc
);
112 auto inCookie
{IONAME(BeginInternalArrayListInput
)(
113 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
114 ASSERT_TRUE(IONAME(InputNamelist
)(inCookie
, group
));
115 auto inStatus
{IONAME(EndIoStatement
)(inCookie
)};
116 ASSERT_EQ(inStatus
, 0) << "Failed namelist input sanity, status "
117 << static_cast<int>(inStatus
);
118 auto outCookie2
{IONAME(BeginInternalArrayListOutput
)(
119 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
120 ASSERT_TRUE(IONAME(SetDelim
)(outCookie2
, "APOSTROPHE", 10));
121 ASSERT_TRUE(IONAME(OutputNamelist
)(outCookie2
, group
));
122 auto outStatus2
{IONAME(EndIoStatement
)(outCookie2
)};
123 ASSERT_EQ(outStatus2
, 0) << "Failed namelist output sanity rewrite, status "
124 << static_cast<int>(outStatus2
);
125 std::string got2
{buffer
[0], sizeof buffer
};
126 EXPECT_EQ(got2
, expect
);
129 TEST(NamelistTests
, Subscripts
) {
130 // INTEGER :: A(-1:0, -1:1)
131 OwningPtr
<Descriptor
> aDesc
{
132 MakeArray
<TypeCategory::Integer
, static_cast<int>(sizeof(int))>(
133 std::vector
<int>{2, 3}, std::vector
<int>(6, 0))};
134 aDesc
->GetDimension(0).SetBounds(-1, 0);
135 aDesc
->GetDimension(1).SetBounds(-1, 1);
136 const NamelistGroup::Item items
[]{{"a", *aDesc
}};
137 const NamelistGroup group
{"justa", 1, items
};
138 static char t1
[]{"&justa A(0,+1:-1:-2)=1 2/"};
139 StaticDescriptor
<1, true> statDesc
;
140 Descriptor
&internalDesc
{statDesc
.descriptor()};
141 internalDesc
.Establish(TypeCode
{CFI_type_char
},
142 /*elementBytes=*/std::strlen(t1
), t1
, 0, nullptr, CFI_attribute_pointer
);
143 auto inCookie
{IONAME(BeginInternalArrayListInput
)(
144 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
145 ASSERT_TRUE(IONAME(InputNamelist
)(inCookie
, group
));
146 auto inStatus
{IONAME(EndIoStatement
)(inCookie
)};
147 ASSERT_EQ(inStatus
, 0) << "Failed namelist input subscripts, status "
148 << static_cast<int>(inStatus
);
150 internalDesc
.Establish(TypeCode
{CFI_type_char
}, /*elementBytes=*/sizeof out
,
151 out
, 0, nullptr, CFI_attribute_pointer
);
152 auto outCookie
{IONAME(BeginInternalArrayListOutput
)(
153 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
154 ASSERT_TRUE(IONAME(OutputNamelist
)(outCookie
, group
));
155 auto outStatus
{IONAME(EndIoStatement
)(outCookie
)};
156 ASSERT_EQ(outStatus
, 0)
157 << "Failed namelist output subscripts rewrite, status "
158 << static_cast<int>(outStatus
);
159 std::string got
{out
, sizeof out
};
160 static const std::string expect
{"&JUSTA A= 0 2 0 0 0 1/ "};
161 EXPECT_EQ(got
, expect
);
164 TEST(NamelistTests
, ShortArrayInput
) {
165 OwningPtr
<Descriptor
> aDesc
{
166 MakeArray
<TypeCategory::Integer
, static_cast<int>(sizeof(int))>(
167 std::vector
<int>{2}, std::vector
<int>(2, -1))};
168 OwningPtr
<Descriptor
> bDesc
{
169 MakeArray
<TypeCategory::Integer
, static_cast<int>(sizeof(int))>(
170 std::vector
<int>{2}, std::vector
<int>(2, -2))};
171 const NamelistGroup::Item items
[]{{"a", *aDesc
}, {"b", *bDesc
}};
172 const NamelistGroup group
{"nl", 2, items
};
173 // Two 12-character lines of internal input
174 static char t1
[]{"&nl a = 1 b "
176 StaticDescriptor
<1, true> statDesc
;
177 Descriptor
&internalDesc
{statDesc
.descriptor()};
178 SubscriptValue shape
{2};
179 internalDesc
.Establish(1, 12, t1
, 1, &shape
, CFI_attribute_pointer
);
180 auto inCookie
{IONAME(BeginInternalArrayListInput
)(
181 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
182 ASSERT_TRUE(IONAME(InputNamelist
)(inCookie
, group
));
183 auto inStatus
{IONAME(EndIoStatement
)(inCookie
)};
184 ASSERT_EQ(inStatus
, 0) << "Failed namelist input subscripts, status "
185 << static_cast<int>(inStatus
);
186 EXPECT_EQ(*aDesc
->ZeroBasedIndexedElement
<int>(0), 1);
187 EXPECT_EQ(*aDesc
->ZeroBasedIndexedElement
<int>(1), -1);
188 EXPECT_EQ(*bDesc
->ZeroBasedIndexedElement
<int>(0), 2);
189 EXPECT_EQ(*bDesc
->ZeroBasedIndexedElement
<int>(1), -2);
192 TEST(NamelistTests
, ScalarSubstring
) {
193 OwningPtr
<Descriptor
> scDesc
{MakeArray
<TypeCategory::Character
, 1>(
194 std::vector
<int>{}, std::vector
<std::string
>{"abcdefgh"}, 8)};
195 const NamelistGroup::Item items
[]{{"a", *scDesc
}};
196 const NamelistGroup group
{"justa", 1, items
};
197 static char t1
[]{"&justa A(2:5)='BCDE'/"};
198 StaticDescriptor
<1, true> statDesc
;
199 Descriptor
&internalDesc
{statDesc
.descriptor()};
200 internalDesc
.Establish(TypeCode
{CFI_type_char
},
201 /*elementBytes=*/std::strlen(t1
), t1
, 0, nullptr, CFI_attribute_pointer
);
202 auto inCookie
{IONAME(BeginInternalArrayListInput
)(
203 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
204 ASSERT_TRUE(IONAME(InputNamelist
)(inCookie
, group
));
205 ASSERT_EQ(IONAME(EndIoStatement
)(inCookie
), IostatOk
)
206 << "namelist scalar substring input";
208 internalDesc
.Establish(TypeCode
{CFI_type_char
}, /*elementBytes=*/sizeof out
,
209 out
, 0, nullptr, CFI_attribute_pointer
);
210 auto outCookie
{IONAME(BeginInternalArrayListOutput
)(
211 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
212 ASSERT_TRUE(IONAME(SetDelim
)(outCookie
, "apostrophe", 10));
213 ASSERT_TRUE(IONAME(OutputNamelist
)(outCookie
, group
));
214 ASSERT_EQ(IONAME(EndIoStatement
)(outCookie
), IostatOk
) << "namelist output";
215 std::string got
{out
, sizeof out
};
216 static const std::string expect
{"&JUSTA A= 'aBCDEfgh'/ "};
217 EXPECT_EQ(got
, expect
);
220 TEST(NamelistTests
, ArraySubstring
) {
221 OwningPtr
<Descriptor
> scDesc
{
222 MakeArray
<TypeCategory::Character
, 1>(std::vector
<int>{2},
223 std::vector
<std::string
>{"abcdefgh", "ijklmnop"}, 8)};
224 const NamelistGroup::Item items
[]{{"a", *scDesc
}};
225 const NamelistGroup group
{"justa", 1, items
};
226 static char t1
[]{"&justa A(:)(2:+5)='BCDE' 'JKLM'/"};
227 StaticDescriptor
<1, true> statDesc
;
228 Descriptor
&internalDesc
{statDesc
.descriptor()};
229 internalDesc
.Establish(TypeCode
{CFI_type_char
},
230 /*elementBytes=*/std::strlen(t1
), t1
, 0, nullptr, CFI_attribute_pointer
);
231 auto inCookie
{IONAME(BeginInternalArrayListInput
)(
232 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
233 ASSERT_TRUE(IONAME(InputNamelist
)(inCookie
, group
));
234 ASSERT_EQ(IONAME(EndIoStatement
)(inCookie
), IostatOk
)
235 << "namelist scalar substring input";
237 internalDesc
.Establish(TypeCode
{CFI_type_char
}, /*elementBytes=*/sizeof out
,
238 out
, 0, nullptr, CFI_attribute_pointer
);
239 auto outCookie
{IONAME(BeginInternalArrayListOutput
)(
240 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
241 ASSERT_TRUE(IONAME(SetDelim
)(outCookie
, "apostrophe", 10));
242 ASSERT_TRUE(IONAME(OutputNamelist
)(outCookie
, group
));
243 ASSERT_EQ(IONAME(EndIoStatement
)(outCookie
), IostatOk
) << "namelist output";
244 std::string got
{out
, sizeof out
};
245 static const std::string expect
{"&JUSTA A= 'aBCDEfgh' 'iJKLMnop'/ "};
246 EXPECT_EQ(got
, expect
);
249 TEST(NamelistTests
, Skip
) {
250 OwningPtr
<Descriptor
> scDesc
{
251 MakeArray
<TypeCategory::Integer
, static_cast<int>(sizeof(int))>(
252 std::vector
<int>{}, std::vector
<int>{-1})};
253 const NamelistGroup::Item items
[]{{"j", *scDesc
}};
254 const NamelistGroup group
{"nml", 1, items
};
255 static char t1
[]{"&skip a='str''ing'/&nml j=123/"};
256 StaticDescriptor
<1, true> statDesc
;
257 Descriptor
&internalDesc
{statDesc
.descriptor()};
258 internalDesc
.Establish(TypeCode
{CFI_type_char
},
259 /*elementBytes=*/std::strlen(t1
), t1
, 0, nullptr, CFI_attribute_pointer
);
260 auto inCookie
{IONAME(BeginInternalArrayListInput
)(
261 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
262 ASSERT_TRUE(IONAME(InputNamelist
)(inCookie
, group
));
263 ASSERT_EQ(IONAME(EndIoStatement
)(inCookie
), IostatOk
)
264 << "namelist input with skipping";
266 internalDesc
.Establish(TypeCode
{CFI_type_char
}, /*elementBytes=*/sizeof out
,
267 out
, 0, nullptr, CFI_attribute_pointer
);
268 auto outCookie
{IONAME(BeginInternalArrayListOutput
)(
269 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
270 ASSERT_TRUE(IONAME(OutputNamelist
)(outCookie
, group
));
271 ASSERT_EQ(IONAME(EndIoStatement
)(outCookie
), IostatOk
) << "namelist output";
272 std::string got
{out
, sizeof out
};
273 static const std::string expect
{"&NML J= 123/ "};
274 EXPECT_EQ(got
, expect
);
277 // Tests DECIMAL=COMMA mode
278 TEST(NamelistTests
, Comma
) {
279 OwningPtr
<Descriptor
> scDesc
{
280 MakeArray
<TypeCategory::Complex
, static_cast<int>(sizeof(float))>(
281 std::vector
<int>{2}, std::vector
<std::complex<float>>{{}, {}})};
282 const NamelistGroup::Item items
[]{{"z", *scDesc
}};
283 const NamelistGroup group
{"nml", 1, items
};
284 static char t1
[]{"&nml z=(-1,0;2,0);(-3,0;0,5)/"};
285 StaticDescriptor
<1, true> statDesc
;
286 Descriptor
&internalDesc
{statDesc
.descriptor()};
287 internalDesc
.Establish(TypeCode
{CFI_type_char
},
288 /*elementBytes=*/std::strlen(t1
), t1
, 0, nullptr, CFI_attribute_pointer
);
289 auto inCookie
{IONAME(BeginInternalArrayListInput
)(
290 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
291 ASSERT_TRUE(IONAME(SetDecimal
)(inCookie
, "COMMA", 5));
292 ASSERT_TRUE(IONAME(InputNamelist
)(inCookie
, group
));
293 ASSERT_EQ(IONAME(EndIoStatement
)(inCookie
), IostatOk
)
294 << "namelist input with skipping";
296 internalDesc
.Establish(TypeCode
{CFI_type_char
}, /*elementBytes=*/sizeof out
,
297 out
, 0, nullptr, CFI_attribute_pointer
);
298 auto outCookie
{IONAME(BeginInternalArrayListOutput
)(
299 internalDesc
, nullptr, 0, __FILE__
, __LINE__
)};
300 ASSERT_TRUE(IONAME(SetDecimal
)(outCookie
, "COMMA", 5));
301 ASSERT_TRUE(IONAME(OutputNamelist
)(outCookie
, group
));
302 ASSERT_EQ(IONAME(EndIoStatement
)(outCookie
), IostatOk
) << "namelist output";
303 std::string got
{out
, sizeof out
};
304 static const std::string expect
{"&NML Z= (-1,;2,) (-3,;,5)/ "};
305 EXPECT_EQ(got
, expect
);
308 // TODO: Internal NAMELIST error tests