forked from OSchip/llvm-project
183 lines
5.5 KiB
C++
183 lines
5.5 KiB
C++
//===-- flang/unittests/Runtime/Format.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CrashHandlerFixture.h"
|
|
#include "../runtime/format-implementation.h"
|
|
#include "../runtime/io-error.h"
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
using namespace Fortran::runtime;
|
|
using namespace Fortran::runtime::io;
|
|
using namespace std::literals::string_literals;
|
|
|
|
using ResultsTy = std::vector<std::string>;
|
|
|
|
// A test harness context for testing FormatControl
|
|
class TestFormatContext : public IoErrorHandler {
|
|
public:
|
|
using CharType = char;
|
|
TestFormatContext() : IoErrorHandler{"format.cpp", 1} {}
|
|
bool Emit(const char *, std::size_t);
|
|
bool Emit(const char16_t *, std::size_t);
|
|
bool Emit(const char32_t *, std::size_t);
|
|
bool AdvanceRecord(int = 1);
|
|
void HandleRelativePosition(std::int64_t);
|
|
void HandleAbsolutePosition(std::int64_t);
|
|
void Report(const DataEdit &);
|
|
ResultsTy results;
|
|
MutableModes &mutableModes() { return mutableModes_; }
|
|
|
|
private:
|
|
MutableModes mutableModes_;
|
|
};
|
|
|
|
bool TestFormatContext::Emit(const char *s, std::size_t len) {
|
|
std::string str{s, len};
|
|
results.push_back("'"s + str + '\'');
|
|
return true;
|
|
}
|
|
bool TestFormatContext::Emit(const char16_t *, std::size_t) {
|
|
Crash("TestFormatContext::Emit(const char16_t *) called");
|
|
return false;
|
|
}
|
|
bool TestFormatContext::Emit(const char32_t *, std::size_t) {
|
|
Crash("TestFormatContext::Emit(const char32_t *) called");
|
|
return false;
|
|
}
|
|
|
|
bool TestFormatContext::AdvanceRecord(int n) {
|
|
while (n-- > 0) {
|
|
results.emplace_back("/");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void TestFormatContext::HandleAbsolutePosition(std::int64_t n) {
|
|
results.push_back("T"s + std::to_string(n));
|
|
}
|
|
|
|
void TestFormatContext::HandleRelativePosition(std::int64_t n) {
|
|
if (n < 0) {
|
|
results.push_back("TL"s + std::to_string(-n));
|
|
} else {
|
|
results.push_back(std::to_string(n) + 'X');
|
|
}
|
|
}
|
|
|
|
void TestFormatContext::Report(const DataEdit &edit) {
|
|
std::string str{edit.descriptor};
|
|
if (edit.repeat != 1) {
|
|
str = std::to_string(edit.repeat) + '*' + str;
|
|
}
|
|
if (edit.variation) {
|
|
str += edit.variation;
|
|
}
|
|
if (edit.width) {
|
|
str += std::to_string(*edit.width);
|
|
}
|
|
if (edit.digits) {
|
|
str += "."s + std::to_string(*edit.digits);
|
|
}
|
|
if (edit.expoDigits) {
|
|
str += "E"s + std::to_string(*edit.expoDigits);
|
|
}
|
|
// modes?
|
|
results.push_back(str);
|
|
}
|
|
|
|
struct FormatTests : public CrashHandlerFixture {};
|
|
|
|
TEST(FormatTests, FormatStringTraversal) {
|
|
|
|
using ParamsTy = std::tuple<int, const char *, ResultsTy, int>;
|
|
|
|
static const std::vector<ParamsTy> params{
|
|
{1, "('PI=',F9.7)", ResultsTy{"'PI='", "F9.7"}, 1},
|
|
{1, "(3HPI=F9.7)", ResultsTy{"'PI='", "F9.7"}, 1},
|
|
{1, "(3HPI=/F9.7)", ResultsTy{"'PI='", "/", "F9.7"}, 1},
|
|
{2, "('PI=',F9.7)", ResultsTy{"'PI='", "F9.7", "/", "'PI='", "F9.7"}, 1},
|
|
{2, "(2('PI=',F9.7),'done')",
|
|
ResultsTy{"'PI='", "F9.7", "'PI='", "F9.7", "'done'"}, 1},
|
|
{2, "(3('PI=',F9.7,:),'tooFar')",
|
|
ResultsTy{"'PI='", "F9.7", "'PI='", "F9.7"}, 1},
|
|
{2, "(*('PI=',F9.7,:))", ResultsTy{"'PI='", "F9.7", "'PI='", "F9.7"}, 1},
|
|
{1, "(3F9.7)", ResultsTy{"2*F9.7"}, 2},
|
|
};
|
|
|
|
for (const auto &[n, format, expect, repeat] : params) {
|
|
TestFormatContext context;
|
|
FormatControl<decltype(context)> control{
|
|
context, format, std::strlen(format)};
|
|
|
|
for (auto i{0}; i < n; i++) {
|
|
context.Report(/*edit=*/control.GetNextDataEdit(context, repeat));
|
|
}
|
|
control.Finish(context);
|
|
|
|
auto iostat{context.GetIoStat()};
|
|
ASSERT_EQ(iostat, 0) << "Expected iostat == 0, but GetIoStat() == "
|
|
<< iostat;
|
|
|
|
// Create strings of the expected/actual results for printing errors
|
|
std::string allExpectedResults{""}, allActualResults{""};
|
|
for (const auto &res : context.results) {
|
|
allActualResults += " "s + res;
|
|
}
|
|
for (const auto &res : expect) {
|
|
allExpectedResults += " "s + res;
|
|
}
|
|
|
|
const auto &results = context.results;
|
|
ASSERT_EQ(expect, results) << "Expected '" << allExpectedResults
|
|
<< "' but got '" << allActualResults << "'";
|
|
}
|
|
}
|
|
|
|
struct InvalidFormatFailure : CrashHandlerFixture {};
|
|
|
|
TEST(InvalidFormatFailure, ParenMismatch) {
|
|
static constexpr const char *format{"("};
|
|
static constexpr int repeat{1};
|
|
|
|
TestFormatContext context;
|
|
FormatControl<decltype(context)> control{
|
|
context, format, std::strlen(format)};
|
|
|
|
ASSERT_DEATH(
|
|
context.Report(/*edit=*/control.GetNextDataEdit(context, repeat)),
|
|
R"(FORMAT missing at least one '\)')");
|
|
}
|
|
|
|
TEST(InvalidFormatFailure, MissingPrecision) {
|
|
static constexpr const char *format{"(F9.)"};
|
|
static constexpr int repeat{1};
|
|
|
|
TestFormatContext context;
|
|
FormatControl<decltype(context)> control{
|
|
context, format, std::strlen(format)};
|
|
|
|
ASSERT_DEATH(
|
|
context.Report(/*edit=*/control.GetNextDataEdit(context, repeat)),
|
|
R"(Invalid FORMAT: integer expected at '\)')");
|
|
}
|
|
|
|
TEST(InvalidFormatFailure, MissingFormatWidth) {
|
|
static constexpr const char *format{"(F.9)"};
|
|
static constexpr int repeat{1};
|
|
|
|
TestFormatContext context;
|
|
FormatControl<decltype(context)> control{
|
|
context, format, std::strlen(format)};
|
|
|
|
ASSERT_DEATH(
|
|
context.Report(/*edit=*/control.GetNextDataEdit(context, repeat)),
|
|
"Invalid FORMAT: integer expected at '.'");
|
|
}
|