[lldb] Add ability to hide the root name of a value
[llvm-project.git] / flang / unittests / Runtime / ExternalIOTest.cpp
blob389abf1d0404a79328337a0b775563a28300db0f
1 //===-- flang/unittests/RuntimeGTest/ExternalIOTest.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 // Sanity test for all external I/O modes
11 //===----------------------------------------------------------------------===//
13 #include "CrashHandlerFixture.h"
14 #include "gtest/gtest.h"
15 #include "flang/Runtime/descriptor.h"
16 #include "flang/Runtime/io-api.h"
17 #include "flang/Runtime/main.h"
18 #include "flang/Runtime/stop.h"
19 #include "llvm/Support/raw_ostream.h"
20 #include <cstring>
21 #include <string_view>
23 using namespace Fortran::runtime;
24 using namespace Fortran::runtime::io;
26 struct ExternalIOTests : public CrashHandlerFixture {};
28 TEST(ExternalIOTests, TestDirectUnformatted) {
29 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
30 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
31 Cookie io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
32 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
33 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
34 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
36 std::int64_t buffer;
37 static constexpr std::size_t recl{sizeof buffer};
38 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
39 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
41 int unit{-1};
42 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
43 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
44 << "EndIoStatement() for OpenNewUnit";
46 // INQUIRE(IOLENGTH=) j
47 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
48 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
49 io, reinterpret_cast<const char *>(&buffer), recl, 1))
50 << "OutputUnformattedBlock() for InquireIoLength";
51 ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength";
52 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
53 << "EndIoStatement() for InquireIoLength";
55 static constexpr int records{10};
56 for (int j{1}; j <= records; ++j) {
57 // WRITE(UNIT=unit,REC=j) j
58 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
59 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
61 buffer = j;
62 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
63 io, reinterpret_cast<const char *>(&buffer), 1, recl))
64 << "OutputUnformattedBlock()";
66 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
67 << "EndIoStatement() for OutputUnformattedBlock";
70 for (int j{records}; j >= 1; --j) {
71 // READ(UNIT=unit,REC=j) n
72 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
73 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
74 ASSERT_TRUE(IONAME(InputUnformattedBlock)(
75 io, reinterpret_cast<char *>(&buffer), 1, recl))
76 << "InputUnformattedBlock()";
78 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
79 << "EndIoStatement() for InputUnformattedBlock";
81 ASSERT_EQ(buffer, j) << "Read back " << buffer
82 << " from direct unformatted record " << j
83 << ", expected " << j << '\n';
85 // CLOSE(UNIT=unit,STATUS='DELETE')
86 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
87 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
88 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
89 << "EndIoStatement() for Close";
92 TEST(ExternalIOTests, TestDirectUnformattedSwapped) {
93 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
94 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE')
95 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
96 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
97 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
98 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
99 ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)";
101 std::int64_t buffer;
102 static constexpr std::size_t recl{sizeof buffer};
103 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
104 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
106 int unit{-1};
107 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
108 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
109 << "EndIoStatement() for OpenNewUnit";
111 static constexpr int records{10};
112 for (int j{1}; j <= records; ++j) {
113 // WRITE(UNIT=unit,REC=j) j
114 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
115 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
116 buffer = j;
117 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
118 io, reinterpret_cast<const char *>(&buffer), recl, recl))
119 << "OutputUnformattedBlock()";
120 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
121 << "EndIoStatement() for OutputUnformattedBlock";
124 // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP')
125 io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__);
126 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
127 ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)";
128 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
129 << "EndIoStatement() for OpenUnit";
131 for (int j{records}; j >= 1; --j) {
132 // READ(UNIT=unit,REC=j) n
133 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
134 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
135 ASSERT_TRUE(IONAME(InputUnformattedBlock)(
136 io, reinterpret_cast<char *>(&buffer), recl, recl))
137 << "InputUnformattedBlock()";
138 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
139 << "EndIoStatement() for InputUnformattedBlock";
140 ASSERT_EQ(buffer >> 56, j)
141 << "Read back " << (buffer >> 56) << " from direct unformatted record "
142 << j << ", expected " << j << '\n';
145 // CLOSE(UNIT=unit,STATUS='DELETE')
146 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
147 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
148 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
149 << "EndIoStatement() for Close";
152 TEST(ExternalIOTests, TestSequentialFixedUnformatted) {
153 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
154 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
155 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
156 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
157 << "SetAccess(SEQUENTIAL)";
158 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
159 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
161 std::int64_t buffer;
162 static constexpr std::size_t recl{sizeof buffer};
164 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
165 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
167 int unit{-1};
168 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
169 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
170 << "EndIoStatement() for OpenNewUnit";
172 // INQUIRE(IOLENGTH=) j, ...
173 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
174 for (int j{1}; j <= 3; ++j) {
175 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
176 io, reinterpret_cast<const char *>(&buffer), recl, 1))
177 << "OutputUnformattedBlock() for InquireIoLength";
179 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
180 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
181 << "EndIoStatement() for InquireIoLength";
183 // INQUIRE(IOLENGTH=) j, ...
184 StaticDescriptor<0> staticDescriptor;
185 Descriptor &desc{staticDescriptor.descriptor()};
186 desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0);
187 desc.Dump(stderr);
188 desc.Check();
189 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
190 for (int j{1}; j <= 3; ++j) {
191 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
192 << "OutputDescriptor() for InquireIoLength";
194 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
195 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
196 << "EndIoStatement() for InquireIoLength";
198 static const int records{10};
199 for (int j{1}; j <= records; ++j) {
200 // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO
201 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
202 buffer = j;
203 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
204 io, reinterpret_cast<const char *>(&buffer), recl, recl))
205 << "OutputUnformattedBlock()";
206 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
207 << "EndIoStatement() for OutputUnformattedBlock";
210 // REWIND(UNIT=unit)
211 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
212 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
213 << "EndIoStatement() for Rewind";
215 for (int j{1}; j <= records; ++j) {
216 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
217 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
218 ASSERT_TRUE(IONAME(InputUnformattedBlock)(
219 io, reinterpret_cast<char *>(&buffer), recl, recl))
220 << "InputUnformattedBlock()";
221 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
222 << "EndIoStatement() for InputUnformattedBlock";
223 ASSERT_EQ(buffer, j) << "Read back " << buffer
224 << " from sequential fixed unformatted record " << j
225 << ", expected " << j << '\n';
228 for (int j{records}; j >= 1; --j) {
229 // BACKSPACE(UNIT=unit)
230 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
231 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
232 << "EndIoStatement() for Backspace (before read)";
233 // READ(UNIT=unit) n
234 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
235 ASSERT_TRUE(IONAME(InputUnformattedBlock)(
236 io, reinterpret_cast<char *>(&buffer), recl, recl))
237 << "InputUnformattedBlock()";
238 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
239 << "EndIoStatement() for InputUnformattedBlock";
240 ASSERT_EQ(buffer, j) << "Read back " << buffer
241 << " from sequential fixed unformatted record " << j
242 << " after backspacing, expected " << j << '\n';
243 // BACKSPACE(UNIT=unit)
244 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
245 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
246 << "EndIoStatement() for Backspace (after read)";
249 // CLOSE(UNIT=unit,STATUS='DELETE')
250 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
251 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
252 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
253 << "EndIoStatement() for Close";
256 TEST(ExternalIOTests, TestSequentialVariableUnformatted) {
257 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
258 // FORM='UNFORMATTED',STATUS='SCRATCH')
259 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
261 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
262 << "SetAccess(SEQUENTIAL)";
263 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
264 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
265 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
267 int unit{-1};
268 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
269 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
270 << "EndIoStatement() for OpenNewUnit";
272 static const int records{10};
273 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
274 for (int j{0}; j < records; ++j) {
275 buffer[j] = j;
278 for (int j{1}; j <= records; ++j) {
279 // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO
280 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
281 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(io,
282 reinterpret_cast<const char *>(&buffer), j * sizeof *buffer,
283 sizeof *buffer))
284 << "OutputUnformattedBlock()";
285 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
286 << "EndIoStatement() for OutputUnformattedBlock";
289 // REWIND(UNIT=unit)
290 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
291 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
292 << "EndIoStatement() for Rewind";
293 for (int j{1}; j <= records; ++j) {
294 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
295 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
296 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io,
297 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer))
298 << "InputUnformattedBlock()";
299 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
300 << "EndIoStatement() for InputUnformattedBlock";
301 for (int k{0}; k < j; ++k) {
302 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
303 << " from direct unformatted record " << j
304 << ", expected " << k << '\n';
308 for (int j{records}; j >= 1; --j) {
309 // BACKSPACE(unit)
310 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
311 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
312 << "EndIoStatement() for Backspace (before read)";
313 // READ(unit=unit) n; check
314 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
315 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io,
316 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer))
317 << "InputUnformattedBlock()";
318 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
319 << "EndIoStatement() for InputUnformattedBlock";
320 for (int k{0}; k < j; ++k) {
321 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
322 << " from sequential variable unformatted record "
323 << j << ", expected " << k << '\n';
325 // BACKSPACE(unit)
326 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
327 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
328 << "EndIoStatement() for Backspace (after read)";
331 // CLOSE(UNIT=unit,STATUS='DELETE')
332 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
333 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
334 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
335 << "EndIoStatement() for Close";
338 TEST(ExternalIOTests, TestDirectFormatted) {
339 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
340 // FORM='FORMATTED',RECL=8,STATUS='SCRATCH')
341 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
342 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
343 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
344 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
346 static constexpr std::size_t recl{8};
347 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
348 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
350 int unit{-1};
351 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
352 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
353 << "EndIoStatement() for OpenNewUnit";
355 static constexpr int records{10};
356 static const char fmt[]{"(I4)"};
357 for (int j{1}; j <= records; ++j) {
358 // WRITE(UNIT=unit,FMT=fmt,REC=j) j
359 io = IONAME(BeginExternalFormattedOutput)(
360 fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__);
361 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
362 ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()";
363 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
364 << "EndIoStatement() for OutputInteger64";
367 for (int j{records}; j >= 1; --j) {
368 // READ(UNIT=unit,FMT=fmt,REC=j) n
369 io = IONAME(BeginExternalFormattedInput)(
370 fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__);
371 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
372 std::int64_t buffer;
373 ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()";
374 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
375 << "EndIoStatement() for InputInteger";
377 ASSERT_EQ(buffer, j) << "Read back " << buffer
378 << " from direct formatted record " << j
379 << ", expected " << j << '\n';
382 // CLOSE(UNIT=unit,STATUS='DELETE')
383 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
384 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
385 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
386 << "EndIoStatement() for Close";
389 TEST(ExternalIOTests, TestSequentialVariableFormatted) {
390 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
391 // FORM='FORMATTED',STATUS='SCRATCH')
392 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
393 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
394 << "SetAccess(SEQUENTIAL)";
395 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
396 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
397 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
399 int unit{-1};
400 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
401 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
402 << "EndIoStatement() for OpenNewUnit";
404 static const int records{10};
405 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
406 for (int j{0}; j < records; ++j) {
407 buffer[j] = j;
410 char fmt[32];
411 for (int j{1}; j <= records; ++j) {
412 std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
413 // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO
414 io = IONAME(BeginExternalFormattedOutput)(
415 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
416 for (int k{0}; k < j; ++k) {
417 ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k]))
418 << "OutputInteger64()";
420 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
421 << "EndIoStatement() for OutputInteger64";
424 // REWIND(UNIT=unit)
425 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
426 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
427 << "EndIoStatement() for Rewind";
429 for (int j{1}; j <= records; ++j) {
430 std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
431 // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO
432 io = IONAME(BeginExternalFormattedInput)(
433 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
435 std::int64_t check[records];
436 for (int k{0}; k < j; ++k) {
437 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
439 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
440 << "EndIoStatement() for InputInteger";
442 for (int k{0}; k < j; ++k) {
443 ASSERT_EQ(buffer[k], check[k])
444 << "Read back [" << k << "]=" << check[k]
445 << " from sequential variable formatted record " << j << ", expected "
446 << buffer[k] << '\n';
450 for (int j{records}; j >= 1; --j) {
451 // BACKSPACE(unit)
452 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
453 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
454 << "EndIoStatement() for Backspace (before read)";
456 std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
457 // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check
458 io = IONAME(BeginExternalFormattedInput)(
459 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
461 std::int64_t check[records];
462 for (int k{0}; k < j; ++k) {
463 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
466 std::size_t chars{IONAME(GetSize)(io)};
467 ASSERT_EQ(chars, j * 4u)
468 << "GetSize()=" << chars << ", expected " << (j * 4u) << '\n';
469 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
470 << "EndIoStatement() for InputInteger";
471 for (int k{0}; k < j; ++k) {
472 ASSERT_EQ(buffer[k], check[k])
473 << "Read back [" << k << "]=" << buffer[k]
474 << " from sequential variable formatted record " << j << ", expected "
475 << buffer[k] << '\n';
478 // BACKSPACE(unit)
479 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
480 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
481 << "EndIoStatement() for Backspace (after read)";
484 // CLOSE(UNIT=unit,STATUS='DELETE')
485 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
486 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
487 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
488 << "EndIoStatement() for Close";
491 TEST(ExternalIOTests, TestNonAvancingInput) {
492 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
493 // FORM='FORMATTED',STATUS='SCRATCH')
494 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
495 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
496 << "SetAccess(SEQUENTIAL)";
497 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
498 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
499 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
501 int unit{-1};
502 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
503 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
504 << "EndIoStatement() for OpenNewUnit";
506 // Write the file to be used for the input test.
507 static constexpr std::string_view records[] = {
508 "ABCDEFGH", "IJKLMNOP", "QRSTUVWX"};
509 static constexpr std::string_view fmt{"(A)"};
510 for (const auto &record : records) {
511 // WRITE(UNIT=unit,FMT=fmt) record
512 io = IONAME(BeginExternalFormattedOutput)(
513 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
514 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length()))
515 << "OutputAscii()";
516 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
517 << "EndIoStatement() for OutputAscii";
520 // REWIND(UNIT=unit)
521 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
522 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
523 << "EndIoStatement() for Rewind";
525 struct TestItems {
526 std::string item;
527 int expectedIoStat;
528 std::string expectedItemValue;
530 // Actual non advancing input IO test
531 TestItems inputItems[]{
532 {std::string(4, '+'), IostatOk, "ABCD"},
533 {std::string(4, '+'), IostatOk, "EFGH"},
534 {std::string(4, '+'), IostatEor, " "},
535 {std::string(2, '+'), IostatOk, "IJ"},
536 {std::string(8, '+'), IostatEor, "KLMNOP "},
537 {std::string(10, '+'), IostatEor, "QRSTUVWX "},
540 int j{0};
541 for (auto &inputItem : inputItems) {
542 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
543 io = IONAME(BeginExternalFormattedInput)(
544 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
545 IONAME(EnableHandlers)(io, true, false, false, false, false);
546 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
547 bool result{
548 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
549 ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
550 << "InputAscii() " << j;
551 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
552 << "EndIoStatement() for Read " << j;
553 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue)
554 << "Input-item value after non advancing read " << j;
555 j++;
557 // CLOSE(UNIT=unit)
558 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
559 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
560 << "EndIoStatement() for Close";
563 TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) {
564 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
565 // FORM='FORMATTED',STATUS='SCRATCH')
566 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
567 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
568 << "SetAccess(SEQUENTIAL)";
569 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
570 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
571 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
573 int unit{-1};
574 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
575 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
576 << "EndIoStatement() for OpenNewUnit";
578 // Write the file to be used for the input test.
579 static constexpr std::string_view records[] = {"ABCDEFGHIJKLMNOPQRST"};
580 static constexpr std::string_view fmt{"(A)"};
581 for (const auto &record : records) {
582 // WRITE(UNIT=unit,FMT=fmt) record
583 io = IONAME(BeginExternalFormattedOutput)(
584 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
585 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length()))
586 << "OutputAscii()";
587 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
588 << "EndIoStatement() for OutputAscii";
591 // REWIND(UNIT=unit)
592 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
593 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
594 << "EndIoStatement() for Rewind";
596 struct TestItems {
597 std::string item;
598 int expectedIoStat;
599 std::string expectedItemValue;
601 // Actual non advancing input IO test
602 TestItems inputItems[]{
603 {std::string(4, '+'), IostatOk, "ABCD"},
604 {std::string(4, '+'), IostatOk, "EFGH"},
607 int j{0};
608 for (auto &inputItem : inputItems) {
609 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
610 io = IONAME(BeginExternalFormattedInput)(
611 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
612 IONAME(EnableHandlers)(io, true, false, false, false, false);
613 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
614 ASSERT_TRUE(
615 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length()))
616 << "InputAscii() " << j;
617 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
618 << "EndIoStatement() for Read " << j;
619 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue)
620 << "Input-item value after non advancing read " << j;
621 j++;
624 // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem.
625 static constexpr std::string_view outputItem{"XYZ"};
626 // WRITE(UNIT=unit,FMT=fmt) record
627 io = IONAME(BeginExternalFormattedOutput)(
628 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
629 ASSERT_TRUE(IONAME(OutputAscii)(io, outputItem.data(), outputItem.length()))
630 << "OutputAscii()";
631 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
632 << "EndIoStatement() for OutputAscii";
634 // Verify that the output was written in the record read in non advancing
635 // mode, after the read part, and that the end was truncated.
637 // REWIND(UNIT=unit)
638 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
639 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
640 << "EndIoStatement() for Rewind";
642 std::string resultRecord(20, '+');
643 std::string expectedRecord{"ABCDEFGHXYZ "};
644 // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result
645 io = IONAME(BeginExternalFormattedInput)(
646 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
647 IONAME(EnableHandlers)(io, true, false, false, false, false);
648 ASSERT_TRUE(
649 IONAME(InputAscii)(io, resultRecord.data(), resultRecord.length()))
650 << "InputAscii() ";
651 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
652 << "EndIoStatement() for Read ";
653 ASSERT_EQ(resultRecord, expectedRecord)
654 << "Record after non advancing read followed by write";
655 // CLOSE(UNIT=unit)
656 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
657 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
658 << "EndIoStatement() for Close";
661 TEST(ExternalIOTests, TestWriteAfterEndfile) {
662 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
663 // FORM='FORMATTED',STATUS='SCRATCH')
664 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
665 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
666 << "SetAccess(SEQUENTIAL)";
667 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
668 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
669 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
670 int unit{-1};
671 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
672 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
673 << "EndIoStatement() for OpenNewUnit";
674 // WRITE(unit,"(I8)") 1234
675 static constexpr std::string_view format{"(I8)"};
676 io = IONAME(BeginExternalFormattedOutput)(
677 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
678 ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()";
679 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
680 << "EndIoStatement for WRITE before ENDFILE";
681 // ENDFILE(unit)
682 io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__);
683 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
684 << "EndIoStatement for ENDFILE";
685 // WRITE(unit,"(I8)",iostat=iostat) 5678
686 io = IONAME(BeginExternalFormattedOutput)(
687 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
688 IONAME(EnableHandlers)(io, true /*IOSTAT=*/);
689 ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()";
690 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile)
691 << "EndIoStatement for WRITE after ENDFILE";
692 // BACKSPACE(unit)
693 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
694 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
695 << "EndIoStatement for BACKSPACE";
696 // WRITE(unit,"(I8)") 3456
697 io = IONAME(BeginExternalFormattedOutput)(
698 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
699 ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()";
700 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
701 << "EndIoStatement for WRITE after BACKSPACE";
702 // REWIND(unit)
703 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
704 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
705 << "EndIoStatement for REWIND";
706 // READ(unit,"(I8)",END=) j, k
707 std::int64_t j{-1}, k{-1}, eof{-1};
708 io = IONAME(BeginExternalFormattedInput)(
709 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
710 IONAME(EnableHandlers)(io, false, false, true /*END=*/);
711 ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)";
712 ASSERT_EQ(j, 1234) << "READ(j)";
713 ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)";
714 ASSERT_EQ(k, 3456) << "READ(k)";
715 ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)";
716 ASSERT_EQ(eof, -1) << "READ(eof)";
717 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ";
718 // CLOSE(UNIT=unit)
719 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
720 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
721 << "EndIoStatement() for Close";
724 TEST(ExternalIOTests, TestUTF8Encoding) {
725 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
726 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
727 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
728 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
729 << "SetAccess(SEQUENTIAL)";
730 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
731 ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)";
732 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
733 ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)";
734 ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)";
735 int unit{-1};
736 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
737 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
738 << "EndIoStatement() for first OPEN";
739 char buffer[12];
740 std::memcpy(buffer,
741 "abc\x80\xff"
742 "de\0\0\0\0\0",
743 12);
744 // WRITE(unit, *) buffer
745 io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__);
746 StaticDescriptor<0> staticDescriptor;
747 Descriptor &desc{staticDescriptor.descriptor()};
748 desc.Establish(TypeCode{CFI_type_char}, 7, buffer, 0);
749 desc.Check();
750 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc));
751 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
752 << "EndIoStatement() for WRITE";
753 // REWIND(unit)
754 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
755 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
756 << "EndIoStatement for REWIND";
757 // READ(unit, *) buffer
758 desc.Establish(TypeCode(CFI_type_char), sizeof buffer, buffer, 0);
759 desc.Check();
760 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
761 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
762 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
763 << "EndIoStatement() for first READ";
764 ASSERT_EQ(std::memcmp(buffer,
765 "abc\x80\xff"
766 "de ",
767 12),
769 // CLOSE(UNIT=unit,STATUS='KEEP')
770 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
771 ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)";
772 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
773 << "EndIoStatement() for first CLOSE";
774 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
775 // FORM='FORMATTED',STATUS='OLD')
776 io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__);
777 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
778 << "SetAccess(SEQUENTIAL)";
779 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
780 ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)";
781 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
782 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
783 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
784 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
785 << "EndIoStatement() for second OPEN";
786 // READ(unit, *) buffer
787 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
788 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
789 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
790 << "EndIoStatement() for second READ";
791 ASSERT_EQ(std::memcmp(buffer,
792 "abc\xc2\x80\xc3\xbf"
793 "de ",
794 12),
796 // CLOSE(UNIT=unit,STATUS='DELETE')
797 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
798 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
799 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
800 << "EndIoStatement() for second CLOSE";
803 TEST(ExternalIOTests, TestUCS) {
804 // OPEN(FILE="ucstest',NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
805 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
806 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
807 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
808 << "SetAccess(SEQUENTIAL)";
809 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
810 ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetAction(ucstest)";
811 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
812 ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)";
813 ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)";
814 int unit{-1};
815 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
816 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
817 << "EndIoStatement() for first OPEN";
818 char32_t wbuffer[8]{U"abc\u0080\uffff"
819 "de"};
820 // WRITE(unit, *) wbuffec
821 io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__);
822 StaticDescriptor<0> staticDescriptor;
823 Descriptor &desc{staticDescriptor.descriptor()};
824 desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer - sizeof(char32_t),
825 wbuffer, 0);
826 desc.Check();
827 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc));
828 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
829 << "EndIoStatement() for WRITE";
830 // REWIND(unit)
831 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
832 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
833 << "EndIoStatement for REWIND";
834 // READ(unit, *) buffer
835 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
836 desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer, wbuffer, 0);
837 desc.Check();
838 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
839 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
840 << "EndIoStatement() for first READ";
841 char dump[80];
842 dump[0] = '\0';
843 for (int j{0}; j < 8; ++j) {
844 std::size_t dumpLen{std::strlen(dump)};
845 std::snprintf(
846 dump + dumpLen, sizeof dump - dumpLen, " %x", (unsigned)wbuffer[j]);
848 EXPECT_EQ(wbuffer[0], U'a') << dump;
849 EXPECT_EQ(wbuffer[1], U'b') << dump;
850 EXPECT_EQ(wbuffer[2], U'c') << dump;
851 EXPECT_EQ(wbuffer[3], U'\u0080') << dump;
852 EXPECT_EQ(wbuffer[4], U'\uffff') << dump;
853 EXPECT_EQ(wbuffer[5], U'd') << dump;
854 EXPECT_EQ(wbuffer[6], U'e') << dump;
855 EXPECT_EQ(wbuffer[7], U' ') << dump;
856 // CLOSE(UNIT=unit,STATUS='KEEP')
857 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
858 ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)";
859 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
860 << "EndIoStatement() for first CLOSE";
861 // OPEN(FILE="ucstest",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
862 // FORM='FORMATTED',STATUS='OLD')
863 io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__);
864 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
865 << "SetAccess(SEQUENTIAL)";
866 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
867 ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetFile(ucstest)";
868 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
869 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
870 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
871 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
872 << "EndIoStatement() for second OPEN";
873 char buffer[12];
874 // READ(unit, *) buffer
875 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
876 desc.Establish(TypeCode{CFI_type_char}, sizeof buffer, buffer, 0);
877 desc.Check();
878 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
879 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
880 << "EndIoStatement() for second READ";
881 dump[0] = '\0';
882 for (int j{0}; j < 12; ++j) {
883 std::size_t dumpLen{std::strlen(dump)};
884 std::snprintf(dump + dumpLen, sizeof dump - dumpLen, " %x",
885 (unsigned)(unsigned char)buffer[j]);
887 EXPECT_EQ(std::memcmp(buffer,
888 "abc\xc2\x80\xef\xbf\xbf"
889 "de ",
890 12),
892 << dump;
893 // CLOSE(UNIT=unit,STATUS='DELETE')
894 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
895 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
896 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
897 << "EndIoStatement() for second CLOSE";
900 TEST(ExternalIOTests, BigUnitNumbers) {
901 if (std::numeric_limits<ExternalUnit>::max() <
902 std::numeric_limits<std::int64_t>::max()) {
903 std::int64_t unit64Ok = std::numeric_limits<ExternalUnit>::max();
904 std::int64_t unit64Bad = unit64Ok + 1;
905 std::int64_t unit64Bad2 =
906 static_cast<std::int64_t>(std::numeric_limits<ExternalUnit>::min()) - 1;
907 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, true), IostatOk);
908 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, false), IostatOk);
909 EXPECT_EQ(
910 IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow);
911 EXPECT_EQ(
912 IONAME(CheckUnitNumberInRange64)(unit64Bad2, true), IostatUnitOverflow);
913 EXPECT_EQ(
914 IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow);
915 EXPECT_EQ(
916 IONAME(CheckUnitNumberInRange64)(unit64Bad2, true), IostatUnitOverflow);
917 constexpr std::size_t n{80};
918 char expectedMsg[n + 1];
919 expectedMsg[n] = '\0';
920 std::snprintf(expectedMsg, n, "UNIT number %jd is out of range",
921 static_cast<std::intmax_t>(unit64Bad));
922 EXPECT_DEATH(
923 IONAME(CheckUnitNumberInRange64)(2147483648, false), expectedMsg);
924 for (auto i{std::strlen(expectedMsg)}; i < n; ++i) {
925 expectedMsg[i] = ' ';
927 char msg[n + 1];
928 msg[n] = '\0';
929 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Bad, true, msg, n),
930 IostatUnitOverflow);
931 EXPECT_EQ(std::strncmp(msg, expectedMsg, n), 0);