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
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "CrashHandlerFixture.h"
14 #include "gtest/gtest.h"
15 #include "../../runtime/io-api.h"
16 #include "../../runtime/main.h"
17 #include "../../runtime/stop.h"
18 #include "llvm/Support/raw_ostream.h"
19 #include <cstring>
20 
21 using namespace Fortran::runtime::io;
22 
23 struct ExternalIOTests : public CrashHandlerFixture {};
24 
25 TEST(ExternalIOTests, TestDirectUnformatted) {
26   // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
27   //   FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
28   Cookie io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
29   ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
30   ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
31   ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
32 
33   std::int64_t buffer;
34   static constexpr std::size_t recl{sizeof buffer};
35   ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
36   ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
37 
38   int unit{-1};
39   ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
40   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
41       << "EndIoStatement() for OpenNewUnit";
42 
43   static constexpr int records{10};
44   for (int j{1}; j <= records; ++j) {
45     // WRITE(UNIT=unit,REC=j) j
46     io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
47     ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
48 
49     buffer = j;
50     ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
51         io, reinterpret_cast<const char *>(&buffer), recl, recl))
52         << "OutputUnformattedBlock()";
53 
54     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
55         << "EndIoStatement() for OutputUnformattedBlock";
56   }
57 
58   for (int j{records}; j >= 1; --j) {
59     // READ(UNIT=unit,REC=j) n
60     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
61     ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
62     ASSERT_TRUE(IONAME(InputUnformattedBlock)(
63         io, reinterpret_cast<char *>(&buffer), recl, recl))
64         << "InputUnformattedBlock()";
65 
66     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
67         << "EndIoStatement() for InputUnformattedBlock";
68 
69     ASSERT_EQ(buffer, j) << "Read back " << buffer
70                          << " from direct unformatted record " << j
71                          << ", expected " << j << '\n';
72   }
73   // CLOSE(UNIT=unit,STATUS='DELETE')
74   io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
75   ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
76   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
77       << "EndIoStatement() for Close";
78 }
79 
80 TEST(ExternalIOTests, TestDirectUnformattedSwapped) {
81   // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
82   //   FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE')
83   auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
84   ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
85   ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
86   ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
87   ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)";
88 
89   std::int64_t buffer;
90   static constexpr std::size_t recl{sizeof buffer};
91   ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
92   ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
93 
94   int unit{-1};
95   ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
96   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
97       << "EndIoStatement() for OpenNewUnit";
98 
99   static constexpr int records{10};
100   for (int j{1}; j <= records; ++j) {
101     // WRITE(UNIT=unit,REC=j) j
102     io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
103     ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
104     buffer = j;
105     ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
106         io, reinterpret_cast<const char *>(&buffer), recl, recl))
107         << "OutputUnformattedBlock()";
108     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
109         << "EndIoStatement() for OutputUnformattedBlock";
110   }
111 
112   // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP')
113   io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__);
114   ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
115   ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)";
116   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
117       << "EndIoStatement() for OpenUnit";
118 
119   for (int j{records}; j >= 1; --j) {
120     // READ(UNIT=unit,REC=j) n
121     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
122     ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
123     ASSERT_TRUE(IONAME(InputUnformattedBlock)(
124         io, reinterpret_cast<char *>(&buffer), recl, recl))
125         << "InputUnformattedBlock()";
126     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
127         << "EndIoStatement() for InputUnformattedBlock";
128     ASSERT_EQ(buffer >> 56, j)
129         << "Read back " << (buffer >> 56) << " from direct unformatted record "
130         << j << ", expected " << j << '\n';
131   }
132 
133   // CLOSE(UNIT=unit,STATUS='DELETE')
134   io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
135   ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
136   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
137       << "EndIoStatement() for Close";
138 }
139 
140 TEST(ExternalIOTests, TestSequentialFixedUnformatted) {
141   // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
142   //   FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
143   auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
144   ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
145       << "SetAccess(SEQUENTIAL)";
146   ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
147   ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
148 
149   std::int64_t buffer;
150   static constexpr std::size_t recl{sizeof buffer};
151 
152   ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
153   ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
154 
155   int unit{-1};
156   ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
157   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
158       << "EndIoStatement() for OpenNewUnit";
159 
160   static const int records{10};
161   for (int j{1}; j <= records; ++j) {
162     // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO
163     io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
164     buffer = j;
165     ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
166         io, reinterpret_cast<const char *>(&buffer), recl, recl))
167         << "OutputUnformattedBlock()";
168     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
169         << "EndIoStatement() for OutputUnformattedBlock";
170   }
171 
172   // REWIND(UNIT=unit)
173   io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
174   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
175       << "EndIoStatement() for Rewind";
176 
177   for (int j{1}; j <= records; ++j) {
178     // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
179     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
180     ASSERT_TRUE(IONAME(InputUnformattedBlock)(
181         io, reinterpret_cast<char *>(&buffer), recl, recl))
182         << "InputUnformattedBlock()";
183     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
184         << "EndIoStatement() for InputUnformattedBlock";
185     ASSERT_EQ(buffer, j) << "Read back " << buffer
186                          << " from sequential fixed unformatted record " << j
187                          << ", expected " << j << '\n';
188   }
189 
190   for (int j{records}; j >= 1; --j) {
191     // BACKSPACE(UNIT=unit)
192     io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
193     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
194         << "EndIoStatement() for Backspace (before read)";
195     // READ(UNIT=unit) n
196     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
197     ASSERT_TRUE(IONAME(InputUnformattedBlock)(
198         io, reinterpret_cast<char *>(&buffer), recl, recl))
199         << "InputUnformattedBlock()";
200     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
201         << "EndIoStatement() for InputUnformattedBlock";
202     ASSERT_EQ(buffer, j) << "Read back " << buffer
203                          << " from sequential fixed unformatted record " << j
204                          << " after backspacing, expected " << j << '\n';
205     // BACKSPACE(UNIT=unit)
206     io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
207     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
208         << "EndIoStatement() for Backspace (after read)";
209   }
210 
211   // CLOSE(UNIT=unit,STATUS='DELETE')
212   io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
213   ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
214   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
215       << "EndIoStatement() for Close";
216 }
217 
218 TEST(ExternalIOTests, TestSequentialVariableUnformatted) {
219   // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
220   //   FORM='UNFORMATTED',STATUS='SCRATCH')
221   auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
222 
223   ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
224       << "SetAccess(SEQUENTIAL)";
225   ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
226   ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
227   ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
228 
229   int unit{-1};
230   ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
231   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
232       << "EndIoStatement() for OpenNewUnit";
233 
234   static const int records{10};
235   std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
236   for (int j{0}; j < records; ++j) {
237     buffer[j] = j;
238   }
239 
240   for (int j{1}; j <= records; ++j) {
241     // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO
242     io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
243     ASSERT_TRUE(IONAME(OutputUnformattedBlock)(io,
244         reinterpret_cast<const char *>(&buffer), j * sizeof *buffer,
245         sizeof *buffer))
246         << "OutputUnformattedBlock()";
247     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
248         << "EndIoStatement() for OutputUnformattedBlock";
249   }
250 
251   // REWIND(UNIT=unit)
252   io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
253   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
254       << "EndIoStatement() for Rewind";
255   for (int j{1}; j <= records; ++j) {
256     // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
257     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
258     ASSERT_TRUE(IONAME(InputUnformattedBlock)(io,
259         reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer))
260         << "InputUnformattedBlock()";
261     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
262         << "EndIoStatement() for InputUnformattedBlock";
263     for (int k{0}; k < j; ++k) {
264       ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
265                               << " from direct unformatted record " << j
266                               << ", expected " << k << '\n';
267     }
268   }
269 
270   for (int j{records}; j >= 1; --j) {
271     // BACKSPACE(unit)
272     io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
273     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
274         << "EndIoStatement() for Backspace (before read)";
275     // READ(unit=unit) n; check
276     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
277     ASSERT_TRUE(IONAME(InputUnformattedBlock)(io,
278         reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer))
279         << "InputUnformattedBlock()";
280     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
281         << "EndIoStatement() for InputUnformattedBlock";
282     for (int k{0}; k < j; ++k) {
283       ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
284                               << " from sequential variable unformatted record "
285                               << j << ", expected " << k << '\n';
286     }
287     // BACKSPACE(unit)
288     io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
289     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
290         << "EndIoStatement() for Backspace (after read)";
291   }
292 
293   // CLOSE(UNIT=unit,STATUS='DELETE')
294   io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
295   ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
296   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
297       << "EndIoStatement() for Close";
298 }
299 
300 TEST(ExternalIOTests, TestDirectFormatted) {
301   // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
302   //   FORM='FORMATTED',RECL=8,STATUS='SCRATCH')
303   auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
304   ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
305   ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
306   ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
307 
308   static constexpr std::size_t recl{8};
309   ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
310   ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
311 
312   int unit{-1};
313   ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
314   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
315       << "EndIoStatement() for OpenNewUnit";
316 
317   static constexpr int records{10};
318   static const char fmt[]{"(I4)"};
319   for (int j{1}; j <= records; ++j) {
320     // WRITE(UNIT=unit,FMT=fmt,REC=j) j
321     io = IONAME(BeginExternalFormattedOutput)(
322         fmt, sizeof fmt - 1, unit, __FILE__, __LINE__);
323     ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
324     ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()";
325     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
326         << "EndIoStatement() for OutputInteger64";
327   }
328 
329   for (int j{records}; j >= 1; --j) {
330     // READ(UNIT=unit,FMT=fmt,REC=j) n
331     io = IONAME(BeginExternalFormattedInput)(
332         fmt, sizeof fmt - 1, unit, __FILE__, __LINE__);
333     ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
334     std::int64_t buffer;
335     ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()";
336     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
337         << "EndIoStatement() for InputInteger";
338 
339     ASSERT_EQ(buffer, j) << "Read back " << buffer
340                          << " from direct formatted record " << j
341                          << ", expected " << j << '\n';
342   }
343 
344   // CLOSE(UNIT=unit,STATUS='DELETE')
345   io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
346   ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
347   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
348       << "EndIoStatement() for Close";
349 }
350 
351 TEST(ExternalIOTests, TestSequentialVariableFormatted) {
352   // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
353   //   FORM='FORMATTED',STATUS='SCRATCH')
354   auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
355   ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
356       << "SetAccess(SEQUENTIAL)";
357   ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
358   ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
359   ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
360 
361   int unit{-1};
362   ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
363   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
364       << "EndIoStatement() for OpenNewUnit";
365 
366   static const int records{10};
367   std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
368   for (int j{0}; j < records; ++j) {
369     buffer[j] = j;
370   }
371 
372   char fmt[32];
373   for (int j{1}; j <= records; ++j) {
374     std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
375     // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO
376     io = IONAME(BeginExternalFormattedOutput)(
377         fmt, std::strlen(fmt), unit, __FILE__, __LINE__);
378     for (int k{0}; k < j; ++k) {
379       ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k]))
380           << "OutputInteger64()";
381     }
382     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
383         << "EndIoStatement() for OutputInteger64";
384   }
385 
386   // REWIND(UNIT=unit)
387   io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
388   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
389       << "EndIoStatement() for Rewind";
390 
391   for (int j{1}; j <= records; ++j) {
392     std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
393     // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO
394     io = IONAME(BeginExternalFormattedInput)(
395         fmt, std::strlen(fmt), unit, __FILE__, __LINE__);
396 
397     std::int64_t check[records];
398     for (int k{0}; k < j; ++k) {
399       ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
400     }
401     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
402         << "EndIoStatement() for InputInteger";
403 
404     for (int k{0}; k < j; ++k) {
405       ASSERT_EQ(buffer[k], check[k])
406           << "Read back [" << k << "]=" << check[k]
407           << " from sequential variable formatted record " << j << ", expected "
408           << buffer[k] << '\n';
409     }
410   }
411 
412   for (int j{records}; j >= 1; --j) {
413     // BACKSPACE(unit)
414     io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
415     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
416         << "EndIoStatement() for Backspace (before read)";
417 
418     std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
419     // READ(UNIT=unit,FMT=fmt) n; check
420     io = IONAME(BeginExternalFormattedInput)(
421         fmt, std::strlen(fmt), unit, __FILE__, __LINE__);
422 
423     std::int64_t check[records];
424     for (int k{0}; k < j; ++k) {
425       ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
426     }
427 
428     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
429         << "EndIoStatement() for InputInteger";
430     for (int k{0}; k < j; ++k) {
431       ASSERT_EQ(buffer[k], check[k])
432           << "Read back [" << k << "]=" << buffer[k]
433           << " from sequential variable formatted record " << j << ", expected "
434           << buffer[k] << '\n';
435     }
436 
437     // BACKSPACE(unit)
438     io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
439     ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
440         << "EndIoStatement() for Backspace (after read)";
441   }
442 
443   // CLOSE(UNIT=unit,STATUS='DELETE')
444   io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
445   ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
446   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
447       << "EndIoStatement() for Close";
448 }
449