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