1//===-- flang/unittests/RuntimeGTest/ExternalIOTest.cpp ---------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Sanity test for all external I/O modes
10//
11//===----------------------------------------------------------------------===//
12
13#include "CrashHandlerFixture.h"
14#include "gtest/gtest.h"
15#include "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>
22
23using namespace Fortran::runtime;
24using namespace Fortran::runtime::io;
25
26struct ExternalIOTests : public CrashHandlerFixture {};
27
28TEST(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)";
35
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)";
40
41 int unit{-1};
42 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
43 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
44 << "EndIoStatement() for OpenNewUnit";
45
46 StaticDescriptor<0> staticDescriptor;
47 Descriptor &desc{staticDescriptor.descriptor()};
48 desc.Establish(TypeCode{CFI_type_int8_t}, recl, &buffer, 0);
49 desc.Check();
50
51 // INQUIRE(IOLENGTH=) j
52 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
53 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
54 << "OutputDescriptor() for InquireIoLength";
55 ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength";
56 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
57 << "EndIoStatement() for InquireIoLength";
58
59 static constexpr int records{10};
60 for (int j{1}; j <= records; ++j) {
61 // WRITE(UNIT=unit,REC=j) j
62 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
63 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
64
65 buffer = j;
66 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
67 << "OutputDescriptor() for Write";
68
69 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
70 << "EndIoStatement() for Write";
71 }
72
73 for (int j{records}; j >= 1; --j) {
74 buffer = -1;
75 // READ(UNIT=unit,REC=j) n
76 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
77 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
78 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
79 << "InputDescriptor() for Read";
80
81 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
82 << "EndIoStatement() for Read";
83
84 ASSERT_EQ(buffer, j) << "Read back " << buffer
85 << " from direct unformatted record " << j
86 << ", expected " << j << '\n';
87 }
88 // CLOSE(UNIT=unit,STATUS='DELETE')
89 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
90 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
91 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
92 << "EndIoStatement() for Close";
93}
94
95TEST(ExternalIOTests, TestDirectUnformattedSwapped) {
96 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
97 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE')
98 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
99 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
100 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
101 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
102 ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)";
103
104 std::int64_t buffer;
105 static constexpr std::size_t recl{sizeof buffer};
106 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
107 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
108
109 int unit{-1};
110 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
111 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
112 << "EndIoStatement() for OpenNewUnit";
113
114 StaticDescriptor<0> staticDescriptor;
115 Descriptor &desc{staticDescriptor.descriptor()};
116 desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0);
117 desc.Check();
118
119 static constexpr int records{10};
120 for (int j{1}; j <= records; ++j) {
121 // WRITE(UNIT=unit,REC=j) j
122 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
123 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
124 buffer = j;
125 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
126 << "OutputDescriptor() for Write";
127 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
128 << "EndIoStatement() for Write";
129 }
130
131 // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP')
132 io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__);
133 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
134 ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)";
135 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
136 << "EndIoStatement() for OpenUnit";
137
138 for (int j{records}; j >= 1; --j) {
139 // READ(UNIT=unit,REC=j) n
140 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
141 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
142 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
143 << "InputDescriptor() for Read";
144 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
145 << "EndIoStatement() for Read";
146 ASSERT_EQ(buffer >> 56, j)
147 << "Read back " << (buffer >> 56) << " from direct unformatted record "
148 << j << ", expected " << j << '\n';
149 }
150
151 // CLOSE(UNIT=unit,STATUS='DELETE')
152 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
153 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
154 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
155 << "EndIoStatement() for Close";
156}
157
158TEST(ExternalIOTests, TestSequentialFixedUnformatted) {
159 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
160 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
161 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
162 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
163 << "SetAccess(SEQUENTIAL)";
164 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
165 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
166
167 std::int64_t buffer;
168 static constexpr std::size_t recl{sizeof buffer};
169
170 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
171 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
172
173 int unit{-1};
174 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
175 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
176 << "EndIoStatement() for OpenNewUnit";
177
178 // INQUIRE(IOLENGTH=) j, ...
179 StaticDescriptor<0> staticDescriptor;
180 Descriptor &desc{staticDescriptor.descriptor()};
181 desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0);
182 desc.Dump(stderr);
183 desc.Check();
184 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
185 for (int j{1}; j <= 3; ++j) {
186 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
187 << "OutputDescriptor() for InquireIoLength";
188 }
189 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
190 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
191 << "EndIoStatement() for InquireIoLength";
192
193 static const int records{10};
194 for (int j{1}; j <= records; ++j) {
195 // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO
196 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
197 buffer = j;
198 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
199 << "OutputDescriptor() for Write";
200 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
201 << "EndIoStatement() for WRITE";
202 }
203
204 // REWIND(UNIT=unit)
205 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
206 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
207 << "EndIoStatement() for Rewind";
208
209 for (int j{1}; j <= records; ++j) {
210 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
211 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
212 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
213 << "InputDescriptor() for Read";
214 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
215 << "EndIoStatement() for Read";
216 ASSERT_EQ(buffer, j) << "Read back " << buffer
217 << " from sequential fixed unformatted record " << j
218 << ", expected " << j << '\n';
219 }
220
221 for (int j{records}; j >= 1; --j) {
222 // BACKSPACE(UNIT=unit)
223 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
224 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
225 << "EndIoStatement() for Backspace (before read)";
226 // READ(UNIT=unit) n
227 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
228 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
229 << "InputDescriptor() for Read";
230 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
231 << "EndIoStatement() for Read";
232 ASSERT_EQ(buffer, j) << "Read back " << buffer
233 << " from sequential fixed unformatted record " << j
234 << " after backspacing, expected " << j << '\n';
235 // BACKSPACE(UNIT=unit)
236 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
237 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
238 << "EndIoStatement() for Backspace (after read)";
239 }
240
241 // CLOSE(UNIT=unit,STATUS='DELETE')
242 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
243 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
244 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
245 << "EndIoStatement() for Close";
246}
247
248TEST(ExternalIOTests, TestSequentialVariableUnformatted) {
249 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
250 // FORM='UNFORMATTED',STATUS='SCRATCH')
251 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
252
253 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
254 << "SetAccess(SEQUENTIAL)";
255 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
256 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
257 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
258
259 int unit{-1};
260 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
261 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
262 << "EndIoStatement() for OpenNewUnit";
263
264 static const int records{10};
265 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
266 for (int j{0}; j < records; ++j) {
267 buffer[j] = j;
268 }
269
270 StaticDescriptor<0> staticDescriptor;
271 Descriptor &desc{staticDescriptor.descriptor()};
272
273 for (int j{1}; j <= records; ++j) {
274 // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO
275 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
276 desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0);
277 desc.Check();
278 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
279 << "OutputDescriptor() for Write";
280 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
281 << "EndIoStatement() for Write";
282 }
283
284 // REWIND(UNIT=unit)
285 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
286 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
287 << "EndIoStatement() for Rewind";
288 for (int j{1}; j <= records; ++j) {
289 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
290 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
291 desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0);
292 desc.Check();
293 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
294 << "InputDescriptor() for Read";
295 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
296 << "EndIoStatement() for Read";
297 for (int k{0}; k < j; ++k) {
298 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
299 << " from direct unformatted record " << j
300 << ", expected " << k << '\n';
301 }
302 }
303
304 for (int j{records}; j >= 1; --j) {
305 // BACKSPACE(unit)
306 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
307 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
308 << "EndIoStatement() for Backspace (before read)";
309 // READ(unit=unit) n; check
310 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
311 desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0);
312 desc.Check();
313 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)) << "InputDescriptor()";
314 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
315 << "EndIoStatement() for InputUnformattedBlock";
316 for (int k{0}; k < j; ++k) {
317 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
318 << " from sequential variable unformatted record "
319 << j << ", expected " << k << '\n';
320 }
321 // BACKSPACE(unit)
322 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
323 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
324 << "EndIoStatement() for Backspace (after read)";
325 }
326
327 // CLOSE(UNIT=unit,STATUS='DELETE')
328 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
329 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
330 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
331 << "EndIoStatement() for Close";
332}
333
334TEST(ExternalIOTests, TestDirectFormatted) {
335 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
336 // FORM='FORMATTED',RECL=8,STATUS='SCRATCH')
337 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
338 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
339 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
340 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
341
342 static constexpr std::size_t recl{8};
343 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
344 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
345
346 int unit{-1};
347 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
348 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
349 << "EndIoStatement() for OpenNewUnit";
350
351 static constexpr int records{10};
352 static const char fmt[]{"(I4)"};
353 for (int j{1}; j <= records; ++j) {
354 // WRITE(UNIT=unit,FMT=fmt,REC=j) j
355 io = IONAME(BeginExternalFormattedOutput)(
356 fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__);
357 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
358 ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()";
359 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
360 << "EndIoStatement() for OutputInteger64";
361 }
362
363 for (int j{records}; j >= 1; --j) {
364 // READ(UNIT=unit,FMT=fmt,REC=j) n
365 io = IONAME(BeginExternalFormattedInput)(
366 fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__);
367 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
368 std::int64_t buffer;
369 ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()";
370 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
371 << "EndIoStatement() for InputInteger";
372
373 ASSERT_EQ(buffer, j) << "Read back " << buffer
374 << " from direct formatted record " << j
375 << ", expected " << j << '\n';
376 }
377
378 // CLOSE(UNIT=unit,STATUS='DELETE')
379 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
380 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
381 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
382 << "EndIoStatement() for Close";
383}
384
385TEST(ExternalIOTests, TestSequentialVariableFormatted) {
386 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
387 // FORM='FORMATTED',STATUS='SCRATCH')
388 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
389 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
390 << "SetAccess(SEQUENTIAL)";
391 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
392 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
393 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
394
395 int unit{-1};
396 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
397 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
398 << "EndIoStatement() for OpenNewUnit";
399
400 static const int records{10};
401 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
402 for (int j{0}; j < records; ++j) {
403 buffer[j] = j;
404 }
405
406 char fmt[32];
407 for (int j{1}; j <= records; ++j) {
408 std::snprintf(s: fmt, maxlen: sizeof fmt, format: "(%dI4)", j);
409 // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO
410 io = IONAME(BeginExternalFormattedOutput)(
411 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
412 for (int k{0}; k < j; ++k) {
413 ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k]))
414 << "OutputInteger64()";
415 }
416 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
417 << "EndIoStatement() for OutputInteger64";
418 }
419
420 // REWIND(UNIT=unit)
421 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
422 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
423 << "EndIoStatement() for Rewind";
424
425 for (int j{1}; j <= records; ++j) {
426 std::snprintf(s: fmt, maxlen: sizeof fmt, format: "(%dI4)", j);
427 // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO
428 io = IONAME(BeginExternalFormattedInput)(
429 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
430
431 std::int64_t check[records];
432 for (int k{0}; k < j; ++k) {
433 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
434 }
435 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
436 << "EndIoStatement() for InputInteger";
437
438 for (int k{0}; k < j; ++k) {
439 ASSERT_EQ(buffer[k], check[k])
440 << "Read back [" << k << "]=" << check[k]
441 << " from sequential variable formatted record " << j << ", expected "
442 << buffer[k] << '\n';
443 }
444 }
445
446 for (int j{records}; j >= 1; --j) {
447 // BACKSPACE(unit)
448 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
449 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
450 << "EndIoStatement() for Backspace (before read)";
451
452 std::snprintf(s: fmt, maxlen: sizeof fmt, format: "(%dI4)", j);
453 // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check
454 io = IONAME(BeginExternalFormattedInput)(
455 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
456
457 std::int64_t check[records];
458 for (int k{0}; k < j; ++k) {
459 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
460 }
461
462 std::size_t chars{IONAME(GetSize)(io)};
463 ASSERT_EQ(chars, j * 4u)
464 << "GetSize()=" << chars << ", expected " << (j * 4u) << '\n';
465 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
466 << "EndIoStatement() for InputInteger";
467 for (int k{0}; k < j; ++k) {
468 ASSERT_EQ(buffer[k], check[k])
469 << "Read back [" << k << "]=" << buffer[k]
470 << " from sequential variable formatted record " << j << ", expected "
471 << buffer[k] << '\n';
472 }
473
474 // BACKSPACE(unit)
475 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
476 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
477 << "EndIoStatement() for Backspace (after read)";
478 }
479
480 // CLOSE(UNIT=unit,STATUS='DELETE')
481 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
482 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
483 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
484 << "EndIoStatement() for Close";
485}
486
487TEST(ExternalIOTests, TestNonAvancingInput) {
488 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
489 // FORM='FORMATTED',STATUS='SCRATCH')
490 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
491 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
492 << "SetAccess(SEQUENTIAL)";
493 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
494 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
495 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
496
497 int unit{-1};
498 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
499 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
500 << "EndIoStatement() for OpenNewUnit";
501
502 // Write the file to be used for the input test.
503 static constexpr std::string_view records[] = {
504 "ABCDEFGH", "IJKLMNOP", "QRSTUVWX"};
505 static constexpr std::string_view fmt{"(A)"};
506 for (const auto &record : records) {
507 // WRITE(UNIT=unit,FMT=fmt) record
508 io = IONAME(BeginExternalFormattedOutput)(
509 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
510 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length()))
511 << "OutputAscii()";
512 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
513 << "EndIoStatement() for OutputAscii";
514 }
515
516 // REWIND(UNIT=unit)
517 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
518 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
519 << "EndIoStatement() for Rewind";
520
521 struct TestItems {
522 std::string item;
523 int expectedIoStat;
524 std::string expectedItemValue[2];
525 };
526 // Actual non advancing input IO test
527 TestItems inputItems[]{
528 {std::string(4, '+'), IostatOk, {"ABCD", "ABCD"}},
529 {std::string(4, '+'), IostatOk, {"EFGH", "EFGH"}},
530 {std::string(4, '+'), IostatEor, {"++++", " "}},
531 {std::string(2, '+'), IostatOk, {"IJ", "IJ"}},
532 {std::string(8, '+'), IostatEor, {"++++++++", "KLMNOP "}},
533 {std::string(10, '+'), IostatEor, {"++++++++++", "QRSTUVWX "}},
534 };
535
536 // Test with PAD='NO'
537 int j{0};
538 for (auto &inputItem : inputItems) {
539 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='NO', IOSTAT=iostat) inputItem
540 io = IONAME(BeginExternalFormattedInput)(
541 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
542 IONAME(EnableHandlers)(io, true, false, false, false, false);
543 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
544 ASSERT_TRUE(IONAME(SetPad)(io, "NO", 2)) << "SetPad(NO)" << j;
545 bool result{
546 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
547 ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
548 << "InputAscii() " << j;
549 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
550 << "EndIoStatement() for Read " << j;
551 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[0])
552 << "Input-item value after non advancing read " << j;
553 j++;
554 }
555
556 // REWIND(UNIT=unit)
557 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
558 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
559 << "EndIoStatement() for Rewind";
560
561 // Test again with PAD='YES'
562 j = 0;
563 for (auto &inputItem : inputItems) {
564 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat)
565 // inputItem
566 io = IONAME(BeginExternalFormattedInput)(
567 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
568 IONAME(EnableHandlers)(io, true, false, false, false, false);
569 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
570 ASSERT_TRUE(IONAME(SetPad)(io, "YES", 3)) << "SetPad(YES)" << j;
571 bool result{
572 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
573 ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
574 << "InputAscii() " << j;
575 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
576 << "EndIoStatement() for Read " << j;
577 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[1])
578 << "Input-item value after non advancing read " << j;
579 j++;
580 }
581
582 // CLOSE(UNIT=unit)
583 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
584 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
585 << "EndIoStatement() for Close";
586}
587
588TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) {
589 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
590 // FORM='FORMATTED',STATUS='SCRATCH')
591 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
592 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
593 << "SetAccess(SEQUENTIAL)";
594 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
595 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
596 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
597
598 int unit{-1};
599 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
600 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
601 << "EndIoStatement() for OpenNewUnit";
602
603 // Write the file to be used for the input test.
604 static constexpr std::string_view records[] = {"ABCDEFGHIJKLMNOPQRST"};
605 static constexpr std::string_view fmt{"(A)"};
606 for (const auto &record : records) {
607 // WRITE(UNIT=unit,FMT=fmt) record
608 io = IONAME(BeginExternalFormattedOutput)(
609 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
610 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length()))
611 << "OutputAscii()";
612 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
613 << "EndIoStatement() for OutputAscii";
614 }
615
616 // REWIND(UNIT=unit)
617 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
618 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
619 << "EndIoStatement() for Rewind";
620
621 struct TestItems {
622 std::string item;
623 int expectedIoStat;
624 std::string expectedItemValue;
625 };
626 // Actual non advancing input IO test
627 TestItems inputItems[]{
628 {std::string(4, '+'), IostatOk, "ABCD"},
629 {std::string(4, '+'), IostatOk, "EFGH"},
630 };
631
632 int j{0};
633 for (auto &inputItem : inputItems) {
634 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
635 io = IONAME(BeginExternalFormattedInput)(
636 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
637 IONAME(EnableHandlers)(io, true, false, false, false, false);
638 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
639 ASSERT_TRUE(
640 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length()))
641 << "InputAscii() " << j;
642 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
643 << "EndIoStatement() for Read " << j;
644 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue)
645 << "Input-item value after non advancing read " << j;
646 j++;
647 }
648
649 // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem.
650 static constexpr std::string_view outputItem{"XYZ"};
651 // WRITE(UNIT=unit,FMT=fmt) record
652 io = IONAME(BeginExternalFormattedOutput)(
653 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
654 ASSERT_TRUE(IONAME(OutputAscii)(io, outputItem.data(), outputItem.length()))
655 << "OutputAscii()";
656 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
657 << "EndIoStatement() for OutputAscii";
658
659 // Verify that the output was written in the record read in non advancing
660 // mode, after the read part, and that the end was truncated.
661
662 // REWIND(UNIT=unit)
663 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
664 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
665 << "EndIoStatement() for Rewind";
666
667 std::string resultRecord(20, '+');
668 std::string expectedRecord{"ABCDEFGHXYZ "};
669 // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result
670 io = IONAME(BeginExternalFormattedInput)(
671 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
672 IONAME(EnableHandlers)(io, true, false, false, false, false);
673 ASSERT_TRUE(
674 IONAME(InputAscii)(io, resultRecord.data(), resultRecord.length()))
675 << "InputAscii() ";
676 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
677 << "EndIoStatement() for Read ";
678 ASSERT_EQ(resultRecord, expectedRecord)
679 << "Record after non advancing read followed by write";
680 // CLOSE(UNIT=unit)
681 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
682 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
683 << "EndIoStatement() for Close";
684}
685
686TEST(ExternalIOTests, TestWriteAfterEndfile) {
687 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
688 // FORM='FORMATTED',STATUS='SCRATCH')
689 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
690 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
691 << "SetAccess(SEQUENTIAL)";
692 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
693 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
694 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
695 int unit{-1};
696 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
697 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
698 << "EndIoStatement() for OpenNewUnit";
699 // WRITE(unit,"(I8)") 1234
700 static constexpr std::string_view format{"(I8)"};
701 io = IONAME(BeginExternalFormattedOutput)(
702 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
703 ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()";
704 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
705 << "EndIoStatement for WRITE before ENDFILE";
706 // ENDFILE(unit)
707 io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__);
708 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
709 << "EndIoStatement for ENDFILE";
710 // WRITE(unit,"(I8)",iostat=iostat) 5678
711 io = IONAME(BeginExternalFormattedOutput)(
712 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
713 IONAME(EnableHandlers)(io, true /*IOSTAT=*/);
714 ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()";
715 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile)
716 << "EndIoStatement for WRITE after ENDFILE";
717 // BACKSPACE(unit)
718 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
719 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
720 << "EndIoStatement for BACKSPACE";
721 // WRITE(unit,"(I8)") 3456
722 io = IONAME(BeginExternalFormattedOutput)(
723 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
724 ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()";
725 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
726 << "EndIoStatement for WRITE after BACKSPACE";
727 // REWIND(unit)
728 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
729 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
730 << "EndIoStatement for REWIND";
731 // READ(unit,"(I8)",END=) j, k
732 std::int64_t j{-1}, k{-1}, eof{-1};
733 io = IONAME(BeginExternalFormattedInput)(
734 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
735 IONAME(EnableHandlers)(io, false, false, true /*END=*/);
736 ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)";
737 ASSERT_EQ(j, 1234) << "READ(j)";
738 ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)";
739 ASSERT_EQ(k, 3456) << "READ(k)";
740 ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)";
741 ASSERT_EQ(eof, -1) << "READ(eof)";
742 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ";
743 // CLOSE(UNIT=unit)
744 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
745 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
746 << "EndIoStatement() for Close";
747}
748
749TEST(ExternalIOTests, TestUTF8Encoding) {
750 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
751 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
752 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
753 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
754 << "SetAccess(SEQUENTIAL)";
755 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
756 ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)";
757 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
758 ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)";
759 ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)";
760 int unit{-1};
761 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
762 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
763 << "EndIoStatement() for first OPEN";
764 char buffer[12];
765 std::memcpy(dest: buffer,
766 src: "abc\x80\xff"
767 "de\0\0\0\0\0",
768 n: 12);
769 // WRITE(unit, *) buffer
770 io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__);
771 StaticDescriptor<0> staticDescriptor;
772 Descriptor &desc{staticDescriptor.descriptor()};
773 desc.Establish(TypeCode{CFI_type_char}, 7, buffer, 0);
774 desc.Check();
775 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc));
776 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
777 << "EndIoStatement() for WRITE";
778 // REWIND(unit)
779 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
780 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
781 << "EndIoStatement for REWIND";
782 // READ(unit, *) buffer
783 desc.Establish(TypeCode(CFI_type_char), sizeof buffer, buffer, 0);
784 desc.Check();
785 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
786 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
787 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
788 << "EndIoStatement() for first READ";
789 ASSERT_EQ(std::memcmp(buffer,
790 "abc\x80\xff"
791 "de ",
792 12),
793 0);
794 // CLOSE(UNIT=unit,STATUS='KEEP')
795 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
796 ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)";
797 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
798 << "EndIoStatement() for first CLOSE";
799 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
800 // FORM='FORMATTED',STATUS='OLD')
801 io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__);
802 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
803 << "SetAccess(SEQUENTIAL)";
804 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
805 ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)";
806 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
807 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
808 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
809 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
810 << "EndIoStatement() for second OPEN";
811 // READ(unit, *) buffer
812 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
813 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
814 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
815 << "EndIoStatement() for second READ";
816 ASSERT_EQ(std::memcmp(buffer,
817 "abc\xc2\x80\xc3\xbf"
818 "de ",
819 12),
820 0);
821 // CLOSE(UNIT=unit,STATUS='DELETE')
822 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
823 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
824 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
825 << "EndIoStatement() for second CLOSE";
826}
827
828TEST(ExternalIOTests, TestUCS) {
829 // OPEN(FILE="ucstest',NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
830 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
831 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
832 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
833 << "SetAccess(SEQUENTIAL)";
834 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
835 ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetAction(ucstest)";
836 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
837 ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)";
838 ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)";
839 int unit{-1};
840 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
841 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
842 << "EndIoStatement() for first OPEN";
843 char32_t wbuffer[8]{U"abc\u0080\uffff"
844 "de"};
845 // WRITE(unit, *) wbuffec
846 io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__);
847 StaticDescriptor<0> staticDescriptor;
848 Descriptor &desc{staticDescriptor.descriptor()};
849 desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer - sizeof(char32_t),
850 wbuffer, 0);
851 desc.Check();
852 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc));
853 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
854 << "EndIoStatement() for WRITE";
855 // REWIND(unit)
856 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
857 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
858 << "EndIoStatement for REWIND";
859 // READ(unit, *) buffer
860 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
861 desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer, wbuffer, 0);
862 desc.Check();
863 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
864 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
865 << "EndIoStatement() for first READ";
866 char dump[80];
867 dump[0] = '\0';
868 for (int j{0}; j < 8; ++j) {
869 std::size_t dumpLen{std::strlen(s: dump)};
870 std::snprintf(
871 s: dump + dumpLen, maxlen: sizeof dump - dumpLen, format: " %x", (unsigned)wbuffer[j]);
872 }
873 EXPECT_EQ(wbuffer[0], U'a') << dump;
874 EXPECT_EQ(wbuffer[1], U'b') << dump;
875 EXPECT_EQ(wbuffer[2], U'c') << dump;
876 EXPECT_EQ(wbuffer[3], U'\u0080') << dump;
877 EXPECT_EQ(wbuffer[4], U'\uffff') << dump;
878 EXPECT_EQ(wbuffer[5], U'd') << dump;
879 EXPECT_EQ(wbuffer[6], U'e') << dump;
880 EXPECT_EQ(wbuffer[7], U' ') << dump;
881 // CLOSE(UNIT=unit,STATUS='KEEP')
882 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
883 ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)";
884 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
885 << "EndIoStatement() for first CLOSE";
886 // OPEN(FILE="ucstest",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
887 // FORM='FORMATTED',STATUS='OLD')
888 io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__);
889 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
890 << "SetAccess(SEQUENTIAL)";
891 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
892 ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetFile(ucstest)";
893 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
894 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
895 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
896 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
897 << "EndIoStatement() for second OPEN";
898 char buffer[12];
899 // READ(unit, *) buffer
900 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
901 desc.Establish(TypeCode{CFI_type_char}, sizeof buffer, buffer, 0);
902 desc.Check();
903 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
904 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
905 << "EndIoStatement() for second READ";
906 dump[0] = '\0';
907 for (int j{0}; j < 12; ++j) {
908 std::size_t dumpLen{std::strlen(s: dump)};
909 std::snprintf(s: dump + dumpLen, maxlen: sizeof dump - dumpLen, format: " %x",
910 (unsigned)(unsigned char)buffer[j]);
911 }
912 EXPECT_EQ(std::memcmp(buffer,
913 "abc\xc2\x80\xef\xbf\xbf"
914 "de ",
915 12),
916 0)
917 << dump;
918 // CLOSE(UNIT=unit,STATUS='DELETE')
919 io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
920 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
921 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
922 << "EndIoStatement() for second CLOSE";
923}
924
925TEST(ExternalIOTests, BigUnitNumbers) {
926 if (std::numeric_limits<ExternalUnit>::max() <
927 std::numeric_limits<std::int64_t>::max()) {
928 std::int64_t unit64Ok = std::numeric_limits<ExternalUnit>::max();
929 std::int64_t unit64Bad = unit64Ok + 1;
930 std::int64_t unit64Bad2 =
931 static_cast<std::int64_t>(std::numeric_limits<ExternalUnit>::min()) - 1;
932 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, true), IostatOk);
933 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, false), IostatOk);
934 EXPECT_EQ(
935 IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow);
936 EXPECT_EQ(
937 IONAME(CheckUnitNumberInRange64)(unit64Bad2, true), IostatUnitOverflow);
938 constexpr std::size_t n{80};
939 char expectedMsg[n + 1];
940 expectedMsg[n] = '\0';
941 std::snprintf(s: expectedMsg, maxlen: n, format: "UNIT number %jd is out of range",
942 static_cast<std::intmax_t>(unit64Bad));
943 EXPECT_DEATH(
944 IONAME(CheckUnitNumberInRange64)(unit64Bad, false), expectedMsg);
945 for (auto i{std::strlen(s: expectedMsg)}; i < n; ++i) {
946 expectedMsg[i] = ' ';
947 }
948 char msg[n + 1];
949 msg[n] = '\0';
950 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Bad, true, msg, n),
951 IostatUnitOverflow);
952 EXPECT_EQ(std::strncmp(msg, expectedMsg, n), 0);
953 }
954}
955

source code of flang/unittests/Runtime/ExternalIOTest.cpp