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
;
530 // Actual non advancing input IO test
531 TestItems inputItems
[]{
532 {std::string(4, '+'), IostatOk
, "ABCD"},
533 {std::string(4, '+'), IostatOk
, "EFGH"},
534 {std::string(4, '+'), IostatEor
, " "},
535 {std::string(2, '+'), IostatOk
, "IJ"},
536 {std::string(8, '+'), IostatEor
, "KLMNOP "},
537 {std::string(10, '+'), IostatEor
, "QRSTUVWX "},
541 for (auto &inputItem
: inputItems
) {
542 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
543 io
= IONAME(BeginExternalFormattedInput
)(
544 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
545 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
546 ASSERT_TRUE(IONAME(SetAdvance
)(io
, "NO", 2)) << "SetAdvance(NO)" << j
;
548 IONAME(InputAscii
)(io
, inputItem
.item
.data(), inputItem
.item
.length())};
549 ASSERT_EQ(result
, inputItem
.expectedIoStat
== IostatOk
)
550 << "InputAscii() " << j
;
551 ASSERT_EQ(IONAME(EndIoStatement
)(io
), inputItem
.expectedIoStat
)
552 << "EndIoStatement() for Read " << j
;
553 ASSERT_EQ(inputItem
.item
, inputItem
.expectedItemValue
)
554 << "Input-item value after non advancing read " << j
;
558 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
559 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
560 << "EndIoStatement() for Close";
563 TEST(ExternalIOTests
, TestWriteAfterNonAvancingInput
) {
564 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
565 // FORM='FORMATTED',STATUS='SCRATCH')
566 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
567 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
568 << "SetAccess(SEQUENTIAL)";
569 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
570 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
571 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
574 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
575 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
576 << "EndIoStatement() for OpenNewUnit";
578 // Write the file to be used for the input test.
579 static constexpr std::string_view records
[] = {"ABCDEFGHIJKLMNOPQRST"};
580 static constexpr std::string_view fmt
{"(A)"};
581 for (const auto &record
: records
) {
582 // WRITE(UNIT=unit,FMT=fmt) record
583 io
= IONAME(BeginExternalFormattedOutput
)(
584 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
585 ASSERT_TRUE(IONAME(OutputAscii
)(io
, record
.data(), record
.length()))
587 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
588 << "EndIoStatement() for OutputAscii";
592 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
593 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
594 << "EndIoStatement() for Rewind";
599 std::string expectedItemValue
;
601 // Actual non advancing input IO test
602 TestItems inputItems
[]{
603 {std::string(4, '+'), IostatOk
, "ABCD"},
604 {std::string(4, '+'), IostatOk
, "EFGH"},
608 for (auto &inputItem
: inputItems
) {
609 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
610 io
= IONAME(BeginExternalFormattedInput
)(
611 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
612 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
613 ASSERT_TRUE(IONAME(SetAdvance
)(io
, "NO", 2)) << "SetAdvance(NO)" << j
;
615 IONAME(InputAscii
)(io
, inputItem
.item
.data(), inputItem
.item
.length()))
616 << "InputAscii() " << j
;
617 ASSERT_EQ(IONAME(EndIoStatement
)(io
), inputItem
.expectedIoStat
)
618 << "EndIoStatement() for Read " << j
;
619 ASSERT_EQ(inputItem
.item
, inputItem
.expectedItemValue
)
620 << "Input-item value after non advancing read " << j
;
624 // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem.
625 static constexpr std::string_view outputItem
{"XYZ"};
626 // WRITE(UNIT=unit,FMT=fmt) record
627 io
= IONAME(BeginExternalFormattedOutput
)(
628 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
629 ASSERT_TRUE(IONAME(OutputAscii
)(io
, outputItem
.data(), outputItem
.length()))
631 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
632 << "EndIoStatement() for OutputAscii";
634 // Verify that the output was written in the record read in non advancing
635 // mode, after the read part, and that the end was truncated.
638 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
639 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
640 << "EndIoStatement() for Rewind";
642 std::string
resultRecord(20, '+');
643 std::string expectedRecord
{"ABCDEFGHXYZ "};
644 // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result
645 io
= IONAME(BeginExternalFormattedInput
)(
646 fmt
.data(), fmt
.length(), nullptr, unit
, __FILE__
, __LINE__
);
647 IONAME(EnableHandlers
)(io
, true, false, false, false, false);
649 IONAME(InputAscii
)(io
, resultRecord
.data(), resultRecord
.length()))
651 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
652 << "EndIoStatement() for Read ";
653 ASSERT_EQ(resultRecord
, expectedRecord
)
654 << "Record after non advancing read followed by write";
656 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
657 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
658 << "EndIoStatement() for Close";
661 TEST(ExternalIOTests
, TestWriteAfterEndfile
) {
662 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
663 // FORM='FORMATTED',STATUS='SCRATCH')
664 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
665 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
666 << "SetAccess(SEQUENTIAL)";
667 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
668 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
669 ASSERT_TRUE(IONAME(SetStatus
)(io
, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
671 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
672 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
673 << "EndIoStatement() for OpenNewUnit";
674 // WRITE(unit,"(I8)") 1234
675 static constexpr std::string_view format
{"(I8)"};
676 io
= IONAME(BeginExternalFormattedOutput
)(
677 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
678 ASSERT_TRUE(IONAME(OutputInteger64
)(io
, 1234)) << "OutputInteger64()";
679 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
680 << "EndIoStatement for WRITE before ENDFILE";
682 io
= IONAME(BeginEndfile
)(unit
, __FILE__
, __LINE__
);
683 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
684 << "EndIoStatement for ENDFILE";
685 // WRITE(unit,"(I8)",iostat=iostat) 5678
686 io
= IONAME(BeginExternalFormattedOutput
)(
687 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
688 IONAME(EnableHandlers
)(io
, true /*IOSTAT=*/);
689 ASSERT_FALSE(IONAME(OutputInteger64
)(io
, 5678)) << "OutputInteger64()";
690 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatWriteAfterEndfile
)
691 << "EndIoStatement for WRITE after ENDFILE";
693 io
= IONAME(BeginBackspace
)(unit
, __FILE__
, __LINE__
);
694 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
695 << "EndIoStatement for BACKSPACE";
696 // WRITE(unit,"(I8)") 3456
697 io
= IONAME(BeginExternalFormattedOutput
)(
698 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
699 ASSERT_TRUE(IONAME(OutputInteger64
)(io
, 3456)) << "OutputInteger64()";
700 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
701 << "EndIoStatement for WRITE after BACKSPACE";
703 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
704 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
705 << "EndIoStatement for REWIND";
706 // READ(unit,"(I8)",END=) j, k
707 std::int64_t j
{-1}, k
{-1}, eof
{-1};
708 io
= IONAME(BeginExternalFormattedInput
)(
709 format
.data(), format
.length(), nullptr, unit
, __FILE__
, __LINE__
);
710 IONAME(EnableHandlers
)(io
, false, false, true /*END=*/);
711 ASSERT_TRUE(IONAME(InputInteger
)(io
, j
)) << "InputInteger(j)";
712 ASSERT_EQ(j
, 1234) << "READ(j)";
713 ASSERT_TRUE(IONAME(InputInteger
)(io
, k
)) << "InputInteger(k)";
714 ASSERT_EQ(k
, 3456) << "READ(k)";
715 ASSERT_FALSE(IONAME(InputInteger
)(io
, eof
)) << "InputInteger(eof)";
716 ASSERT_EQ(eof
, -1) << "READ(eof)";
717 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatEnd
) << "EndIoStatement for READ";
719 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
720 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
721 << "EndIoStatement() for Close";
724 TEST(ExternalIOTests
, TestUTF8Encoding
) {
725 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
726 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
727 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
728 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
729 << "SetAccess(SEQUENTIAL)";
730 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
731 ASSERT_TRUE(IONAME(SetFile
)(io
, "utf8test", 8)) << "SetFile(utf8test)";
732 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
733 ASSERT_TRUE(IONAME(SetStatus
)(io
, "REPLACE", 7)) << "SetStatus(REPLACE)";
734 ASSERT_TRUE(IONAME(SetEncoding
)(io
, "UTF-8", 5)) << "SetEncoding(UTF-8)";
736 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
737 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
738 << "EndIoStatement() for first OPEN";
744 // WRITE(unit, *) buffer
745 io
= IONAME(BeginExternalListOutput
)(unit
, __FILE__
, __LINE__
);
746 StaticDescriptor
<0> staticDescriptor
;
747 Descriptor
&desc
{staticDescriptor
.descriptor()};
748 desc
.Establish(TypeCode
{CFI_type_char
}, 7, buffer
, 0);
750 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
));
751 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
752 << "EndIoStatement() for WRITE";
754 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
755 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
756 << "EndIoStatement for REWIND";
757 // READ(unit, *) buffer
758 desc
.Establish(TypeCode(CFI_type_char
), sizeof buffer
, buffer
, 0);
760 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
761 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
762 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
763 << "EndIoStatement() for first READ";
764 ASSERT_EQ(std::memcmp(buffer
,
769 // CLOSE(UNIT=unit,STATUS='KEEP')
770 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
771 ASSERT_TRUE(IONAME(SetStatus
)(io
, "KEEP", 4)) << "SetStatus(KEEP)";
772 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
773 << "EndIoStatement() for first CLOSE";
774 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
775 // FORM='FORMATTED',STATUS='OLD')
776 io
= IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
);
777 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
778 << "SetAccess(SEQUENTIAL)";
779 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
780 ASSERT_TRUE(IONAME(SetFile
)(io
, "utf8test", 8)) << "SetFile(utf8test)";
781 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
782 ASSERT_TRUE(IONAME(SetStatus
)(io
, "OLD", 3)) << "SetStatus(OLD)";
783 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
784 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
785 << "EndIoStatement() for second OPEN";
786 // READ(unit, *) buffer
787 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
788 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
789 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
790 << "EndIoStatement() for second READ";
791 ASSERT_EQ(std::memcmp(buffer
,
792 "abc\xc2\x80\xc3\xbf"
796 // CLOSE(UNIT=unit,STATUS='DELETE')
797 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
798 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
799 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
800 << "EndIoStatement() for second CLOSE";
803 TEST(ExternalIOTests
, TestUCS
) {
804 // OPEN(FILE="ucstest',NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
805 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
806 auto *io
{IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
)};
807 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
808 << "SetAccess(SEQUENTIAL)";
809 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
810 ASSERT_TRUE(IONAME(SetFile
)(io
, "ucstest", 7)) << "SetAction(ucstest)";
811 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
812 ASSERT_TRUE(IONAME(SetStatus
)(io
, "REPLACE", 7)) << "SetStatus(REPLACE)";
813 ASSERT_TRUE(IONAME(SetEncoding
)(io
, "UTF-8", 5)) << "SetEncoding(UTF-8)";
815 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
816 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
817 << "EndIoStatement() for first OPEN";
818 char32_t wbuffer
[8]{U
"abc\u0080\uffff"
820 // WRITE(unit, *) wbuffec
821 io
= IONAME(BeginExternalListOutput
)(unit
, __FILE__
, __LINE__
);
822 StaticDescriptor
<0> staticDescriptor
;
823 Descriptor
&desc
{staticDescriptor
.descriptor()};
824 desc
.Establish(TypeCode
{CFI_type_char32_t
}, sizeof wbuffer
- sizeof(char32_t
),
827 ASSERT_TRUE(IONAME(OutputDescriptor
)(io
, desc
));
828 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
829 << "EndIoStatement() for WRITE";
831 io
= IONAME(BeginRewind
)(unit
, __FILE__
, __LINE__
);
832 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
833 << "EndIoStatement for REWIND";
834 // READ(unit, *) buffer
835 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
836 desc
.Establish(TypeCode
{CFI_type_char32_t
}, sizeof wbuffer
, wbuffer
, 0);
838 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
839 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
840 << "EndIoStatement() for first READ";
843 for (int j
{0}; j
< 8; ++j
) {
844 std::size_t dumpLen
{std::strlen(dump
)};
846 dump
+ dumpLen
, sizeof dump
- dumpLen
, " %x", (unsigned)wbuffer
[j
]);
848 EXPECT_EQ(wbuffer
[0], U
'a') << dump
;
849 EXPECT_EQ(wbuffer
[1], U
'b') << dump
;
850 EXPECT_EQ(wbuffer
[2], U
'c') << dump
;
851 EXPECT_EQ(wbuffer
[3], U
'\u0080') << dump
;
852 EXPECT_EQ(wbuffer
[4], U
'\uffff') << dump
;
853 EXPECT_EQ(wbuffer
[5], U
'd') << dump
;
854 EXPECT_EQ(wbuffer
[6], U
'e') << dump
;
855 EXPECT_EQ(wbuffer
[7], U
' ') << dump
;
856 // CLOSE(UNIT=unit,STATUS='KEEP')
857 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
858 ASSERT_TRUE(IONAME(SetStatus
)(io
, "KEEP", 4)) << "SetStatus(KEEP)";
859 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
860 << "EndIoStatement() for first CLOSE";
861 // OPEN(FILE="ucstest",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
862 // FORM='FORMATTED',STATUS='OLD')
863 io
= IONAME(BeginOpenNewUnit
)(__FILE__
, __LINE__
);
864 ASSERT_TRUE(IONAME(SetAccess
)(io
, "SEQUENTIAL", 10))
865 << "SetAccess(SEQUENTIAL)";
866 ASSERT_TRUE(IONAME(SetAction
)(io
, "READWRITE", 9)) << "SetAction(READWRITE)";
867 ASSERT_TRUE(IONAME(SetFile
)(io
, "ucstest", 7)) << "SetFile(ucstest)";
868 ASSERT_TRUE(IONAME(SetForm
)(io
, "FORMATTED", 9)) << "SetForm(FORMATTED)";
869 ASSERT_TRUE(IONAME(SetStatus
)(io
, "OLD", 3)) << "SetStatus(OLD)";
870 ASSERT_TRUE(IONAME(GetNewUnit
)(io
, unit
)) << "GetNewUnit()";
871 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
872 << "EndIoStatement() for second OPEN";
874 // READ(unit, *) buffer
875 io
= IONAME(BeginExternalListInput
)(unit
, __FILE__
, __LINE__
);
876 desc
.Establish(TypeCode
{CFI_type_char
}, sizeof buffer
, buffer
, 0);
878 ASSERT_TRUE(IONAME(InputDescriptor
)(io
, desc
));
879 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
880 << "EndIoStatement() for second READ";
882 for (int j
{0}; j
< 12; ++j
) {
883 std::size_t dumpLen
{std::strlen(dump
)};
884 std::snprintf(dump
+ dumpLen
, sizeof dump
- dumpLen
, " %x",
885 (unsigned)(unsigned char)buffer
[j
]);
887 EXPECT_EQ(std::memcmp(buffer
,
888 "abc\xc2\x80\xef\xbf\xbf"
893 // CLOSE(UNIT=unit,STATUS='DELETE')
894 io
= IONAME(BeginClose
)(unit
, __FILE__
, __LINE__
);
895 ASSERT_TRUE(IONAME(SetStatus
)(io
, "DELETE", 6)) << "SetStatus(DELETE)";
896 ASSERT_EQ(IONAME(EndIoStatement
)(io
), IostatOk
)
897 << "EndIoStatement() for second CLOSE";
900 TEST(ExternalIOTests
, BigUnitNumbers
) {
901 if (std::numeric_limits
<ExternalUnit
>::max() <
902 std::numeric_limits
<std::int64_t>::max()) {
903 std::int64_t unit64Ok
= std::numeric_limits
<ExternalUnit
>::max();
904 std::int64_t unit64Bad
= unit64Ok
+ 1;
905 std::int64_t unit64Bad2
=
906 static_cast<std::int64_t>(std::numeric_limits
<ExternalUnit
>::min()) - 1;
907 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Ok
, true), IostatOk
);
908 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Ok
, false), IostatOk
);
910 IONAME(CheckUnitNumberInRange64
)(unit64Bad
, true), IostatUnitOverflow
);
912 IONAME(CheckUnitNumberInRange64
)(unit64Bad2
, true), IostatUnitOverflow
);
914 IONAME(CheckUnitNumberInRange64
)(unit64Bad
, true), IostatUnitOverflow
);
916 IONAME(CheckUnitNumberInRange64
)(unit64Bad2
, true), IostatUnitOverflow
);
917 constexpr std::size_t n
{80};
918 char expectedMsg
[n
+ 1];
919 expectedMsg
[n
] = '\0';
920 std::snprintf(expectedMsg
, n
, "UNIT number %jd is out of range",
921 static_cast<std::intmax_t>(unit64Bad
));
923 IONAME(CheckUnitNumberInRange64
)(2147483648, false), expectedMsg
);
924 for (auto i
{std::strlen(expectedMsg
)}; i
< n
; ++i
) {
925 expectedMsg
[i
] = ' ';
929 EXPECT_EQ(IONAME(CheckUnitNumberInRange64
)(unit64Bad
, true, msg
, n
),
931 EXPECT_EQ(std::strncmp(msg
, expectedMsg
, n
), 0);