[flang-commits] [flang] 4abba77 - [flang] Add format test to GTest suite
Asher Mancinelli via flang-commits
flang-commits at lists.llvm.org
Tue Apr 27 07:45:55 PDT 2021
Author: Asher Mancinelli
Date: 2021-04-27T07:45:18-07:00
New Revision: 4abba775a39af7f5be5d4573011930b639d69b63
URL: https://github.com/llvm/llvm-project/commit/4abba775a39af7f5be5d4573011930b639d69b63
DIFF: https://github.com/llvm/llvm-project/commit/4abba775a39af7f5be5d4573011930b639d69b63.diff
LOG: [flang] Add format test to GTest suite
Reviewed by: awarzynski
Differential Revision: https://reviews.llvm.org/D100765
Added:
flang/unittests/RuntimeGTest/Format.cpp
Modified:
flang/unittests/Runtime/CMakeLists.txt
flang/unittests/RuntimeGTest/CMakeLists.txt
Removed:
flang/unittests/Runtime/format.cpp
################################################################################
diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt
index 03998fa55d14..2459aed47bb7 100644
--- a/flang/unittests/Runtime/CMakeLists.txt
+++ b/flang/unittests/Runtime/CMakeLists.txt
@@ -18,11 +18,6 @@ target_link_libraries(RuntimeTesting
${llvm_libs}
)
-add_flang_nongtest_unittest(format
- RuntimeTesting
- FortranRuntime
-)
-
# This test is not run by default as it requires input.
add_executable(external-hello-world
external-hello.cpp
diff --git a/flang/unittests/Runtime/format.cpp b/flang/unittests/Runtime/format.cpp
deleted file mode 100644
index 87989eacebcb..000000000000
--- a/flang/unittests/Runtime/format.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-// Tests basic FORMAT string traversal
-
-#include "testing.h"
-#include "../runtime/format-implementation.h"
-#include "../runtime/io-error.h"
-#include <cstdarg>
-#include <cstring>
-#include <string>
-#include <vector>
-
-using namespace Fortran::runtime;
-using namespace Fortran::runtime::io;
-using namespace std::literals::string_literals;
-
-using Results = 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 &);
- void Check(Results &);
- Results 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);
-}
-
-void TestFormatContext::Check(Results &expect) {
- if (expect != results) {
- Fail() << "expected:";
- for (const std::string &s : expect) {
- llvm::errs() << ' ' << s;
- }
- llvm::errs() << "\ngot:";
- for (const std::string &s : results) {
- llvm::errs() << ' ' << s;
- }
- llvm::errs() << '\n';
- }
- expect.clear();
- results.clear();
-}
-
-static void Test(int n, const char *format, Results &&expect, int repeat = 1) {
- TestFormatContext context;
- FormatControl<TestFormatContext> control{
- context, format, std::strlen(format)};
- try {
- for (int j{0}; j < n; ++j) {
- context.Report(control.GetNextDataEdit(context, repeat));
- }
- control.Finish(context);
- if (int iostat{context.GetIoStat()}) {
- context.Crash("GetIoStat() == %d", iostat);
- }
- } catch (const std::string &crash) {
- context.results.push_back("Crash:"s + crash);
- }
- context.Check(expect);
-}
-
-int main() {
- StartTests();
- Test(1, "('PI=',F9.7)", Results{"'PI='", "F9.7"});
- Test(1, "(3HPI=F9.7)", Results{"'PI='", "F9.7"});
- Test(1, "(3HPI=/F9.7)", Results{"'PI='", "/", "F9.7"});
- Test(2, "('PI=',F9.7)", Results{"'PI='", "F9.7", "/", "'PI='", "F9.7"});
- Test(2, "(2('PI=',F9.7),'done')",
- Results{"'PI='", "F9.7", "'PI='", "F9.7", "'done'"});
- Test(2, "(3('PI=',F9.7,:),'tooFar')",
- Results{"'PI='", "F9.7", "'PI='", "F9.7"});
- Test(2, "(*('PI=',F9.7,:),'tooFar')",
- Results{"'PI='", "F9.7", "'PI='", "F9.7"});
- Test(1, "(3F9.7)", Results{"2*F9.7"}, 2);
- return EndTests();
-}
diff --git a/flang/unittests/RuntimeGTest/CMakeLists.txt b/flang/unittests/RuntimeGTest/CMakeLists.txt
index 38a577d5add0..cc29f31c829d 100644
--- a/flang/unittests/RuntimeGTest/CMakeLists.txt
+++ b/flang/unittests/RuntimeGTest/CMakeLists.txt
@@ -1,6 +1,7 @@
add_flang_unittest(FlangRuntimeTests
CharacterTest.cpp
CrashHandlerFixture.cpp
+ Format.cpp
MiscIntrinsic.cpp
Numeric.cpp
NumericalFormatTest.cpp
diff --git a/flang/unittests/RuntimeGTest/Format.cpp b/flang/unittests/RuntimeGTest/Format.cpp
new file mode 100644
index 000000000000..63a1801071b4
--- /dev/null
+++ b/flang/unittests/RuntimeGTest/Format.cpp
@@ -0,0 +1,183 @@
+//===-- flang/unittests/RuntimeGTest/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,:),'tooFar')",
+ 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)),
+ "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)),
+ "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 '.'");
+}
More information about the flang-commits
mailing list