[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / flang / unittests / Runtime / ExternalIOTest.cpp
blob4f08505f05d0ad40071158e8ff0375c0c2186f4f
1 //===-- flang/unittests/RuntimeGTest/ExternalIOTest.cpp ---------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Sanity test for all external I/O modes
11 //===----------------------------------------------------------------------===//
13 #include "CrashHandlerFixture.h"
14 #include "gtest/gtest.h"
15 #include "flang/Runtime/descriptor.h"
16 #include "flang/Runtime/io-api.h"
17 #include "flang/Runtime/main.h"
18 #include "flang/Runtime/stop.h"
19 #include "llvm/Support/raw_ostream.h"
20 #include <cstring>
21 #include <string_view>
23 using namespace Fortran::runtime;
24 using namespace Fortran::runtime::io;
26 struct ExternalIOTests : public CrashHandlerFixture {};
28 TEST(ExternalIOTests, TestDirectUnformatted) {
29 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
30 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
31 Cookie io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
32 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
33 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
34 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
36 std::int64_t buffer;
37 static constexpr std::size_t recl{sizeof buffer};
38 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
39 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
41 int unit{-1};
42 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
43 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
44 << "EndIoStatement() for OpenNewUnit";
46 // INQUIRE(IOLENGTH=) j
47 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
48 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
49 io, reinterpret_cast<const char *>(&buffer), recl, 1))
50 << "OutputUnformattedBlock() for InquireIoLength";
51 ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength";
52 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
53 << "EndIoStatement() for InquireIoLength";
55 static constexpr int records{10};
56 for (int j{1}; j <= records; ++j) {
57 // WRITE(UNIT=unit,REC=j) j
58 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
59 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
61 buffer = j;
62 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
63 io, reinterpret_cast<const char *>(&buffer), 1, recl))
64 << "OutputUnformattedBlock()";
66 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
67 << "EndIoStatement() for OutputUnformattedBlock";
70 for (int j{records}; j >= 1; --j) {
71 // READ(UNIT=unit,REC=j) n
72 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
73 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
74 ASSERT_TRUE(IONAME(InputUnformattedBlock)(
75 io, reinterpret_cast<char *>(&buffer), 1, recl))
76 << "InputUnformattedBlock()";
78 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
79 << "EndIoStatement() for InputUnformattedBlock";
81 ASSERT_EQ(buffer, j) << "Read back " << buffer
82 << " from direct unformatted record " << j
83 << ", expected " << j << '\n';
85 // CLOSE(UNIT=unit,STATUS='DELETE')
86 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
87 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
88 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
89 << "EndIoStatement() for Close";
92 TEST(ExternalIOTests, TestDirectUnformattedSwapped) {
93 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
94 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE')
95 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
96 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
97 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
98 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
99 ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)";
101 std::int64_t buffer;
102 static constexpr std::size_t recl{sizeof buffer};
103 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
104 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
106 int unit{-1};
107 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
108 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
109 << "EndIoStatement() for OpenNewUnit";
111 static constexpr int records{10};
112 for (int j{1}; j <= records; ++j) {
113 // WRITE(UNIT=unit,REC=j) j
114 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
115 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
116 buffer = j;
117 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
118 io, reinterpret_cast<const char *>(&buffer), recl, recl))
119 << "OutputUnformattedBlock()";
120 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
121 << "EndIoStatement() for OutputUnformattedBlock";
124 // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP')
125 io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__);
126 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
127 ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)";
128 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
129 << "EndIoStatement() for OpenUnit";
131 for (int j{records}; j >= 1; --j) {
132 // READ(UNIT=unit,REC=j) n
133 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
134 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
135 ASSERT_TRUE(IONAME(InputUnformattedBlock)(
136 io, reinterpret_cast<char *>(&buffer), recl, recl))
137 << "InputUnformattedBlock()";
138 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
139 << "EndIoStatement() for InputUnformattedBlock";
140 ASSERT_EQ(buffer >> 56, j)
141 << "Read back " << (buffer >> 56) << " from direct unformatted record "
142 << j << ", expected " << j << '\n';
145 // CLOSE(UNIT=unit,STATUS='DELETE')
146 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
147 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
148 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
149 << "EndIoStatement() for Close";
152 TEST(ExternalIOTests, TestSequentialFixedUnformatted) {
153 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
154 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
155 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
156 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
157 << "SetAccess(SEQUENTIAL)";
158 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
159 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
161 std::int64_t buffer;
162 static constexpr std::size_t recl{sizeof buffer};
164 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
165 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
167 int unit{-1};
168 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
169 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
170 << "EndIoStatement() for OpenNewUnit";
172 // INQUIRE(IOLENGTH=) j, ...
173 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
174 for (int j{1}; j <= 3; ++j) {
175 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
176 io, reinterpret_cast<const char *>(&buffer), recl, 1))
177 << "OutputUnformattedBlock() for InquireIoLength";
179 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
180 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
181 << "EndIoStatement() for InquireIoLength";
183 // INQUIRE(IOLENGTH=) j, ...
184 StaticDescriptor<0> staticDescriptor;
185 Descriptor &desc{staticDescriptor.descriptor()};
186 desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0);
187 desc.Dump(stderr);
188 desc.Check();
189 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
190 for (int j{1}; j <= 3; ++j) {
191 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
192 << "OutputDescriptor() for InquireIoLength";
194 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
195 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
196 << "EndIoStatement() for InquireIoLength";
198 static const int records{10};
199 for (int j{1}; j <= records; ++j) {
200 // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO
201 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
202 buffer = j;
203 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
204 io, reinterpret_cast<const char *>(&buffer), recl, recl))
205 << "OutputUnformattedBlock()";
206 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
207 << "EndIoStatement() for OutputUnformattedBlock";
210 // REWIND(UNIT=unit)
211 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
212 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
213 << "EndIoStatement() for Rewind";
215 for (int j{1}; j <= records; ++j) {
216 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
217 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
218 ASSERT_TRUE(IONAME(InputUnformattedBlock)(
219 io, reinterpret_cast<char *>(&buffer), recl, recl))
220 << "InputUnformattedBlock()";
221 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
222 << "EndIoStatement() for InputUnformattedBlock";
223 ASSERT_EQ(buffer, j) << "Read back " << buffer
224 << " from sequential fixed unformatted record " << j
225 << ", expected " << j << '\n';
228 for (int j{records}; j >= 1; --j) {
229 // BACKSPACE(UNIT=unit)
230 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
231 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
232 << "EndIoStatement() for Backspace (before read)";
233 // READ(UNIT=unit) n
234 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
235 ASSERT_TRUE(IONAME(InputUnformattedBlock)(
236 io, reinterpret_cast<char *>(&buffer), recl, recl))
237 << "InputUnformattedBlock()";
238 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
239 << "EndIoStatement() for InputUnformattedBlock";
240 ASSERT_EQ(buffer, j) << "Read back " << buffer
241 << " from sequential fixed unformatted record " << j
242 << " after backspacing, expected " << j << '\n';
243 // BACKSPACE(UNIT=unit)
244 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
245 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
246 << "EndIoStatement() for Backspace (after read)";
249 // CLOSE(UNIT=unit,STATUS='DELETE')
250 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
251 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
252 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
253 << "EndIoStatement() for Close";
256 TEST(ExternalIOTests, TestSequentialVariableUnformatted) {
257 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
258 // FORM='UNFORMATTED',STATUS='SCRATCH')
259 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
261 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
262 << "SetAccess(SEQUENTIAL)";
263 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
264 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
265 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
267 int unit{-1};
268 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
269 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
270 << "EndIoStatement() for OpenNewUnit";
272 static const int records{10};
273 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
274 for (int j{0}; j < records; ++j) {
275 buffer[j] = j;
278 for (int j{1}; j <= records; ++j) {
279 // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO
280 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
281 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(io,
282 reinterpret_cast<const char *>(&buffer), j * sizeof *buffer,
283 sizeof *buffer))
284 << "OutputUnformattedBlock()";
285 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
286 << "EndIoStatement() for OutputUnformattedBlock";
289 // REWIND(UNIT=unit)
290 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
291 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
292 << "EndIoStatement() for Rewind";
293 for (int j{1}; j <= records; ++j) {
294 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
295 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
296 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io,
297 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer))
298 << "InputUnformattedBlock()";
299 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
300 << "EndIoStatement() for InputUnformattedBlock";
301 for (int k{0}; k < j; ++k) {
302 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
303 << " from direct unformatted record " << j
304 << ", expected " << k << '\n';
308 for (int j{records}; j >= 1; --j) {
309 // BACKSPACE(unit)
310 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
311 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
312 << "EndIoStatement() for Backspace (before read)";
313 // READ(unit=unit) n; check
314 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
315 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io,
316 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer))
317 << "InputUnformattedBlock()";
318 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
319 << "EndIoStatement() for InputUnformattedBlock";
320 for (int k{0}; k < j; ++k) {
321 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
322 << " from sequential variable unformatted record "
323 << j << ", expected " << k << '\n';
325 // BACKSPACE(unit)
326 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
327 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
328 << "EndIoStatement() for Backspace (after read)";
331 // CLOSE(UNIT=unit,STATUS='DELETE')
332 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
333 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
334 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
335 << "EndIoStatement() for Close";
338 TEST(ExternalIOTests, TestDirectFormatted) {
339 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
340 // FORM='FORMATTED',RECL=8,STATUS='SCRATCH')
341 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
342 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
343 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
344 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
346 static constexpr std::size_t recl{8};
347 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
348 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
350 int unit{-1};
351 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
352 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
353 << "EndIoStatement() for OpenNewUnit";
355 static constexpr int records{10};
356 static const char fmt[]{"(I4)"};
357 for (int j{1}; j <= records; ++j) {
358 // WRITE(UNIT=unit,FMT=fmt,REC=j) j
359 io = IONAME(BeginExternalFormattedOutput)(
360 fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__);
361 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
362 ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()";
363 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
364 << "EndIoStatement() for OutputInteger64";
367 for (int j{records}; j >= 1; --j) {
368 // READ(UNIT=unit,FMT=fmt,REC=j) n
369 io = IONAME(BeginExternalFormattedInput)(
370 fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__);
371 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
372 std::int64_t buffer;
373 ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()";
374 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
375 << "EndIoStatement() for InputInteger";
377 ASSERT_EQ(buffer, j) << "Read back " << buffer
378 << " from direct formatted record " << j
379 << ", expected " << j << '\n';
382 // CLOSE(UNIT=unit,STATUS='DELETE')
383 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
384 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
385 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
386 << "EndIoStatement() for Close";
389 TEST(ExternalIOTests, TestSequentialVariableFormatted) {
390 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
391 // FORM='FORMATTED',STATUS='SCRATCH')
392 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
393 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
394 << "SetAccess(SEQUENTIAL)";
395 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
396 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
397 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
399 int unit{-1};
400 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
401 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
402 << "EndIoStatement() for OpenNewUnit";
404 static const int records{10};
405 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
406 for (int j{0}; j < records; ++j) {
407 buffer[j] = j;
410 char fmt[32];
411 for (int j{1}; j <= records; ++j) {
412 std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
413 // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO
414 io = IONAME(BeginExternalFormattedOutput)(
415 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
416 for (int k{0}; k < j; ++k) {
417 ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k]))
418 << "OutputInteger64()";
420 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
421 << "EndIoStatement() for OutputInteger64";
424 // REWIND(UNIT=unit)
425 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
426 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
427 << "EndIoStatement() for Rewind";
429 for (int j{1}; j <= records; ++j) {
430 std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
431 // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO
432 io = IONAME(BeginExternalFormattedInput)(
433 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
435 std::int64_t check[records];
436 for (int k{0}; k < j; ++k) {
437 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
439 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
440 << "EndIoStatement() for InputInteger";
442 for (int k{0}; k < j; ++k) {
443 ASSERT_EQ(buffer[k], check[k])
444 << "Read back [" << k << "]=" << check[k]
445 << " from sequential variable formatted record " << j << ", expected "
446 << buffer[k] << '\n';
450 for (int j{records}; j >= 1; --j) {
451 // BACKSPACE(unit)
452 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
453 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
454 << "EndIoStatement() for Backspace (before read)";
456 std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
457 // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check
458 io = IONAME(BeginExternalFormattedInput)(
459 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
461 std::int64_t check[records];
462 for (int k{0}; k < j; ++k) {
463 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
466 std::size_t chars{IONAME(GetSize)(io)};
467 ASSERT_EQ(chars, j * 4u)
468 << "GetSize()=" << chars << ", expected " << (j * 4u) << '\n';
469 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
470 << "EndIoStatement() for InputInteger";
471 for (int k{0}; k < j; ++k) {
472 ASSERT_EQ(buffer[k], check[k])
473 << "Read back [" << k << "]=" << buffer[k]
474 << " from sequential variable formatted record " << j << ", expected "
475 << buffer[k] << '\n';
478 // BACKSPACE(unit)
479 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
480 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
481 << "EndIoStatement() for Backspace (after read)";
484 // CLOSE(UNIT=unit,STATUS='DELETE')
485 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
486 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
487 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
488 << "EndIoStatement() for Close";
491 TEST(ExternalIOTests, TestNonAvancingInput) {
492 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
493 // FORM='FORMATTED',STATUS='SCRATCH')
494 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
495 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
496 << "SetAccess(SEQUENTIAL)";
497 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
498 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
499 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
501 int unit{-1};
502 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
503 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
504 << "EndIoStatement() for OpenNewUnit";
506 // Write the file to be used for the input test.
507 static constexpr std::string_view records[] = {
508 "ABCDEFGH", "IJKLMNOP", "QRSTUVWX"};
509 static constexpr std::string_view fmt{"(A)"};
510 for (const auto &record : records) {
511 // WRITE(UNIT=unit,FMT=fmt) record
512 io = IONAME(BeginExternalFormattedOutput)(
513 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
514 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length()))
515 << "OutputAscii()";
516 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
517 << "EndIoStatement() for OutputAscii";
520 // REWIND(UNIT=unit)
521 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
522 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
523 << "EndIoStatement() for Rewind";
525 struct TestItems {
526 std::string item;
527 int expectedIoStat;
528 std::string expectedItemValue[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'
541 int j{0};
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;
549 bool result{
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;
557 j++;
560 // REWIND(UNIT=unit)
561 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
562 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
563 << "EndIoStatement() for Rewind";
565 // Test again with PAD='YES'
566 j = 0;
567 for (auto &inputItem : inputItems) {
568 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat)
569 // inputItem
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;
575 bool result{
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;
583 j++;
586 // CLOSE(UNIT=unit)
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)";
602 int unit{-1};
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()))
615 << "OutputAscii()";
616 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
617 << "EndIoStatement() for OutputAscii";
620 // REWIND(UNIT=unit)
621 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
622 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
623 << "EndIoStatement() for Rewind";
625 struct TestItems {
626 std::string item;
627 int expectedIoStat;
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"},
636 int j{0};
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;
643 ASSERT_TRUE(
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;
650 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()))
659 << "OutputAscii()";
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.
666 // REWIND(UNIT=unit)
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);
677 ASSERT_TRUE(
678 IONAME(InputAscii)(io, resultRecord.data(), resultRecord.length()))
679 << "InputAscii() ";
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";
684 // CLOSE(UNIT=unit)
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)";
699 int unit{-1};
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";
710 // ENDFILE(unit)
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";
721 // BACKSPACE(unit)
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";
731 // REWIND(unit)
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";
747 // CLOSE(UNIT=unit)
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)";
764 int unit{-1};
765 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
766 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
767 << "EndIoStatement() for first OPEN";
768 char buffer[12];
769 std::memcpy(buffer,
770 "abc\x80\xff"
771 "de\0\0\0\0\0",
772 12);
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);
778 desc.Check();
779 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc));
780 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
781 << "EndIoStatement() for WRITE";
782 // REWIND(unit)
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);
788 desc.Check();
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,
794 "abc\x80\xff"
795 "de ",
796 12),
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"
822 "de ",
823 12),
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)";
843 int unit{-1};
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"
848 "de"};
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),
854 wbuffer, 0);
855 desc.Check();
856 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc));
857 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
858 << "EndIoStatement() for WRITE";
859 // REWIND(unit)
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);
866 desc.Check();
867 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
868 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
869 << "EndIoStatement() for first READ";
870 char dump[80];
871 dump[0] = '\0';
872 for (int j{0}; j < 8; ++j) {
873 std::size_t dumpLen{std::strlen(dump)};
874 std::snprintf(
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";
902 char buffer[12];
903 // READ(unit, *) buffer
904 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
905 desc.Establish(TypeCode{CFI_type_char}, sizeof buffer, buffer, 0);
906 desc.Check();
907 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
908 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
909 << "EndIoStatement() for second READ";
910 dump[0] = '\0';
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"
918 "de ",
919 12),
921 << dump;
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);
938 EXPECT_EQ(
939 IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow);
940 EXPECT_EQ(
941 IONAME(CheckUnitNumberInRange64)(unit64Bad2, true), IostatUnitOverflow);
942 EXPECT_EQ(
943 IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow);
944 EXPECT_EQ(
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));
951 EXPECT_DEATH(
952 IONAME(CheckUnitNumberInRange64)(2147483648, false), expectedMsg);
953 for (auto i{std::strlen(expectedMsg)}; i < n; ++i) {
954 expectedMsg[i] = ' ';
956 char msg[n + 1];
957 msg[n] = '\0';
958 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Bad, true, msg, n),
959 IostatUnitOverflow);
960 EXPECT_EQ(std::strncmp(msg, expectedMsg, n), 0);