
Recursion, both direct and indirect, prevents accurate stack size calculation at link time for GPU device code. Restructure these recursive (often mutually so) routines in the Fortran runtime with new implementations based on an iterative work queue with suspendable/resumable work tickets: Assign, Initialize, initializeClone, Finalize, and Destroy. Default derived type I/O is also recursive, but already disabled. It can be added to this new framework later if the overall approach succeeds. Note that derived type FINAL subroutine calls, defined assignments, and defined I/O procedures all perform callbacks into user code, which may well reenter the runtime library. This kind of recursion is not handled by this change, although it may be possible to do so in the future using thread-local work queues. (Relanding this patch after reverting initial attempt due to some test failures that needed some time to analyze and fix.) Fixes https://github.com/llvm/llvm-project/issues/142481.
955 lines
39 KiB
C++
955 lines
39 KiB
C++
//===-- unittests/Runtime/ExternalIOTest.cpp --------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Sanity test for all external I/O modes
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CrashHandlerFixture.h"
|
|
#include "gtest/gtest.h"
|
|
#include "flang-rt/runtime/descriptor.h"
|
|
#include "flang/Runtime/io-api.h"
|
|
#include "flang/Runtime/main.h"
|
|
#include "flang/Runtime/stop.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cstring>
|
|
#include <string_view>
|
|
|
|
using namespace Fortran::runtime;
|
|
using namespace Fortran::runtime::io;
|
|
|
|
struct ExternalIOTests : public CrashHandlerFixture {};
|
|
|
|
TEST(ExternalIOTests, TestDirectUnformatted) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
|
|
// FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
|
|
Cookie io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
|
|
|
|
std::int64_t buffer;
|
|
static constexpr std::size_t recl{sizeof buffer};
|
|
ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
|
|
StaticDescriptor<0> staticDescriptor;
|
|
Descriptor &desc{staticDescriptor.descriptor()};
|
|
desc.Establish(TypeCode{CFI_type_int8_t}, recl, &buffer, 0);
|
|
desc.Check();
|
|
|
|
// INQUIRE(IOLENGTH=) j
|
|
io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
|
|
<< "OutputDescriptor() for InquireIoLength";
|
|
ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InquireIoLength";
|
|
|
|
static constexpr int records{10};
|
|
for (int j{1}; j <= records; ++j) {
|
|
// WRITE(UNIT=unit,REC=j) j
|
|
io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
|
|
|
|
buffer = j;
|
|
ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
|
|
<< "OutputDescriptor() for Write";
|
|
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Write";
|
|
}
|
|
|
|
for (int j{records}; j >= 1; --j) {
|
|
buffer = -1;
|
|
// READ(UNIT=unit,REC=j) n
|
|
io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
|
|
<< "InputDescriptor() for Read";
|
|
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Read";
|
|
|
|
ASSERT_EQ(buffer, j) << "Read back " << buffer
|
|
<< " from direct unformatted record " << j
|
|
<< ", expected " << j << '\n';
|
|
}
|
|
// CLOSE(UNIT=unit,STATUS='DELETE')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestDirectUnformattedSwapped) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
|
|
// FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)";
|
|
|
|
std::int64_t buffer;
|
|
static constexpr std::size_t recl{sizeof buffer};
|
|
ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
|
|
StaticDescriptor<0> staticDescriptor;
|
|
Descriptor &desc{staticDescriptor.descriptor()};
|
|
desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0);
|
|
desc.Check();
|
|
|
|
static constexpr int records{10};
|
|
for (int j{1}; j <= records; ++j) {
|
|
// WRITE(UNIT=unit,REC=j) j
|
|
io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
|
|
buffer = j;
|
|
ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
|
|
<< "OutputDescriptor() for Write";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Write";
|
|
}
|
|
|
|
// OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP')
|
|
io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
|
|
ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenUnit";
|
|
|
|
for (int j{records}; j >= 1; --j) {
|
|
// READ(UNIT=unit,REC=j) n
|
|
io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
|
|
<< "InputDescriptor() for Read";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Read";
|
|
ASSERT_EQ(buffer >> 56, j)
|
|
<< "Read back " << (buffer >> 56) << " from direct unformatted record "
|
|
<< j << ", expected " << j << '\n';
|
|
}
|
|
|
|
// CLOSE(UNIT=unit,STATUS='DELETE')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestSequentialFixedUnformatted) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
|
|
|
|
std::int64_t buffer;
|
|
static constexpr std::size_t recl{sizeof buffer};
|
|
|
|
ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
|
|
// INQUIRE(IOLENGTH=) j, ...
|
|
StaticDescriptor<0> staticDescriptor;
|
|
Descriptor &desc{staticDescriptor.descriptor()};
|
|
desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0);
|
|
desc.Dump(stderr);
|
|
desc.Check();
|
|
io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
|
|
for (int j{1}; j <= 3; ++j) {
|
|
ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
|
|
<< "OutputDescriptor() for InquireIoLength " << j;
|
|
}
|
|
ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InquireIoLength";
|
|
|
|
static const int records{10};
|
|
for (int j{1}; j <= records; ++j) {
|
|
// DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO
|
|
io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
|
|
buffer = j;
|
|
ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
|
|
<< "OutputDescriptor() for Write";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for WRITE";
|
|
}
|
|
|
|
// REWIND(UNIT=unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Rewind";
|
|
|
|
for (int j{1}; j <= records; ++j) {
|
|
// DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
|
|
io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
|
|
<< "InputDescriptor() for Read";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Read";
|
|
ASSERT_EQ(buffer, j) << "Read back " << buffer
|
|
<< " from sequential fixed unformatted record " << j
|
|
<< ", expected " << j << '\n';
|
|
}
|
|
|
|
for (int j{records}; j >= 1; --j) {
|
|
// BACKSPACE(UNIT=unit)
|
|
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Backspace (before read)";
|
|
// READ(UNIT=unit) n
|
|
io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
|
|
<< "InputDescriptor() for Read";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Read";
|
|
ASSERT_EQ(buffer, j) << "Read back " << buffer
|
|
<< " from sequential fixed unformatted record " << j
|
|
<< " after backspacing, expected " << j << '\n';
|
|
// BACKSPACE(UNIT=unit)
|
|
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Backspace (after read)";
|
|
}
|
|
|
|
// CLOSE(UNIT=unit,STATUS='DELETE')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestSequentialVariableUnformatted) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='UNFORMATTED',STATUS='SCRATCH')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
|
|
static const int records{10};
|
|
std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
|
|
for (int j{0}; j < records; ++j) {
|
|
buffer[j] = j;
|
|
}
|
|
|
|
StaticDescriptor<0> staticDescriptor;
|
|
Descriptor &desc{staticDescriptor.descriptor()};
|
|
|
|
for (int j{1}; j <= records; ++j) {
|
|
// DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO
|
|
io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
|
|
desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0);
|
|
desc.Check();
|
|
ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc))
|
|
<< "OutputDescriptor() for Write";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Write";
|
|
}
|
|
|
|
// REWIND(UNIT=unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Rewind";
|
|
for (int j{1}; j <= records; ++j) {
|
|
// DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
|
|
io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
|
|
desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0);
|
|
desc.Check();
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc))
|
|
<< "InputDescriptor() for Read";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Read";
|
|
for (int k{0}; k < j; ++k) {
|
|
ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
|
|
<< " from direct unformatted record " << j
|
|
<< ", expected " << k << '\n';
|
|
}
|
|
}
|
|
|
|
for (int j{records}; j >= 1; --j) {
|
|
// BACKSPACE(unit)
|
|
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Backspace (before read)";
|
|
// READ(unit=unit) n; check
|
|
io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
|
|
desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0);
|
|
desc.Check();
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)) << "InputDescriptor()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InputUnformattedBlock";
|
|
for (int k{0}; k < j; ++k) {
|
|
ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k]
|
|
<< " from sequential variable unformatted record "
|
|
<< j << ", expected " << k << '\n';
|
|
}
|
|
// BACKSPACE(unit)
|
|
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Backspace (after read)";
|
|
}
|
|
|
|
// CLOSE(UNIT=unit,STATUS='DELETE')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestDirectFormatted) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',RECL=8,STATUS='SCRATCH')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
|
|
static constexpr std::size_t recl{8};
|
|
ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
|
|
static constexpr int records{10};
|
|
static const char fmt[]{"(I4)"};
|
|
for (int j{1}; j <= records; ++j) {
|
|
// WRITE(UNIT=unit,FMT=fmt,REC=j) j
|
|
io = IONAME(BeginExternalFormattedOutput)(
|
|
fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
|
|
ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputInteger64";
|
|
}
|
|
|
|
for (int j{records}; j >= 1; --j) {
|
|
// READ(UNIT=unit,FMT=fmt,REC=j) n
|
|
io = IONAME(BeginExternalFormattedInput)(
|
|
fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')';
|
|
std::int64_t buffer;
|
|
ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InputInteger";
|
|
|
|
ASSERT_EQ(buffer, j) << "Read back " << buffer
|
|
<< " from direct formatted record " << j
|
|
<< ", expected " << j << '\n';
|
|
}
|
|
|
|
// CLOSE(UNIT=unit,STATUS='DELETE')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestSequentialVariableFormatted) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',STATUS='SCRATCH')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
|
|
static const int records{10};
|
|
std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)]
|
|
for (int j{0}; j < records; ++j) {
|
|
buffer[j] = j;
|
|
}
|
|
|
|
char fmt[32];
|
|
for (int j{1}; j <= records; ++j) {
|
|
std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
|
|
// DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO
|
|
io = IONAME(BeginExternalFormattedOutput)(
|
|
fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
|
|
for (int k{0}; k < j; ++k) {
|
|
ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k]))
|
|
<< "OutputInteger64()";
|
|
}
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputInteger64";
|
|
}
|
|
|
|
// REWIND(UNIT=unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Rewind";
|
|
|
|
for (int j{1}; j <= records; ++j) {
|
|
std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
|
|
// DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO
|
|
io = IONAME(BeginExternalFormattedInput)(
|
|
fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
|
|
|
|
std::int64_t check[records];
|
|
for (int k{0}; k < j; ++k) {
|
|
ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
|
|
}
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InputInteger";
|
|
|
|
for (int k{0}; k < j; ++k) {
|
|
ASSERT_EQ(buffer[k], check[k])
|
|
<< "Read back [" << k << "]=" << check[k]
|
|
<< " from sequential variable formatted record " << j << ", expected "
|
|
<< buffer[k] << '\n';
|
|
}
|
|
}
|
|
|
|
for (int j{records}; j >= 1; --j) {
|
|
// BACKSPACE(unit)
|
|
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Backspace (before read)";
|
|
|
|
std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
|
|
// READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check
|
|
io = IONAME(BeginExternalFormattedInput)(
|
|
fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__);
|
|
|
|
std::int64_t check[records];
|
|
for (int k{0}; k < j; ++k) {
|
|
ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()";
|
|
}
|
|
|
|
std::size_t chars{IONAME(GetSize)(io)};
|
|
ASSERT_EQ(chars, j * 4u)
|
|
<< "GetSize()=" << chars << ", expected " << (j * 4u) << '\n';
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InputInteger";
|
|
for (int k{0}; k < j; ++k) {
|
|
ASSERT_EQ(buffer[k], check[k])
|
|
<< "Read back [" << k << "]=" << buffer[k]
|
|
<< " from sequential variable formatted record " << j << ", expected "
|
|
<< buffer[k] << '\n';
|
|
}
|
|
|
|
// BACKSPACE(unit)
|
|
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Backspace (after read)";
|
|
}
|
|
|
|
// CLOSE(UNIT=unit,STATUS='DELETE')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestNonAvancingInput) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',STATUS='SCRATCH')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
|
|
// Write the file to be used for the input test.
|
|
static constexpr std::string_view records[] = {
|
|
"ABCDEFGH", "IJKLMNOP", "QRSTUVWX"};
|
|
static constexpr std::string_view fmt{"(A)"};
|
|
for (const auto &record : records) {
|
|
// WRITE(UNIT=unit,FMT=fmt) record
|
|
io = IONAME(BeginExternalFormattedOutput)(
|
|
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length()))
|
|
<< "OutputAscii()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputAscii";
|
|
}
|
|
|
|
// REWIND(UNIT=unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Rewind";
|
|
|
|
struct TestItems {
|
|
std::string item;
|
|
int expectedIoStat;
|
|
std::string expectedItemValue[2];
|
|
};
|
|
// Actual non advancing input IO test
|
|
TestItems inputItems[]{
|
|
{std::string(4, '+'), IostatOk, {"ABCD", "ABCD"}},
|
|
{std::string(4, '+'), IostatOk, {"EFGH", "EFGH"}},
|
|
{std::string(4, '+'), IostatEor, {"++++", " "}},
|
|
{std::string(2, '+'), IostatOk, {"IJ", "IJ"}},
|
|
{std::string(8, '+'), IostatEor, {"++++++++", "KLMNOP "}},
|
|
{std::string(10, '+'), IostatEor, {"++++++++++", "QRSTUVWX "}},
|
|
};
|
|
|
|
// Test with PAD='NO'
|
|
int j{0};
|
|
for (auto &inputItem : inputItems) {
|
|
// READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='NO', IOSTAT=iostat) inputItem
|
|
io = IONAME(BeginExternalFormattedInput)(
|
|
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
|
|
IONAME(EnableHandlers)(io, true, false, false, false, false);
|
|
ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
|
|
ASSERT_TRUE(IONAME(SetPad)(io, "NO", 2)) << "SetPad(NO)" << j;
|
|
bool result{
|
|
IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
|
|
ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
|
|
<< "InputAscii() " << j;
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
|
|
<< "EndIoStatement() for Read " << j;
|
|
ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[0])
|
|
<< "Input-item value after non advancing read " << j;
|
|
j++;
|
|
}
|
|
|
|
// REWIND(UNIT=unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Rewind";
|
|
|
|
// Test again with PAD='YES'
|
|
j = 0;
|
|
for (auto &inputItem : inputItems) {
|
|
// READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat)
|
|
// inputItem
|
|
io = IONAME(BeginExternalFormattedInput)(
|
|
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
|
|
IONAME(EnableHandlers)(io, true, false, false, false, false);
|
|
ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
|
|
ASSERT_TRUE(IONAME(SetPad)(io, "YES", 3)) << "SetPad(YES)" << j;
|
|
bool result{
|
|
IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
|
|
ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
|
|
<< "InputAscii() " << j;
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
|
|
<< "EndIoStatement() for Read " << j;
|
|
ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[1])
|
|
<< "Input-item value after non advancing read " << j;
|
|
j++;
|
|
}
|
|
|
|
// CLOSE(UNIT=unit)
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',STATUS='SCRATCH')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
|
|
// Write the file to be used for the input test.
|
|
static constexpr std::string_view records[] = {"ABCDEFGHIJKLMNOPQRST"};
|
|
static constexpr std::string_view fmt{"(A)"};
|
|
for (const auto &record : records) {
|
|
// WRITE(UNIT=unit,FMT=fmt) record
|
|
io = IONAME(BeginExternalFormattedOutput)(
|
|
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length()))
|
|
<< "OutputAscii()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputAscii";
|
|
}
|
|
|
|
// REWIND(UNIT=unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Rewind";
|
|
|
|
struct TestItems {
|
|
std::string item;
|
|
int expectedIoStat;
|
|
std::string expectedItemValue;
|
|
};
|
|
// Actual non advancing input IO test
|
|
TestItems inputItems[]{
|
|
{std::string(4, '+'), IostatOk, "ABCD"},
|
|
{std::string(4, '+'), IostatOk, "EFGH"},
|
|
};
|
|
|
|
int j{0};
|
|
for (auto &inputItem : inputItems) {
|
|
// READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
|
|
io = IONAME(BeginExternalFormattedInput)(
|
|
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
|
|
IONAME(EnableHandlers)(io, true, false, false, false, false);
|
|
ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
|
|
ASSERT_TRUE(
|
|
IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length()))
|
|
<< "InputAscii() " << j;
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
|
|
<< "EndIoStatement() for Read " << j;
|
|
ASSERT_EQ(inputItem.item, inputItem.expectedItemValue)
|
|
<< "Input-item value after non advancing read " << j;
|
|
j++;
|
|
}
|
|
|
|
// WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem.
|
|
static constexpr std::string_view outputItem{"XYZ"};
|
|
// WRITE(UNIT=unit,FMT=fmt) record
|
|
io = IONAME(BeginExternalFormattedOutput)(
|
|
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(OutputAscii)(io, outputItem.data(), outputItem.length()))
|
|
<< "OutputAscii()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputAscii";
|
|
|
|
// Verify that the output was written in the record read in non advancing
|
|
// mode, after the read part, and that the end was truncated.
|
|
|
|
// REWIND(UNIT=unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Rewind";
|
|
|
|
std::string resultRecord(20, '+');
|
|
std::string expectedRecord{"ABCDEFGHXYZ "};
|
|
// READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result
|
|
io = IONAME(BeginExternalFormattedInput)(
|
|
fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
|
|
IONAME(EnableHandlers)(io, true, false, false, false, false);
|
|
ASSERT_TRUE(
|
|
IONAME(InputAscii)(io, resultRecord.data(), resultRecord.length()))
|
|
<< "InputAscii() ";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Read ";
|
|
ASSERT_EQ(resultRecord, expectedRecord)
|
|
<< "Record after non advancing read followed by write";
|
|
// CLOSE(UNIT=unit)
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestWriteAfterEndfile) {
|
|
// OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',STATUS='SCRATCH')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)";
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OpenNewUnit";
|
|
// WRITE(unit,"(I8)") 1234
|
|
static constexpr std::string_view format{"(I8)"};
|
|
io = IONAME(BeginExternalFormattedOutput)(
|
|
format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement for WRITE before ENDFILE";
|
|
// ENDFILE(unit)
|
|
io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement for ENDFILE";
|
|
// WRITE(unit,"(I8)",iostat=iostat) 5678
|
|
io = IONAME(BeginExternalFormattedOutput)(
|
|
format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
|
|
IONAME(EnableHandlers)(io, true /*IOSTAT=*/);
|
|
ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile)
|
|
<< "EndIoStatement for WRITE after ENDFILE";
|
|
// BACKSPACE(unit)
|
|
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement for BACKSPACE";
|
|
// WRITE(unit,"(I8)") 3456
|
|
io = IONAME(BeginExternalFormattedOutput)(
|
|
format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement for WRITE after BACKSPACE";
|
|
// REWIND(unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement for REWIND";
|
|
// READ(unit,"(I8)",END=) j, k
|
|
std::int64_t j{-1}, k{-1}, eof{-1};
|
|
io = IONAME(BeginExternalFormattedInput)(
|
|
format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
|
|
IONAME(EnableHandlers)(io, false, false, true /*END=*/);
|
|
ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)";
|
|
ASSERT_EQ(j, 1234) << "READ(j)";
|
|
ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)";
|
|
ASSERT_EQ(k, 3456) << "READ(k)";
|
|
ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)";
|
|
ASSERT_EQ(eof, -1) << "READ(eof)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ";
|
|
// CLOSE(UNIT=unit)
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for Close";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestUTF8Encoding) {
|
|
// OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)";
|
|
ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)";
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for first OPEN";
|
|
char buffer[12];
|
|
std::memcpy(buffer,
|
|
"abc\x80\xff"
|
|
"de\0\0\0\0\0",
|
|
12);
|
|
// WRITE(unit, *) buffer
|
|
io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__);
|
|
StaticDescriptor<0> staticDescriptor;
|
|
Descriptor &desc{staticDescriptor.descriptor()};
|
|
desc.Establish(TypeCode{CFI_type_char}, 7, buffer, 0);
|
|
desc.Check();
|
|
ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc));
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for WRITE";
|
|
// REWIND(unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement for REWIND";
|
|
// READ(unit, *) buffer
|
|
desc.Establish(TypeCode(CFI_type_char), sizeof buffer, buffer, 0);
|
|
desc.Check();
|
|
io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for first READ";
|
|
ASSERT_EQ(std::memcmp(buffer,
|
|
"abc\x80\xff"
|
|
"de ",
|
|
12),
|
|
0);
|
|
// CLOSE(UNIT=unit,STATUS='KEEP')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for first CLOSE";
|
|
// OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',STATUS='OLD')
|
|
io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for second OPEN";
|
|
// READ(unit, *) buffer
|
|
io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for second READ";
|
|
ASSERT_EQ(std::memcmp(buffer,
|
|
"abc\xc2\x80\xc3\xbf"
|
|
"de ",
|
|
12),
|
|
0);
|
|
// CLOSE(UNIT=unit,STATUS='DELETE')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for second CLOSE";
|
|
}
|
|
|
|
TEST(ExternalIOTests, TestUCS) {
|
|
// OPEN(FILE="ucstest',NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8')
|
|
auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetAction(ucstest)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)";
|
|
ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)";
|
|
int unit{-1};
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for first OPEN";
|
|
char32_t wbuffer[8]{U"abc\u0080\uffff"
|
|
"de"};
|
|
// WRITE(unit, *) wbuffec
|
|
io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__);
|
|
StaticDescriptor<0> staticDescriptor;
|
|
Descriptor &desc{staticDescriptor.descriptor()};
|
|
desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer - sizeof(char32_t),
|
|
wbuffer, 0);
|
|
desc.Check();
|
|
ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc));
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for WRITE";
|
|
// REWIND(unit)
|
|
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement for REWIND";
|
|
// READ(unit, *) buffer
|
|
io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
|
|
desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer, wbuffer, 0);
|
|
desc.Check();
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for first READ";
|
|
char dump[80];
|
|
dump[0] = '\0';
|
|
for (int j{0}; j < 8; ++j) {
|
|
std::size_t dumpLen{std::strlen(dump)};
|
|
std::snprintf(
|
|
dump + dumpLen, sizeof dump - dumpLen, " %x", (unsigned)wbuffer[j]);
|
|
}
|
|
EXPECT_EQ(wbuffer[0], U'a') << dump;
|
|
EXPECT_EQ(wbuffer[1], U'b') << dump;
|
|
EXPECT_EQ(wbuffer[2], U'c') << dump;
|
|
EXPECT_EQ(wbuffer[3], U'\u0080') << dump;
|
|
EXPECT_EQ(wbuffer[4], U'\uffff') << dump;
|
|
EXPECT_EQ(wbuffer[5], U'd') << dump;
|
|
EXPECT_EQ(wbuffer[6], U'e') << dump;
|
|
EXPECT_EQ(wbuffer[7], U' ') << dump;
|
|
// CLOSE(UNIT=unit,STATUS='KEEP')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for first CLOSE";
|
|
// OPEN(FILE="ucstest",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
|
|
// FORM='FORMATTED',STATUS='OLD')
|
|
io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10))
|
|
<< "SetAccess(SEQUENTIAL)";
|
|
ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)";
|
|
ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetFile(ucstest)";
|
|
ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)";
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)";
|
|
ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for second OPEN";
|
|
char buffer[12];
|
|
// READ(unit, *) buffer
|
|
io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__);
|
|
desc.Establish(TypeCode{CFI_type_char}, sizeof buffer, buffer, 0);
|
|
desc.Check();
|
|
ASSERT_TRUE(IONAME(InputDescriptor)(io, desc));
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for second READ";
|
|
dump[0] = '\0';
|
|
for (int j{0}; j < 12; ++j) {
|
|
std::size_t dumpLen{std::strlen(dump)};
|
|
std::snprintf(dump + dumpLen, sizeof dump - dumpLen, " %x",
|
|
(unsigned)(unsigned char)buffer[j]);
|
|
}
|
|
EXPECT_EQ(std::memcmp(buffer,
|
|
"abc\xc2\x80\xef\xbf\xbf"
|
|
"de ",
|
|
12),
|
|
0)
|
|
<< dump;
|
|
// CLOSE(UNIT=unit,STATUS='DELETE')
|
|
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for second CLOSE";
|
|
}
|
|
|
|
TEST(ExternalIOTests, BigUnitNumbers) {
|
|
if (std::numeric_limits<ExternalUnit>::max() <
|
|
std::numeric_limits<std::int64_t>::max()) {
|
|
std::int64_t unit64Ok = std::numeric_limits<ExternalUnit>::max();
|
|
std::int64_t unit64Bad = unit64Ok + 1;
|
|
std::int64_t unit64Bad2 =
|
|
static_cast<std::int64_t>(std::numeric_limits<ExternalUnit>::min()) - 1;
|
|
EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, true), IostatOk);
|
|
EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, false), IostatOk);
|
|
EXPECT_EQ(
|
|
IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow);
|
|
EXPECT_EQ(
|
|
IONAME(CheckUnitNumberInRange64)(unit64Bad2, true), IostatUnitOverflow);
|
|
constexpr std::size_t n{80};
|
|
char expectedMsg[n + 1];
|
|
expectedMsg[n] = '\0';
|
|
std::snprintf(expectedMsg, n, "UNIT number %jd is out of range",
|
|
static_cast<std::intmax_t>(unit64Bad));
|
|
EXPECT_DEATH(
|
|
IONAME(CheckUnitNumberInRange64)(unit64Bad, false), expectedMsg);
|
|
for (auto i{std::strlen(expectedMsg)}; i < n; ++i) {
|
|
expectedMsg[i] = ' ';
|
|
}
|
|
char msg[n + 1];
|
|
msg[n] = '\0';
|
|
EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Bad, true, msg, n),
|
|
IostatUnitOverflow);
|
|
EXPECT_EQ(std::strncmp(msg, expectedMsg, n), 0);
|
|
}
|
|
}
|