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 // 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
<< ')';
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)";
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)";
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
<< ')';
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)";
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)";
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);
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__
);
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";
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)";
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)";
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
) {
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
,
284 << "OutputUnformattedBlock()";
285 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
286 << "EndIoStatement() for OutputUnformattedBlock";
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
) {
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';
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)";
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
<< ')';
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)";
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
) {
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";
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
) {
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';
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)";
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()))
516 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
517 << "EndIoStatement() for OutputAscii";
521 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
522 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
523 << "EndIoStatement() for Rewind";
528 std::string expectedItemValue
[2];
530 // Actual non advancing input IO test
531 TestItems inputItems
[]{
532 {std::string(4, '+'), IostatOk
, {"ABCD", "ABCD"}},
533 {std::string(4, '+'), IostatOk
, {"EFGH", "EFGH"}},
534 {std::string(4, '+'), IostatEor
, {"++++", " "}},
535 {std::string(2, '+'), IostatOk
, {"IJ", "IJ"}},
536 {std::string(8, '+'), IostatEor
, {"++++++++", "KLMNOP "}},
537 {std::string(10, '+'), IostatEor
, {"++++++++++", "QRSTUVWX "}},
540 // Test with PAD='NO'
542 for (auto &inputItem
: inputItems
) {
543 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='NO', IOSTAT=iostat) inputItem
544 io
= IONAME(BeginExternalFormattedInput
)(
545 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
546 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
547 ASSERT_TRUE(IONAME(SetAdvance
)(io
, "NO", 2)) << "SetAdvance(NO)" << j
;
548 ASSERT_TRUE(IONAME(SetPad
)(io
, "NO", 2)) << "SetPad(NO)" << j
;
550 IONAME(InputAscii
)(io
, inputItem
.item
.data(), inputItem
.item
.length())};
551 ASSERT_EQ(result
, inputItem
.expectedIoStat
== IostatOk
)
552 << "InputAscii() " << j
;
553 ASSERT_EQ(IONAME(EndIoStatement
)(io
), inputItem
.expectedIoStat
)
554 << "EndIoStatement() for Read " << j
;
555 ASSERT_EQ(inputItem
.item
, inputItem
.expectedItemValue
[0])
556 << "Input-item value after non advancing read " << j
;
561 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
562 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
563 << "EndIoStatement() for Rewind";
565 // Test again with PAD='YES'
567 for (auto &inputItem
: inputItems
) {
568 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat)
570 io
= IONAME(BeginExternalFormattedInput
)(
571 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
572 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
573 ASSERT_TRUE(IONAME(SetAdvance
)(io
, "NO", 2)) << "SetAdvance(NO)" << j
;
574 ASSERT_TRUE(IONAME(SetPad
)(io
, "YES", 3)) << "SetPad(YES)" << j
;
576 IONAME(InputAscii
)(io
, inputItem
.item
.data(), inputItem
.item
.length())};
577 ASSERT_EQ(result
, inputItem
.expectedIoStat
== IostatOk
)
578 << "InputAscii() " << j
;
579 ASSERT_EQ(IONAME(EndIoStatement
)(io
), inputItem
.expectedIoStat
)
580 << "EndIoStatement() for Read " << j
;
581 ASSERT_EQ(inputItem
.item
, inputItem
.expectedItemValue
[1])
582 << "Input-item value after non advancing read " << j
;
587 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
588 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
589 << "EndIoStatement() for Close";
592 TEST(ExternalIOTests
, TestWriteAfterNonAvancingInput
) {
593 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
594 // FORM='FORMATTED',STATUS='SCRATCH')
595 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
596 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
597 << "SetAccess(SEQUENTIAL)";
598 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
599 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
600 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
603 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
604 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
605 << "EndIoStatement() for OpenNewUnit";
607 // Write the file to be used for the input test.
608 static constexpr std::string_view records
[] = {"ABCDEFGHIJKLMNOPQRST"};
609 static constexpr std::string_view fmt
{"(A)"};
610 for (const auto &record
: records
) {
611 // WRITE(UNIT=unit,FMT=fmt) record
612 io
= IONAME(BeginExternalFormattedOutput
)(
613 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
614 ASSERT_TRUE(IONAME(OutputAscii
)(io
, record
.data(), record
.length()))
616 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
617 << "EndIoStatement() for OutputAscii";
621 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
622 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
623 << "EndIoStatement() for Rewind";
628 std::string expectedItemValue
;
630 // Actual non advancing input IO test
631 TestItems inputItems
[]{
632 {std::string(4, '+'), IostatOk
, "ABCD"},
633 {std::string(4, '+'), IostatOk
, "EFGH"},
637 for (auto &inputItem
: inputItems
) {
638 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
639 io
= IONAME(BeginExternalFormattedInput
)(
640 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
641 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
642 ASSERT_TRUE(IONAME(SetAdvance
)(io
, "NO", 2)) << "SetAdvance(NO)" << j
;
644 IONAME(InputAscii
)(io
, inputItem
.item
.data(), inputItem
.item
.length()))
645 << "InputAscii() " << j
;
646 ASSERT_EQ(IONAME(EndIoStatement
)(io
), inputItem
.expectedIoStat
)
647 << "EndIoStatement() for Read " << j
;
648 ASSERT_EQ(inputItem
.item
, inputItem
.expectedItemValue
)
649 << "Input-item value after non advancing read " << j
;
653 // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem.
654 static constexpr std::string_view outputItem
{"XYZ"};
655 // WRITE(UNIT=unit,FMT=fmt) record
656 io
= IONAME(BeginExternalFormattedOutput
)(
657 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
658 ASSERT_TRUE(IONAME(OutputAscii
)(io
, outputItem
.data(), outputItem
.length()))
660 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
661 << "EndIoStatement() for OutputAscii";
663 // Verify that the output was written in the record read in non advancing
664 // mode, after the read part, and that the end was truncated.
667 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
668 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
669 << "EndIoStatement() for Rewind";
671 std::string
resultRecord(20, '+');
672 std::string expectedRecord
{"ABCDEFGHXYZ "};
673 // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result
674 io
= IONAME(BeginExternalFormattedInput
)(
675 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
676 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
678 IONAME(InputAscii
)(io
, resultRecord
.data(), resultRecord
.length()))
680 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
681 << "EndIoStatement() for Read ";
682 ASSERT_EQ(resultRecord
, expectedRecord
)
683 << "Record after non advancing read followed by write";
685 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
686 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
687 << "EndIoStatement() for Close";
690 TEST(ExternalIOTests
, TestWriteAfterEndfile
) {
691 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
692 // FORM='FORMATTED',STATUS='SCRATCH')
693 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
694 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
695 << "SetAccess(SEQUENTIAL)";
696 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
697 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
698 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
700 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
701 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
702 << "EndIoStatement() for OpenNewUnit";
703 // WRITE(unit,"(I8)") 1234
704 static constexpr std::string_view format
{"(I8)"};
705 io
= IONAME(BeginExternalFormattedOutput
)(
706 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
707 ASSERT_TRUE(IONAME(OutputInteger64
)(io
, 1234)) << "OutputInteger64()";
708 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
709 << "EndIoStatement for WRITE before ENDFILE";
711 io
= IONAME(BeginEndfile
)(unit
, __FILE__
, __LINE__
);
712 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
713 << "EndIoStatement for ENDFILE";
714 // WRITE(unit,"(I8)",iostat=iostat) 5678
715 io
= IONAME(BeginExternalFormattedOutput
)(
716 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
717 IONAME(EnableHandlers
)(io
, true /*IOSTAT=*/);
718 ASSERT_FALSE(IONAME(OutputInteger64
)(io
, 5678)) << "OutputInteger64()";
719 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatWriteAfterEndfile
)
720 << "EndIoStatement for WRITE after ENDFILE";
722 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
723 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
724 << "EndIoStatement for BACKSPACE";
725 // WRITE(unit,"(I8)") 3456
726 io
= IONAME(BeginExternalFormattedOutput
)(
727 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
728 ASSERT_TRUE(IONAME(OutputInteger64
)(io
, 3456)) << "OutputInteger64()";
729 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
730 << "EndIoStatement for WRITE after BACKSPACE";
732 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
733 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
734 << "EndIoStatement for REWIND";
735 // READ(unit,"(I8)",END=) j, k
736 std::int64_t j
{-1}, k
{-1}, eof
{-1};
737 io
= IONAME(BeginExternalFormattedInput
)(
738 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
739 IONAME(EnableHandlers
)(io
, false, false, true /*END=*/);
740 ASSERT_TRUE(IONAME(InputInteger
)(io
, j
)) << "InputInteger(j)";
741 ASSERT_EQ(j
, 1234) << "READ(j)";
742 ASSERT_TRUE(IONAME(InputInteger
)(io
, k
)) << "InputInteger(k)";
743 ASSERT_EQ(k
, 3456) << "READ(k)";
744 ASSERT_FALSE(IONAME(InputInteger
)(io
, eof
)) << "InputInteger(eof)";
745 ASSERT_EQ(eof
, -1) << "READ(eof)";
746 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatEnd
) << "EndIoStatement for READ";
748 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
749 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
750 << "EndIoStatement() for Close";
753 TEST(ExternalIOTests
, TestUTF8Encoding
) {
754 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
755 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
756 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
757 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
758 << "SetAccess(SEQUENTIAL)";
759 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
760 ASSERT_TRUE(IONAME(SetFile
)(io
, "utf8test", 8)) << "SetFile(utf8test)";
761 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
762 ASSERT_TRUE(IONAME(SetStatus
)(io
, "REPLACE", 7)) << "SetStatus(REPLACE)";
763 ASSERT_TRUE(IONAME(SetEncoding
)(io
, "UTF-8", 5)) << "SetEncoding(UTF-8)";
765 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
766 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
767 << "EndIoStatement() for first OPEN";
773 // WRITE(unit, *) buffer
774 io
= IONAME(BeginExternalListOutput
)(unit
, __FILE__
, __LINE__
);
775 StaticDescriptor
<0> staticDescriptor
;
776 Descriptor
&desc
{staticDescriptor
.descriptor()};
777 desc
.Establish(TypeCode
{CFI_type_char
}, 7, buffer
, 0);
779 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
));
780 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
781 << "EndIoStatement() for WRITE";
783 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
784 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
785 << "EndIoStatement for REWIND";
786 // READ(unit, *) buffer
787 desc
.Establish(TypeCode(CFI_type_char
), sizeof buffer
, buffer
, 0);
789 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
790 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
791 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
792 << "EndIoStatement() for first READ";
793 ASSERT_EQ(std::memcmp(buffer
,
798 // CLOSE(UNIT=unit,STATUS='KEEP')
799 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
800 ASSERT_TRUE(IONAME(SetStatus
)(io
, "KEEP", 4)) << "SetStatus(KEEP)";
801 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
802 << "EndIoStatement() for first CLOSE";
803 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
804 // FORM='FORMATTED',STATUS='OLD')
805 io
= IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
);
806 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
807 << "SetAccess(SEQUENTIAL)";
808 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
809 ASSERT_TRUE(IONAME(SetFile
)(io
, "utf8test", 8)) << "SetFile(utf8test)";
810 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
811 ASSERT_TRUE(IONAME(SetStatus
)(io
, "OLD", 3)) << "SetStatus(OLD)";
812 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
813 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
814 << "EndIoStatement() for second OPEN";
815 // READ(unit, *) buffer
816 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
817 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
818 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
819 << "EndIoStatement() for second READ";
820 ASSERT_EQ(std::memcmp(buffer
,
821 "abc\xc2\x80\xc3\xbf"
825 // CLOSE(UNIT=unit,STATUS='DELETE')
826 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
827 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
828 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
829 << "EndIoStatement() for second CLOSE";
832 TEST(ExternalIOTests
, TestUCS
) {
833 // OPEN(FILE="ucstest',NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
834 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
835 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
836 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
837 << "SetAccess(SEQUENTIAL)";
838 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
839 ASSERT_TRUE(IONAME(SetFile
)(io
, "ucstest", 7)) << "SetAction(ucstest)";
840 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
841 ASSERT_TRUE(IONAME(SetStatus
)(io
, "REPLACE", 7)) << "SetStatus(REPLACE)";
842 ASSERT_TRUE(IONAME(SetEncoding
)(io
, "UTF-8", 5)) << "SetEncoding(UTF-8)";
844 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
845 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
846 << "EndIoStatement() for first OPEN";
847 char32_t wbuffer
[8]{U
"abc\u0080\uffff"
849 // WRITE(unit, *) wbuffec
850 io
= IONAME(BeginExternalListOutput
)(unit
, __FILE__
, __LINE__
);
851 StaticDescriptor
<0> staticDescriptor
;
852 Descriptor
&desc
{staticDescriptor
.descriptor()};
853 desc
.Establish(TypeCode
{CFI_type_char32_t
}, sizeof wbuffer
- sizeof(char32_t
),
856 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
));
857 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
858 << "EndIoStatement() for WRITE";
860 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
861 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
862 << "EndIoStatement for REWIND";
863 // READ(unit, *) buffer
864 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
865 desc
.Establish(TypeCode
{CFI_type_char32_t
}, sizeof wbuffer
, wbuffer
, 0);
867 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
868 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
869 << "EndIoStatement() for first READ";
872 for (int j
{0}; j
< 8; ++j
) {
873 std::size_t dumpLen
{std::strlen(dump
)};
875 dump
+ dumpLen
, sizeof dump
- dumpLen
, " %x", (unsigned)wbuffer
[j
]);
877 EXPECT_EQ(wbuffer
[0], U
'a') << dump
;
878 EXPECT_EQ(wbuffer
[1], U
'b') << dump
;
879 EXPECT_EQ(wbuffer
[2], U
'c') << dump
;
880 EXPECT_EQ(wbuffer
[3], U
'\u0080') << dump
;
881 EXPECT_EQ(wbuffer
[4], U
'\uffff') << dump
;
882 EXPECT_EQ(wbuffer
[5], U
'd') << dump
;
883 EXPECT_EQ(wbuffer
[6], U
'e') << dump
;
884 EXPECT_EQ(wbuffer
[7], U
' ') << dump
;
885 // CLOSE(UNIT=unit,STATUS='KEEP')
886 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
887 ASSERT_TRUE(IONAME(SetStatus
)(io
, "KEEP", 4)) << "SetStatus(KEEP)";
888 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
889 << "EndIoStatement() for first CLOSE";
890 // OPEN(FILE="ucstest",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
891 // FORM='FORMATTED',STATUS='OLD')
892 io
= IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
);
893 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
894 << "SetAccess(SEQUENTIAL)";
895 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
896 ASSERT_TRUE(IONAME(SetFile
)(io
, "ucstest", 7)) << "SetFile(ucstest)";
897 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
898 ASSERT_TRUE(IONAME(SetStatus
)(io
, "OLD", 3)) << "SetStatus(OLD)";
899 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
900 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
901 << "EndIoStatement() for second OPEN";
903 // READ(unit, *) buffer
904 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
905 desc
.Establish(TypeCode
{CFI_type_char
}, sizeof buffer
, buffer
, 0);
907 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
908 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
909 << "EndIoStatement() for second READ";
911 for (int j
{0}; j
< 12; ++j
) {
912 std::size_t dumpLen
{std::strlen(dump
)};
913 std::snprintf(dump
+ dumpLen
, sizeof dump
- dumpLen
, " %x",
914 (unsigned)(unsigned char)buffer
[j
]);
916 EXPECT_EQ(std::memcmp(buffer
,
917 "abc\xc2\x80\xef\xbf\xbf"
922 // CLOSE(UNIT=unit,STATUS='DELETE')
923 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
924 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
925 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
926 << "EndIoStatement() for second CLOSE";
929 TEST(ExternalIOTests
, BigUnitNumbers
) {
930 if (std::numeric_limits
<ExternalUnit
>::max() <
931 std::numeric_limits
<std::int64_t>::max()) {
932 std::int64_t unit64Ok
= std::numeric_limits
<ExternalUnit
>::max();
933 std::int64_t unit64Bad
= unit64Ok
+ 1;
934 std::int64_t unit64Bad2
=
935 static_cast<std::int64_t>(std::numeric_limits
<ExternalUnit
>::min()) - 1;
936 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Ok
, true), IostatOk
);
937 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Ok
, false), IostatOk
);
939 IONAME(CheckUnitNumberInRange64
)(unit64Bad
, true), IostatUnitOverflow
);
941 IONAME(CheckUnitNumberInRange64
)(unit64Bad2
, true), IostatUnitOverflow
);
943 IONAME(CheckUnitNumberInRange64
)(unit64Bad
, true), IostatUnitOverflow
);
945 IONAME(CheckUnitNumberInRange64
)(unit64Bad2
, true), IostatUnitOverflow
);
946 constexpr std::size_t n
{80};
947 char expectedMsg
[n
+ 1];
948 expectedMsg
[n
] = '\0';
949 std::snprintf(expectedMsg
, n
, "UNIT number %jd is out of range",
950 static_cast<std::intmax_t>(unit64Bad
));
952 IONAME(CheckUnitNumberInRange64
)(2147483648, false), expectedMsg
);
953 for (auto i
{std::strlen(expectedMsg
)}; i
< n
; ++i
) {
954 expectedMsg
[i
] = ' ';
958 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Bad
, true, msg
, n
),
960 EXPECT_EQ(std::strncmp(msg
, expectedMsg
, n
), 0);