Add the remaining pieces to support IO for noncontigous formats. This is done by passing an array descriptor to IO calls. Scalar formats continue to pass string and length arguments. IO calls with formats are modified to place the new format descriptor argument directly after the original string and length arguments.
934 lines
39 KiB
C++
934 lines
39 KiB
C++
//===-- flang/unittests/RuntimeGTest/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/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";
|
|
|
|
// INQUIRE(IOLENGTH=) j
|
|
io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
|
|
ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
|
|
io, reinterpret_cast<const char *>(&buffer), recl, 1))
|
|
<< "OutputUnformattedBlock() 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(OutputUnformattedBlock)(
|
|
io, reinterpret_cast<const char *>(&buffer), 1, recl))
|
|
<< "OutputUnformattedBlock()";
|
|
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputUnformattedBlock";
|
|
}
|
|
|
|
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(InputUnformattedBlock)(
|
|
io, reinterpret_cast<char *>(&buffer), 1, recl))
|
|
<< "InputUnformattedBlock()";
|
|
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InputUnformattedBlock";
|
|
|
|
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";
|
|
|
|
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(OutputUnformattedBlock)(
|
|
io, reinterpret_cast<const char *>(&buffer), recl, recl))
|
|
<< "OutputUnformattedBlock()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputUnformattedBlock";
|
|
}
|
|
|
|
// 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(InputUnformattedBlock)(
|
|
io, reinterpret_cast<char *>(&buffer), recl, recl))
|
|
<< "InputUnformattedBlock()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InputUnformattedBlock";
|
|
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, ...
|
|
io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__);
|
|
for (int j{1}; j <= 3; ++j) {
|
|
ASSERT_TRUE(IONAME(OutputUnformattedBlock)(
|
|
io, reinterpret_cast<const char *>(&buffer), recl, 1))
|
|
<< "OutputUnformattedBlock() for InquireIoLength";
|
|
}
|
|
ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InquireIoLength";
|
|
|
|
// 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";
|
|
}
|
|
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(OutputUnformattedBlock)(
|
|
io, reinterpret_cast<const char *>(&buffer), recl, recl))
|
|
<< "OutputUnformattedBlock()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputUnformattedBlock";
|
|
}
|
|
|
|
// 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(InputUnformattedBlock)(
|
|
io, reinterpret_cast<char *>(&buffer), recl, recl))
|
|
<< "InputUnformattedBlock()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InputUnformattedBlock";
|
|
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(InputUnformattedBlock)(
|
|
io, reinterpret_cast<char *>(&buffer), recl, recl))
|
|
<< "InputUnformattedBlock()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for InputUnformattedBlock";
|
|
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;
|
|
}
|
|
|
|
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__);
|
|
ASSERT_TRUE(IONAME(OutputUnformattedBlock)(io,
|
|
reinterpret_cast<const char *>(&buffer), j * sizeof *buffer,
|
|
sizeof *buffer))
|
|
<< "OutputUnformattedBlock()";
|
|
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
|
|
<< "EndIoStatement() for OutputUnformattedBlock";
|
|
}
|
|
|
|
// 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(InputUnformattedBlock)(io,
|
|
reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer))
|
|
<< "InputUnformattedBlock()";
|
|
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 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__);
|
|
ASSERT_TRUE(IONAME(InputUnformattedBlock)(io,
|
|
reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer))
|
|
<< "InputUnformattedBlock()";
|
|
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;
|
|
};
|
|
// Actual non advancing input IO test
|
|
TestItems inputItems[]{
|
|
{std::string(4, '+'), IostatOk, "ABCD"},
|
|
{std::string(4, '+'), IostatOk, "EFGH"},
|
|
{std::string(4, '+'), IostatEor, " "},
|
|
{std::string(2, '+'), IostatOk, "IJ"},
|
|
{std::string(8, '+'), IostatEor, "KLMNOP "},
|
|
{std::string(10, '+'), IostatEor, "QRSTUVWX "},
|
|
};
|
|
|
|
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;
|
|
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)
|
|
<< "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);
|
|
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)(2147483648, 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);
|
|
}
|
|
}
|