#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) {
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();
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) {
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;
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';
}
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) {
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) {
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";
}
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) {
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';
}
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) {
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";
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) {
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";
}
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Rewind";
for (int j{1}; j <= records; ++j) {
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) {
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Backspace (before read)";
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';
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Backspace (after read)";
}
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) {
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];
for (int j{0}; j < records; ++j) {
buffer[j] = j;
}
StaticDescriptor<0> staticDescriptor;
Descriptor &desc{staticDescriptor.descriptor()};
for (int j{1}; j <= records; ++j) {
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";
}
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Rewind";
for (int j{1}; j <= records; ++j) {
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) {
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Backspace (before read)";
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';
}
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Backspace (after read)";
}
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) {
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) {
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) {
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';
}
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) {
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];
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);
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";
}
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);
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) {
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Backspace (before read)";
std::snprintf(fmt, sizeof fmt, "(%dI4)", j);
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';
}
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Backspace (after read)";
}
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) {
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 constexpr std::string_view records[] = {
"ABCDEFGH", "IJKLMNOP", "QRSTUVWX"};
static constexpr std::string_view fmt{"(A)"};
for (const auto &record : records) {
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";
}
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];
};
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 "}},
};
int j{0};
for (auto &inputItem : inputItems) {
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++;
}
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Rewind";
j = 0;
for (auto &inputItem : inputItems) {
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++;
}
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Close";
}
TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) {
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 constexpr std::string_view records[] = {"ABCDEFGHIJKLMNOPQRST"};
static constexpr std::string_view fmt{"(A)"};
for (const auto &record : records) {
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";
}
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;
};
TestItems inputItems[]{
{std::string(4, '+'), IostatOk, "ABCD"},
{std::string(4, '+'), IostatOk, "EFGH"},
};
int j{0};
for (auto &inputItem : inputItems) {
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++;
}
static constexpr std::string_view outputItem{"XYZ"};
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";
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Rewind";
std::string resultRecord(20, '+');
std::string expectedRecord{"ABCDEFGHXYZ "};
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";
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Close";
}
TEST(ExternalIOTests, TestWriteAfterEndfile) {
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 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";
io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement for ENDFILE";
io = IONAME(BeginExternalFormattedOutput)(
format.data(), format.length(), nullptr, unit, __FILE__, __LINE__);
IONAME(EnableHandlers)(io, true );
ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()";
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile)
<< "EndIoStatement for WRITE after ENDFILE";
io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement for BACKSPACE";
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";
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement for REWIND";
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 );
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";
io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement() for Close";
}
TEST(ExternalIOTests, TestUTF8Encoding) {
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);
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";
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement for REWIND";
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);
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";
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";
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);
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) {
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"};
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";
io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
<< "EndIoStatement for REWIND";
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;
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";
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];
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;
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);
}
}