1 //===-- flang/unittests/RuntimeGTest/ExternalIOTest.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 // 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"
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)";
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)";
42 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
43 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
44 << "EndIoStatement() for OpenNewUnit";
46 StaticDescriptor
<0> staticDescriptor
;
47 Descriptor
&desc
{staticDescriptor
.descriptor()};
48 desc
.Establish(TypeCode
{CFI_type_int8_t
}, recl
, &buffer
, 0);
51 // INQUIRE(IOLENGTH=) j
52 io
= IONAME(BeginInquireIoLength
)(__FILE__
, __LINE__
);
53 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
))
54 << "OutputDescriptor() for InquireIoLength";
55 ASSERT_EQ(IONAME(GetIoLength
)(io
), recl
) << "GetIoLength";
56 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
57 << "EndIoStatement() for InquireIoLength";
59 static constexpr int records
{10};
60 for (int j
{1}; j
<= records
; ++j
) {
61 // WRITE(UNIT=unit,REC=j) j
62 io
= IONAME(BeginUnformattedOutput
)(unit
, __FILE__
, __LINE__
);
63 ASSERT_TRUE(IONAME(SetRec
)(io
, j
)) << "SetRec(" << j
<< ')';
66 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
))
67 << "OutputDescriptor() for Write";
69 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
70 << "EndIoStatement() for Write";
73 for (int j
{records
}; j
>= 1; --j
) {
75 // READ(UNIT=unit,REC=j) n
76 io
= IONAME(BeginUnformattedInput
)(unit
, __FILE__
, __LINE__
);
77 ASSERT_TRUE(IONAME(SetRec
)(io
, j
)) << "SetRec(" << j
<< ')';
78 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
))
79 << "InputDescriptor() for Read";
81 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
82 << "EndIoStatement() for Read";
84 ASSERT_EQ(buffer
, j
) << "Read back " << buffer
85 << " from direct unformatted record " << j
86 << ", expected " << j
<< '\n';
88 // CLOSE(UNIT=unit,STATUS='DELETE')
89 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
90 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
91 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
92 << "EndIoStatement() for Close";
95 TEST(ExternalIOTests
, TestDirectUnformattedSwapped
) {
96 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
97 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE')
98 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
99 ASSERT_TRUE(IONAME(SetAccess
)(io
, "DIRECT", 6)) << "SetAccess(DIRECT)";
100 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
101 ASSERT_TRUE(IONAME(SetForm
)(io
, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
102 ASSERT_TRUE(IONAME(SetConvert
)(io
, "NATIVE", 6)) << "SetConvert(NATIVE)";
105 static constexpr std::size_t recl
{sizeof buffer
};
106 ASSERT_TRUE(IONAME(SetRecl
)(io
, recl
)) << "SetRecl()";
107 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
110 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
111 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
112 << "EndIoStatement() for OpenNewUnit";
114 StaticDescriptor
<0> staticDescriptor
;
115 Descriptor
&desc
{staticDescriptor
.descriptor()};
116 desc
.Establish(TypeCode
{CFI_type_int64_t
}, recl
, &buffer
, 0);
119 static constexpr int records
{10};
120 for (int j
{1}; j
<= records
; ++j
) {
121 // WRITE(UNIT=unit,REC=j) j
122 io
= IONAME(BeginUnformattedOutput
)(unit
, __FILE__
, __LINE__
);
123 ASSERT_TRUE(IONAME(SetRec
)(io
, j
)) << "SetRec(" << j
<< ')';
125 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
))
126 << "OutputDescriptor() for Write";
127 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
128 << "EndIoStatement() for Write";
131 // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP')
132 io
= IONAME(BeginOpenUnit
)(unit
, __FILE__
, __LINE__
);
133 ASSERT_TRUE(IONAME(SetStatus
)(io
, "OLD", 3)) << "SetStatus(OLD)";
134 ASSERT_TRUE(IONAME(SetConvert
)(io
, "SWAP", 4)) << "SetConvert(SWAP)";
135 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
136 << "EndIoStatement() for OpenUnit";
138 for (int j
{records
}; j
>= 1; --j
) {
139 // READ(UNIT=unit,REC=j) n
140 io
= IONAME(BeginUnformattedInput
)(unit
, __FILE__
, __LINE__
);
141 ASSERT_TRUE(IONAME(SetRec
)(io
, j
)) << "SetRec(" << j
<< ')';
142 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
))
143 << "InputDescriptor() for Read";
144 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
145 << "EndIoStatement() for Read";
146 ASSERT_EQ(buffer
>> 56, j
)
147 << "Read back " << (buffer
>> 56) << " from direct unformatted record "
148 << j
<< ", expected " << j
<< '\n';
151 // CLOSE(UNIT=unit,STATUS='DELETE')
152 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
153 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
154 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
155 << "EndIoStatement() for Close";
158 TEST(ExternalIOTests
, TestSequentialFixedUnformatted
) {
159 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
160 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
161 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
162 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
163 << "SetAccess(SEQUENTIAL)";
164 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
165 ASSERT_TRUE(IONAME(SetForm
)(io
, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
168 static constexpr std::size_t recl
{sizeof buffer
};
170 ASSERT_TRUE(IONAME(SetRecl
)(io
, recl
)) << "SetRecl()";
171 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
174 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
175 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
176 << "EndIoStatement() for OpenNewUnit";
178 // INQUIRE(IOLENGTH=) j, ...
179 StaticDescriptor
<0> staticDescriptor
;
180 Descriptor
&desc
{staticDescriptor
.descriptor()};
181 desc
.Establish(TypeCode
{CFI_type_int64_t
}, recl
, &buffer
, 0);
184 io
= IONAME(BeginInquireIoLength
)(__FILE__
, __LINE__
);
185 for (int j
{1}; j
<= 3; ++j
) {
186 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
))
187 << "OutputDescriptor() for InquireIoLength";
189 ASSERT_EQ(IONAME(GetIoLength
)(io
), 3 * recl
) << "GetIoLength";
190 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
191 << "EndIoStatement() for InquireIoLength";
193 static const int records
{10};
194 for (int j
{1}; j
<= records
; ++j
) {
195 // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO
196 io
= IONAME(BeginUnformattedOutput
)(unit
, __FILE__
, __LINE__
);
198 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
))
199 << "OutputDescriptor() for Write";
200 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
201 << "EndIoStatement() for WRITE";
205 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
206 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
207 << "EndIoStatement() for Rewind";
209 for (int j
{1}; j
<= records
; ++j
) {
210 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
211 io
= IONAME(BeginUnformattedInput
)(unit
, __FILE__
, __LINE__
);
212 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
))
213 << "InputDescriptor() for Read";
214 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
215 << "EndIoStatement() for Read";
216 ASSERT_EQ(buffer
, j
) << "Read back " << buffer
217 << " from sequential fixed unformatted record " << j
218 << ", expected " << j
<< '\n';
221 for (int j
{records
}; j
>= 1; --j
) {
222 // BACKSPACE(UNIT=unit)
223 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
224 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
225 << "EndIoStatement() for Backspace (before read)";
227 io
= IONAME(BeginUnformattedInput
)(unit
, __FILE__
, __LINE__
);
228 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
))
229 << "InputDescriptor() for Read";
230 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
231 << "EndIoStatement() for Read";
232 ASSERT_EQ(buffer
, j
) << "Read back " << buffer
233 << " from sequential fixed unformatted record " << j
234 << " after backspacing, expected " << j
<< '\n';
235 // BACKSPACE(UNIT=unit)
236 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
237 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
238 << "EndIoStatement() for Backspace (after read)";
241 // CLOSE(UNIT=unit,STATUS='DELETE')
242 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
243 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
244 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
245 << "EndIoStatement() for Close";
248 TEST(ExternalIOTests
, TestSequentialVariableUnformatted
) {
249 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
250 // FORM='UNFORMATTED',STATUS='SCRATCH')
251 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
253 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
254 << "SetAccess(SEQUENTIAL)";
255 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
256 ASSERT_TRUE(IONAME(SetForm
)(io
, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
257 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
260 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
261 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
262 << "EndIoStatement() for OpenNewUnit";
264 static const int records
{10};
265 std::int64_t buffer
[records
]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
266 for (int j
{0}; j
< records
; ++j
) {
270 StaticDescriptor
<0> staticDescriptor
;
271 Descriptor
&desc
{staticDescriptor
.descriptor()};
273 for (int j
{1}; j
<= records
; ++j
) {
274 // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO
275 io
= IONAME(BeginUnformattedOutput
)(unit
, __FILE__
, __LINE__
);
276 desc
.Establish(TypeCode
{sizeof *buffer
}, j
* sizeof *buffer
, buffer
, 0);
278 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
))
279 << "OutputDescriptor() for Write";
280 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
281 << "EndIoStatement() for Write";
285 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
286 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
287 << "EndIoStatement() for Rewind";
288 for (int j
{1}; j
<= records
; ++j
) {
289 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
290 io
= IONAME(BeginUnformattedInput
)(unit
, __FILE__
, __LINE__
);
291 desc
.Establish(TypeCode
{sizeof *buffer
}, j
* sizeof *buffer
, buffer
, 0);
293 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
))
294 << "InputDescriptor() for Read";
295 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
296 << "EndIoStatement() for Read";
297 for (int k
{0}; k
< j
; ++k
) {
298 ASSERT_EQ(buffer
[k
], k
) << "Read back [" << k
<< "]=" << buffer
[k
]
299 << " from direct unformatted record " << j
300 << ", expected " << k
<< '\n';
304 for (int j
{records
}; j
>= 1; --j
) {
306 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
307 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
308 << "EndIoStatement() for Backspace (before read)";
309 // READ(unit=unit) n; check
310 io
= IONAME(BeginUnformattedInput
)(unit
, __FILE__
, __LINE__
);
311 desc
.Establish(TypeCode
{sizeof *buffer
}, j
* sizeof *buffer
, buffer
, 0);
313 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
)) << "InputDescriptor()";
314 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
315 << "EndIoStatement() for InputUnformattedBlock";
316 for (int k
{0}; k
< j
; ++k
) {
317 ASSERT_EQ(buffer
[k
], k
) << "Read back [" << k
<< "]=" << buffer
[k
]
318 << " from sequential variable unformatted record "
319 << j
<< ", expected " << k
<< '\n';
322 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
323 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
324 << "EndIoStatement() for Backspace (after read)";
327 // CLOSE(UNIT=unit,STATUS='DELETE')
328 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
329 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
330 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
331 << "EndIoStatement() for Close";
334 TEST(ExternalIOTests
, TestDirectFormatted
) {
335 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
336 // FORM='FORMATTED',RECL=8,STATUS='SCRATCH')
337 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
338 ASSERT_TRUE(IONAME(SetAccess
)(io
, "DIRECT", 6)) << "SetAccess(DIRECT)";
339 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
340 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
342 static constexpr std::size_t recl
{8};
343 ASSERT_TRUE(IONAME(SetRecl
)(io
, recl
)) << "SetRecl()";
344 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
347 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
348 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
349 << "EndIoStatement() for OpenNewUnit";
351 static constexpr int records
{10};
352 static const char fmt
[]{"(I4)"};
353 for (int j
{1}; j
<= records
; ++j
) {
354 // WRITE(UNIT=unit,FMT=fmt,REC=j) j
355 io
= IONAME(BeginExternalFormattedOutput
)(
356 fmt
, sizeof fmt
- 1, nullptr, unit
, __FILE__
, __LINE__
);
357 ASSERT_TRUE(IONAME(SetRec
)(io
, j
)) << "SetRec(" << j
<< ')';
358 ASSERT_TRUE(IONAME(OutputInteger64
)(io
, j
)) << "OutputInteger64()";
359 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
360 << "EndIoStatement() for OutputInteger64";
363 for (int j
{records
}; j
>= 1; --j
) {
364 // READ(UNIT=unit,FMT=fmt,REC=j) n
365 io
= IONAME(BeginExternalFormattedInput
)(
366 fmt
, sizeof fmt
- 1, nullptr, unit
, __FILE__
, __LINE__
);
367 ASSERT_TRUE(IONAME(SetRec
)(io
, j
)) << "SetRec(" << j
<< ')';
369 ASSERT_TRUE(IONAME(InputInteger
)(io
, buffer
)) << "InputInteger()";
370 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
371 << "EndIoStatement() for InputInteger";
373 ASSERT_EQ(buffer
, j
) << "Read back " << buffer
374 << " from direct formatted record " << j
375 << ", expected " << j
<< '\n';
378 // CLOSE(UNIT=unit,STATUS='DELETE')
379 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
380 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
381 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
382 << "EndIoStatement() for Close";
385 TEST(ExternalIOTests
, TestSequentialVariableFormatted
) {
386 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
387 // FORM='FORMATTED',STATUS='SCRATCH')
388 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
389 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
390 << "SetAccess(SEQUENTIAL)";
391 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
392 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
393 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
396 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
397 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
398 << "EndIoStatement() for OpenNewUnit";
400 static const int records
{10};
401 std::int64_t buffer
[records
]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
402 for (int j
{0}; j
< records
; ++j
) {
407 for (int j
{1}; j
<= records
; ++j
) {
408 std::snprintf(fmt
, sizeof fmt
, "(%dI4)", j
);
409 // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO
410 io
= IONAME(BeginExternalFormattedOutput
)(
411 fmt
, std::strlen(fmt
), nullptr, unit
, __FILE__
, __LINE__
);
412 for (int k
{0}; k
< j
; ++k
) {
413 ASSERT_TRUE(IONAME(OutputInteger64
)(io
, buffer
[k
]))
414 << "OutputInteger64()";
416 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
417 << "EndIoStatement() for OutputInteger64";
421 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
422 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
423 << "EndIoStatement() for Rewind";
425 for (int j
{1}; j
<= records
; ++j
) {
426 std::snprintf(fmt
, sizeof fmt
, "(%dI4)", j
);
427 // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO
428 io
= IONAME(BeginExternalFormattedInput
)(
429 fmt
, std::strlen(fmt
), nullptr, unit
, __FILE__
, __LINE__
);
431 std::int64_t check
[records
];
432 for (int k
{0}; k
< j
; ++k
) {
433 ASSERT_TRUE(IONAME(InputInteger
)(io
, check
[k
])) << "InputInteger()";
435 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
436 << "EndIoStatement() for InputInteger";
438 for (int k
{0}; k
< j
; ++k
) {
439 ASSERT_EQ(buffer
[k
], check
[k
])
440 << "Read back [" << k
<< "]=" << check
[k
]
441 << " from sequential variable formatted record " << j
<< ", expected "
442 << buffer
[k
] << '\n';
446 for (int j
{records
}; j
>= 1; --j
) {
448 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
449 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
450 << "EndIoStatement() for Backspace (before read)";
452 std::snprintf(fmt
, sizeof fmt
, "(%dI4)", j
);
453 // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check
454 io
= IONAME(BeginExternalFormattedInput
)(
455 fmt
, std::strlen(fmt
), nullptr, unit
, __FILE__
, __LINE__
);
457 std::int64_t check
[records
];
458 for (int k
{0}; k
< j
; ++k
) {
459 ASSERT_TRUE(IONAME(InputInteger
)(io
, check
[k
])) << "InputInteger()";
462 std::size_t chars
{IONAME(GetSize
)(io
)};
463 ASSERT_EQ(chars
, j
* 4u)
464 << "GetSize()=" << chars
<< ", expected " << (j
* 4u) << '\n';
465 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
466 << "EndIoStatement() for InputInteger";
467 for (int k
{0}; k
< j
; ++k
) {
468 ASSERT_EQ(buffer
[k
], check
[k
])
469 << "Read back [" << k
<< "]=" << buffer
[k
]
470 << " from sequential variable formatted record " << j
<< ", expected "
471 << buffer
[k
] << '\n';
475 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
476 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
477 << "EndIoStatement() for Backspace (after read)";
480 // CLOSE(UNIT=unit,STATUS='DELETE')
481 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
482 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
483 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
484 << "EndIoStatement() for Close";
487 TEST(ExternalIOTests
, TestNonAvancingInput
) {
488 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
489 // FORM='FORMATTED',STATUS='SCRATCH')
490 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
491 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
492 << "SetAccess(SEQUENTIAL)";
493 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
494 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
495 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
498 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
499 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
500 << "EndIoStatement() for OpenNewUnit";
502 // Write the file to be used for the input test.
503 static constexpr std::string_view records
[] = {
504 "ABCDEFGH", "IJKLMNOP", "QRSTUVWX"};
505 static constexpr std::string_view fmt
{"(A)"};
506 for (const auto &record
: records
) {
507 // WRITE(UNIT=unit,FMT=fmt) record
508 io
= IONAME(BeginExternalFormattedOutput
)(
509 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
510 ASSERT_TRUE(IONAME(OutputAscii
)(io
, record
.data(), record
.length()))
512 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
513 << "EndIoStatement() for OutputAscii";
517 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
518 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
519 << "EndIoStatement() for Rewind";
524 std::string expectedItemValue
[2];
526 // Actual non advancing input IO test
527 TestItems inputItems
[]{
528 {std::string(4, '+'), IostatOk
, {"ABCD", "ABCD"}},
529 {std::string(4, '+'), IostatOk
, {"EFGH", "EFGH"}},
530 {std::string(4, '+'), IostatEor
, {"++++", " "}},
531 {std::string(2, '+'), IostatOk
, {"IJ", "IJ"}},
532 {std::string(8, '+'), IostatEor
, {"++++++++", "KLMNOP "}},
533 {std::string(10, '+'), IostatEor
, {"++++++++++", "QRSTUVWX "}},
536 // Test with PAD='NO'
538 for (auto &inputItem
: inputItems
) {
539 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='NO', IOSTAT=iostat) inputItem
540 io
= IONAME(BeginExternalFormattedInput
)(
541 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
542 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
543 ASSERT_TRUE(IONAME(SetAdvance
)(io
, "NO", 2)) << "SetAdvance(NO)" << j
;
544 ASSERT_TRUE(IONAME(SetPad
)(io
, "NO", 2)) << "SetPad(NO)" << j
;
546 IONAME(InputAscii
)(io
, inputItem
.item
.data(), inputItem
.item
.length())};
547 ASSERT_EQ(result
, inputItem
.expectedIoStat
== IostatOk
)
548 << "InputAscii() " << j
;
549 ASSERT_EQ(IONAME(EndIoStatement
)(io
), inputItem
.expectedIoStat
)
550 << "EndIoStatement() for Read " << j
;
551 ASSERT_EQ(inputItem
.item
, inputItem
.expectedItemValue
[0])
552 << "Input-item value after non advancing read " << j
;
557 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
558 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
559 << "EndIoStatement() for Rewind";
561 // Test again with PAD='YES'
563 for (auto &inputItem
: inputItems
) {
564 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat)
566 io
= IONAME(BeginExternalFormattedInput
)(
567 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
568 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
569 ASSERT_TRUE(IONAME(SetAdvance
)(io
, "NO", 2)) << "SetAdvance(NO)" << j
;
570 ASSERT_TRUE(IONAME(SetPad
)(io
, "YES", 3)) << "SetPad(YES)" << j
;
572 IONAME(InputAscii
)(io
, inputItem
.item
.data(), inputItem
.item
.length())};
573 ASSERT_EQ(result
, inputItem
.expectedIoStat
== IostatOk
)
574 << "InputAscii() " << j
;
575 ASSERT_EQ(IONAME(EndIoStatement
)(io
), inputItem
.expectedIoStat
)
576 << "EndIoStatement() for Read " << j
;
577 ASSERT_EQ(inputItem
.item
, inputItem
.expectedItemValue
[1])
578 << "Input-item value after non advancing read " << j
;
583 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
584 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
585 << "EndIoStatement() for Close";
588 TEST(ExternalIOTests
, TestWriteAfterNonAvancingInput
) {
589 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
590 // FORM='FORMATTED',STATUS='SCRATCH')
591 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
592 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
593 << "SetAccess(SEQUENTIAL)";
594 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
595 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
596 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
599 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
600 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
601 << "EndIoStatement() for OpenNewUnit";
603 // Write the file to be used for the input test.
604 static constexpr std::string_view records
[] = {"ABCDEFGHIJKLMNOPQRST"};
605 static constexpr std::string_view fmt
{"(A)"};
606 for (const auto &record
: records
) {
607 // WRITE(UNIT=unit,FMT=fmt) record
608 io
= IONAME(BeginExternalFormattedOutput
)(
609 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
610 ASSERT_TRUE(IONAME(OutputAscii
)(io
, record
.data(), record
.length()))
612 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
613 << "EndIoStatement() for OutputAscii";
617 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
618 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
619 << "EndIoStatement() for Rewind";
624 std::string expectedItemValue
;
626 // Actual non advancing input IO test
627 TestItems inputItems
[]{
628 {std::string(4, '+'), IostatOk
, "ABCD"},
629 {std::string(4, '+'), IostatOk
, "EFGH"},
633 for (auto &inputItem
: inputItems
) {
634 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
635 io
= IONAME(BeginExternalFormattedInput
)(
636 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
637 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
638 ASSERT_TRUE(IONAME(SetAdvance
)(io
, "NO", 2)) << "SetAdvance(NO)" << j
;
640 IONAME(InputAscii
)(io
, inputItem
.item
.data(), inputItem
.item
.length()))
641 << "InputAscii() " << j
;
642 ASSERT_EQ(IONAME(EndIoStatement
)(io
), inputItem
.expectedIoStat
)
643 << "EndIoStatement() for Read " << j
;
644 ASSERT_EQ(inputItem
.item
, inputItem
.expectedItemValue
)
645 << "Input-item value after non advancing read " << j
;
649 // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem.
650 static constexpr std::string_view outputItem
{"XYZ"};
651 // WRITE(UNIT=unit,FMT=fmt) record
652 io
= IONAME(BeginExternalFormattedOutput
)(
653 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
654 ASSERT_TRUE(IONAME(OutputAscii
)(io
, outputItem
.data(), outputItem
.length()))
656 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
657 << "EndIoStatement() for OutputAscii";
659 // Verify that the output was written in the record read in non advancing
660 // mode, after the read part, and that the end was truncated.
663 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
664 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
665 << "EndIoStatement() for Rewind";
667 std::string
resultRecord(20, '+');
668 std::string expectedRecord
{"ABCDEFGHXYZ "};
669 // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result
670 io
= IONAME(BeginExternalFormattedInput
)(
671 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
672 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
674 IONAME(InputAscii
)(io
, resultRecord
.data(), resultRecord
.length()))
676 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
677 << "EndIoStatement() for Read ";
678 ASSERT_EQ(resultRecord
, expectedRecord
)
679 << "Record after non advancing read followed by write";
681 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
682 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
683 << "EndIoStatement() for Close";
686 TEST(ExternalIOTests
, TestWriteAfterEndfile
) {
687 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
688 // FORM='FORMATTED',STATUS='SCRATCH')
689 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
690 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
691 << "SetAccess(SEQUENTIAL)";
692 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
693 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
694 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
696 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
697 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
698 << "EndIoStatement() for OpenNewUnit";
699 // WRITE(unit,"(I8)") 1234
700 static constexpr std::string_view format
{"(I8)"};
701 io
= IONAME(BeginExternalFormattedOutput
)(
702 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
703 ASSERT_TRUE(IONAME(OutputInteger64
)(io
, 1234)) << "OutputInteger64()";
704 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
705 << "EndIoStatement for WRITE before ENDFILE";
707 io
= IONAME(BeginEndfile
)(unit
, __FILE__
, __LINE__
);
708 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
709 << "EndIoStatement for ENDFILE";
710 // WRITE(unit,"(I8)",iostat=iostat) 5678
711 io
= IONAME(BeginExternalFormattedOutput
)(
712 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
713 IONAME(EnableHandlers
)(io
, true /*IOSTAT=*/);
714 ASSERT_FALSE(IONAME(OutputInteger64
)(io
, 5678)) << "OutputInteger64()";
715 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatWriteAfterEndfile
)
716 << "EndIoStatement for WRITE after ENDFILE";
718 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
719 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
720 << "EndIoStatement for BACKSPACE";
721 // WRITE(unit,"(I8)") 3456
722 io
= IONAME(BeginExternalFormattedOutput
)(
723 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
724 ASSERT_TRUE(IONAME(OutputInteger64
)(io
, 3456)) << "OutputInteger64()";
725 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
726 << "EndIoStatement for WRITE after BACKSPACE";
728 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
729 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
730 << "EndIoStatement for REWIND";
731 // READ(unit,"(I8)",END=) j, k
732 std::int64_t j
{-1}, k
{-1}, eof
{-1};
733 io
= IONAME(BeginExternalFormattedInput
)(
734 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
735 IONAME(EnableHandlers
)(io
, false, false, true /*END=*/);
736 ASSERT_TRUE(IONAME(InputInteger
)(io
, j
)) << "InputInteger(j)";
737 ASSERT_EQ(j
, 1234) << "READ(j)";
738 ASSERT_TRUE(IONAME(InputInteger
)(io
, k
)) << "InputInteger(k)";
739 ASSERT_EQ(k
, 3456) << "READ(k)";
740 ASSERT_FALSE(IONAME(InputInteger
)(io
, eof
)) << "InputInteger(eof)";
741 ASSERT_EQ(eof
, -1) << "READ(eof)";
742 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatEnd
) << "EndIoStatement for READ";
744 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
745 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
746 << "EndIoStatement() for Close";
749 TEST(ExternalIOTests
, TestUTF8Encoding
) {
750 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
751 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
752 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
753 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
754 << "SetAccess(SEQUENTIAL)";
755 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
756 ASSERT_TRUE(IONAME(SetFile
)(io
, "utf8test", 8)) << "SetFile(utf8test)";
757 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
758 ASSERT_TRUE(IONAME(SetStatus
)(io
, "REPLACE", 7)) << "SetStatus(REPLACE)";
759 ASSERT_TRUE(IONAME(SetEncoding
)(io
, "UTF-8", 5)) << "SetEncoding(UTF-8)";
761 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
762 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
763 << "EndIoStatement() for first OPEN";
769 // WRITE(unit, *) buffer
770 io
= IONAME(BeginExternalListOutput
)(unit
, __FILE__
, __LINE__
);
771 StaticDescriptor
<0> staticDescriptor
;
772 Descriptor
&desc
{staticDescriptor
.descriptor()};
773 desc
.Establish(TypeCode
{CFI_type_char
}, 7, buffer
, 0);
775 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
));
776 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
777 << "EndIoStatement() for WRITE";
779 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
780 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
781 << "EndIoStatement for REWIND";
782 // READ(unit, *) buffer
783 desc
.Establish(TypeCode(CFI_type_char
), sizeof buffer
, buffer
, 0);
785 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
786 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
787 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
788 << "EndIoStatement() for first READ";
789 ASSERT_EQ(std::memcmp(buffer
,
794 // CLOSE(UNIT=unit,STATUS='KEEP')
795 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
796 ASSERT_TRUE(IONAME(SetStatus
)(io
, "KEEP", 4)) << "SetStatus(KEEP)";
797 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
798 << "EndIoStatement() for first CLOSE";
799 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
800 // FORM='FORMATTED',STATUS='OLD')
801 io
= IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
);
802 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
803 << "SetAccess(SEQUENTIAL)";
804 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
805 ASSERT_TRUE(IONAME(SetFile
)(io
, "utf8test", 8)) << "SetFile(utf8test)";
806 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
807 ASSERT_TRUE(IONAME(SetStatus
)(io
, "OLD", 3)) << "SetStatus(OLD)";
808 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
809 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
810 << "EndIoStatement() for second OPEN";
811 // READ(unit, *) buffer
812 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
813 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
814 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
815 << "EndIoStatement() for second READ";
816 ASSERT_EQ(std::memcmp(buffer
,
817 "abc\xc2\x80\xc3\xbf"
821 // CLOSE(UNIT=unit,STATUS='DELETE')
822 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
823 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
824 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
825 << "EndIoStatement() for second CLOSE";
828 TEST(ExternalIOTests
, TestUCS
) {
829 // OPEN(FILE="ucstest',NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
830 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
831 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
832 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
833 << "SetAccess(SEQUENTIAL)";
834 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
835 ASSERT_TRUE(IONAME(SetFile
)(io
, "ucstest", 7)) << "SetAction(ucstest)";
836 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
837 ASSERT_TRUE(IONAME(SetStatus
)(io
, "REPLACE", 7)) << "SetStatus(REPLACE)";
838 ASSERT_TRUE(IONAME(SetEncoding
)(io
, "UTF-8", 5)) << "SetEncoding(UTF-8)";
840 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
841 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
842 << "EndIoStatement() for first OPEN";
843 char32_t wbuffer
[8]{U
"abc\u0080\uffff"
845 // WRITE(unit, *) wbuffec
846 io
= IONAME(BeginExternalListOutput
)(unit
, __FILE__
, __LINE__
);
847 StaticDescriptor
<0> staticDescriptor
;
848 Descriptor
&desc
{staticDescriptor
.descriptor()};
849 desc
.Establish(TypeCode
{CFI_type_char32_t
}, sizeof wbuffer
- sizeof(char32_t
),
852 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
));
853 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
854 << "EndIoStatement() for WRITE";
856 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
857 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
858 << "EndIoStatement for REWIND";
859 // READ(unit, *) buffer
860 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
861 desc
.Establish(TypeCode
{CFI_type_char32_t
}, sizeof wbuffer
, wbuffer
, 0);
863 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
864 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
865 << "EndIoStatement() for first READ";
868 for (int j
{0}; j
< 8; ++j
) {
869 std::size_t dumpLen
{std::strlen(dump
)};
871 dump
+ dumpLen
, sizeof dump
- dumpLen
, " %x", (unsigned)wbuffer
[j
]);
873 EXPECT_EQ(wbuffer
[0], U
'a') << dump
;
874 EXPECT_EQ(wbuffer
[1], U
'b') << dump
;
875 EXPECT_EQ(wbuffer
[2], U
'c') << dump
;
876 EXPECT_EQ(wbuffer
[3], U
'\u0080') << dump
;
877 EXPECT_EQ(wbuffer
[4], U
'\uffff') << dump
;
878 EXPECT_EQ(wbuffer
[5], U
'd') << dump
;
879 EXPECT_EQ(wbuffer
[6], U
'e') << dump
;
880 EXPECT_EQ(wbuffer
[7], U
' ') << dump
;
881 // CLOSE(UNIT=unit,STATUS='KEEP')
882 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
883 ASSERT_TRUE(IONAME(SetStatus
)(io
, "KEEP", 4)) << "SetStatus(KEEP)";
884 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
885 << "EndIoStatement() for first CLOSE";
886 // OPEN(FILE="ucstest",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
887 // FORM='FORMATTED',STATUS='OLD')
888 io
= IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
);
889 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
890 << "SetAccess(SEQUENTIAL)";
891 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
892 ASSERT_TRUE(IONAME(SetFile
)(io
, "ucstest", 7)) << "SetFile(ucstest)";
893 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
894 ASSERT_TRUE(IONAME(SetStatus
)(io
, "OLD", 3)) << "SetStatus(OLD)";
895 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
896 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
897 << "EndIoStatement() for second OPEN";
899 // READ(unit, *) buffer
900 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
901 desc
.Establish(TypeCode
{CFI_type_char
}, sizeof buffer
, buffer
, 0);
903 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
904 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
905 << "EndIoStatement() for second READ";
907 for (int j
{0}; j
< 12; ++j
) {
908 std::size_t dumpLen
{std::strlen(dump
)};
909 std::snprintf(dump
+ dumpLen
, sizeof dump
- dumpLen
, " %x",
910 (unsigned)(unsigned char)buffer
[j
]);
912 EXPECT_EQ(std::memcmp(buffer
,
913 "abc\xc2\x80\xef\xbf\xbf"
918 // CLOSE(UNIT=unit,STATUS='DELETE')
919 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
920 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
921 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
922 << "EndIoStatement() for second CLOSE";
925 TEST(ExternalIOTests
, BigUnitNumbers
) {
926 if (std::numeric_limits
<ExternalUnit
>::max() <
927 std::numeric_limits
<std::int64_t>::max()) {
928 std::int64_t unit64Ok
= std::numeric_limits
<ExternalUnit
>::max();
929 std::int64_t unit64Bad
= unit64Ok
+ 1;
930 std::int64_t unit64Bad2
=
931 static_cast<std::int64_t>(std::numeric_limits
<ExternalUnit
>::min()) - 1;
932 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Ok
, true), IostatOk
);
933 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Ok
, false), IostatOk
);
935 IONAME(CheckUnitNumberInRange64
)(unit64Bad
, true), IostatUnitOverflow
);
937 IONAME(CheckUnitNumberInRange64
)(unit64Bad2
, true), IostatUnitOverflow
);
938 constexpr std::size_t n
{80};
939 char expectedMsg
[n
+ 1];
940 expectedMsg
[n
] = '\0';
941 std::snprintf(expectedMsg
, n
, "UNIT number %jd is out of range",
942 static_cast<std::intmax_t>(unit64Bad
));
944 IONAME(CheckUnitNumberInRange64
)(unit64Bad
, false), expectedMsg
);
945 for (auto i
{std::strlen(expectedMsg
)}; i
< n
; ++i
) {
946 expectedMsg
[i
] = ' ';
950 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Bad
, true, msg
, n
),
952 EXPECT_EQ(std::strncmp(msg
, expectedMsg
, n
), 0);