1 //===-- flang/unittests/Runtime/Format.cpp ----------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "CrashHandlerFixture.h"
10 #include "../runtime/connection.h"
11 #include "../runtime/format-implementation.h"
12 #include "../runtime/io-error.h"
18 using namespace Fortran::runtime
;
19 using namespace Fortran::runtime::io
;
20 using namespace std::literals::string_literals
;
22 using ResultsTy
= std::vector
<std::string
>;
24 // A test harness context for testing FormatControl
25 class TestFormatContext
: public IoErrorHandler
{
27 using CharType
= char;
28 TestFormatContext() : IoErrorHandler
{"format.cpp", 1} {}
29 bool Emit(const char *, std::size_t, std::size_t = 0);
30 bool AdvanceRecord(int = 1);
31 void HandleRelativePosition(std::int64_t);
32 void HandleAbsolutePosition(std::int64_t);
33 void Report(const std::optional
<DataEdit
> &);
35 MutableModes
&mutableModes() { return mutableModes_
; }
36 ConnectionState
&GetConnectionState() { return connectionState_
; }
39 MutableModes mutableModes_
;
40 ConnectionState connectionState_
;
43 bool TestFormatContext::Emit(const char *s
, std::size_t len
, std::size_t) {
44 std::string str
{s
, len
};
45 results
.push_back("'"s
+ str
+ '\'');
49 bool TestFormatContext::AdvanceRecord(int n
) {
51 results
.emplace_back("/");
56 void TestFormatContext::HandleAbsolutePosition(std::int64_t n
) {
57 results
.push_back("T"s
+ std::to_string(n
));
60 void TestFormatContext::HandleRelativePosition(std::int64_t n
) {
62 results
.push_back("TL"s
+ std::to_string(-n
));
64 results
.push_back(std::to_string(n
) + 'X');
68 void TestFormatContext::Report(const std::optional
<DataEdit
> &edit
) {
70 std::string str
{edit
->descriptor
};
71 if (edit
->repeat
!= 1) {
72 str
= std::to_string(edit
->repeat
) + '*' + str
;
74 if (edit
->variation
) {
75 str
+= edit
->variation
;
78 str
+= std::to_string(*edit
->width
);
81 str
+= "."s
+ std::to_string(*edit
->digits
);
83 if (edit
->expoDigits
) {
84 str
+= "E"s
+ std::to_string(*edit
->expoDigits
);
87 results
.push_back(str
);
89 results
.push_back("(nullopt)"s
);
93 struct FormatTests
: public CrashHandlerFixture
{};
95 TEST(FormatTests
, FormatStringTraversal
) {
97 using ParamsTy
= std::tuple
<int, const char *, ResultsTy
, int>;
99 static const std::vector
<ParamsTy
> params
{
100 {1, "('PI=',F9.7)", ResultsTy
{"'PI='", "F9.7"}, 1},
101 {1, "(3HPI=F9.7)", ResultsTy
{"'PI='", "F9.7"}, 1},
102 {1, "(3HPI=/F9.7)", ResultsTy
{"'PI='", "/", "F9.7"}, 1},
103 {2, "('PI=',F9.7)", ResultsTy
{"'PI='", "F9.7", "/", "'PI='", "F9.7"}, 1},
104 {2, "(2('PI=',F9.7),'done')",
105 ResultsTy
{"'PI='", "F9.7", "'PI='", "F9.7", "'done'"}, 1},
106 {2, "(3('PI=',F9.7,:),'tooFar')",
107 ResultsTy
{"'PI='", "F9.7", "'PI='", "F9.7"}, 1},
108 {2, "(*('PI=',F9.7,:))", ResultsTy
{"'PI='", "F9.7", "'PI='", "F9.7"}, 1},
109 {1, "(3F9.7)", ResultsTy
{"2*F9.7"}, 2},
112 for (const auto &[n
, format
, expect
, repeat
] : params
) {
113 TestFormatContext context
;
114 FormatControl
<decltype(context
)> control
{
115 context
, format
, std::strlen(format
)};
117 for (auto i
{0}; i
< n
; i
++) {
118 context
.Report(/*edit=*/control
.GetNextDataEdit(context
, repeat
));
120 control
.Finish(context
);
122 auto iostat
{context
.GetIoStat()};
123 ASSERT_EQ(iostat
, 0) << "Expected iostat == 0, but GetIoStat() == "
126 // Create strings of the expected/actual results for printing errors
127 std::string allExpectedResults
{""}, allActualResults
{""};
128 for (const auto &res
: context
.results
) {
129 allActualResults
+= " "s
+ res
;
131 for (const auto &res
: expect
) {
132 allExpectedResults
+= " "s
+ res
;
135 const auto &results
= context
.results
;
136 ASSERT_EQ(expect
, results
) << "Expected '" << allExpectedResults
137 << "' but got '" << allActualResults
<< "'";
141 struct InvalidFormatFailure
: CrashHandlerFixture
{};
143 TEST(InvalidFormatFailure
, ParenMismatch
) {
144 static constexpr const char *format
{"("};
145 static constexpr int repeat
{1};
147 TestFormatContext context
;
148 FormatControl
<decltype(context
)> control
{
149 context
, format
, std::strlen(format
)};
152 context
.Report(/*edit=*/control
.GetNextDataEdit(context
, repeat
)),
153 R
"(FORMAT missing at least one '\)')");
156 TEST(InvalidFormatFailure
, MissingPrecision
) {
157 static constexpr const char *format
{"(F9.)"};
158 static constexpr int repeat
{1};
160 TestFormatContext context
;
161 FormatControl
<decltype(context
)> control
{
162 context
, format
, std::strlen(format
)};
165 context
.Report(/*edit=*/control
.GetNextDataEdit(context
, repeat
)),
166 R
"(Invalid FORMAT: integer expected at '\)')");
169 TEST(InvalidFormatFailure
, MissingFormatWidth
) {
170 static constexpr const char *format
{"(F.9)"};
171 static constexpr int repeat
{1};
173 TestFormatContext context
;
174 FormatControl
<decltype(context
)> control
{
175 context
, format
, std::strlen(format
)};
178 context
.Report(/*edit=*/control
.GetNextDataEdit(context
, repeat
)),
179 "Invalid FORMAT: integer expected at '.'");