brintos

brintos / llvm-project-archived public Read only

0
0
Text · 39.2 KiB · 6421194 Raw
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