1 //===-- flang/unittests/Runtime/CrashHandlerFixture.cpp ---------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 /// Selected APIs are tested here to support development of unit tests for other
10 /// runtime components and ensure the test fixture handles crashes as we expect.
11 //
12 //===----------------------------------------------------------------------===//
13 #include "CrashHandlerFixture.h"
14 #include "tools.h"
15 #include "../../runtime/terminator.h"
16 #include "flang/Runtime/io-api.h"
17 #include "flang/Runtime/transformational.h"
18 #include <gtest/gtest.h>
19 
20 using namespace Fortran::runtime;
21 using namespace Fortran::runtime::io;
22 using Fortran::common::TypeCategory;
23 
24 //------------------------------------------------------------------------------
25 /// Test crashes through direct calls to terminator methods
26 //------------------------------------------------------------------------------
27 struct TestTerminator : CrashHandlerFixture {};
28 
29 #define TEST_CRASH_HANDLER_MESSAGE \
30   "Intentionally crashing runtime for unit test"
31 
TEST(TestTerminator,CrashTest)32 TEST(TestTerminator, CrashTest) {
33   static Fortran::runtime::Terminator t;
34   ASSERT_DEATH(t.Crash(TEST_CRASH_HANDLER_MESSAGE), TEST_CRASH_HANDLER_MESSAGE);
35 }
36 
37 #undef TEST_CRASH_HANDLER_MESSAGE
38 
TEST(TestTerminator,CheckFailedLocationTest)39 TEST(TestTerminator, CheckFailedLocationTest) {
40   static Fortran::runtime::Terminator t;
41   ASSERT_DEATH(t.CheckFailed("predicate", "someFileName", 789),
42       "RUNTIME_CHECK\\(predicate\\) failed at someFileName\\(789\\)");
43 }
44 
TEST(TestTerminator,CheckFailedTest)45 TEST(TestTerminator, CheckFailedTest) {
46   static Fortran::runtime::Terminator t;
47   ASSERT_DEATH(t.CheckFailed("predicate"),
48       "RUNTIME_CHECK\\(predicate\\) failed at \\(null\\)\\(0\\)");
49 }
50 
51 //------------------------------------------------------------------------------
52 /// Test misuse of io api
53 //------------------------------------------------------------------------------
54 struct TestIOCrash : CrashHandlerFixture {};
55 
TEST(TestIOCrash,FormatDescriptorWriteMismatchTest)56 TEST(TestIOCrash, FormatDescriptorWriteMismatchTest) {
57   static constexpr int bufferSize{4};
58   static char buffer[bufferSize];
59   static const char *format{"(A4)"};
60   auto *cookie{IONAME(BeginInternalFormattedOutput)(
61       buffer, bufferSize, format, std::strlen(format))};
62   ASSERT_DEATH(IONAME(OutputLogical)(cookie, true),
63       "Data edit descriptor 'A' may not be used with a LOGICAL data item");
64 }
65 
TEST(TestIOCrash,InvalidFormatCharacterTest)66 TEST(TestIOCrash, InvalidFormatCharacterTest) {
67   static constexpr int bufferSize{1};
68   static char buffer[bufferSize];
69   static const char *format{"(C1)"};
70   auto *cookie{IONAME(BeginInternalFormattedOutput)(
71       buffer, bufferSize, format, std::strlen(format))};
72   ASSERT_DEATH(IONAME(OutputInteger64)(cookie, 0xfeedface),
73       "Unknown 'C' edit descriptor in FORMAT");
74 }
75 
76 //------------------------------------------------------------------------------
77 /// Test buffer overwrites with Output* functions
78 /// Each test performs the tested IO operation correctly first, before causing
79 /// an overwrite to demonstrate that the failure is caused by the overwrite and
80 /// not a misuse of the API.
81 //------------------------------------------------------------------------------
TEST(TestIOCrash,OverwriteBufferAsciiTest)82 TEST(TestIOCrash, OverwriteBufferAsciiTest) {
83   static constexpr int bufferSize{4};
84   static char buffer[bufferSize];
85   static const char *format{"(A4)"};
86   auto *cookie{IONAME(BeginInternalFormattedOutput)(
87       buffer, bufferSize, format, std::strlen(format))};
88   IONAME(OutputAscii)(cookie, "four", bufferSize);
89   ASSERT_DEATH(IONAME(OutputAscii)(cookie, "Too many characters!", 20),
90       "Internal write overran available records");
91 }
92 
TEST(TestIOCrash,OverwriteBufferCharacterTest)93 TEST(TestIOCrash, OverwriteBufferCharacterTest) {
94   static constexpr int bufferSize{1};
95   static char buffer[bufferSize];
96   static const char *format{"(A1)"};
97   auto *cookie{IONAME(BeginInternalFormattedOutput)(
98       buffer, bufferSize, format, std::strlen(format))};
99   IONAME(OutputCharacter)(cookie, "a", 1);
100   ASSERT_DEATH(IONAME(OutputCharacter)(cookie, "a", 1),
101       "Internal write overran available records");
102 }
103 
TEST(TestIOCrash,OverwriteBufferLogicalTest)104 TEST(TestIOCrash, OverwriteBufferLogicalTest) {
105   static constexpr int bufferSize{1};
106   static char buffer[bufferSize];
107   static const char *format{"(L1)"};
108   auto *cookie{IONAME(BeginInternalFormattedOutput)(
109       buffer, bufferSize, format, std::strlen(format))};
110   IONAME(OutputLogical)(cookie, true);
111   ASSERT_DEATH(IONAME(OutputLogical)(cookie, true),
112       "Internal write overran available records");
113 }
114 
TEST(TestIOCrash,OverwriteBufferRealTest)115 TEST(TestIOCrash, OverwriteBufferRealTest) {
116   static constexpr int bufferSize{1};
117   static char buffer[bufferSize];
118   static const char *format{"(F1)"};
119   auto *cookie{IONAME(BeginInternalFormattedOutput)(
120       buffer, bufferSize, format, std::strlen(format))};
121   IONAME(OutputReal32)(cookie, 1.);
122   EXPECT_DEATH(IONAME(OutputReal32)(cookie, 1.),
123       "Internal write overran available records");
124 
125   std::memset(buffer, '\0', bufferSize);
126   cookie = IONAME(BeginInternalFormattedOutput)(
127       buffer, bufferSize, format, std::strlen(format));
128   IONAME(OutputReal64)(cookie, 1.);
129   EXPECT_DEATH(IONAME(OutputReal64)(cookie, 1.),
130       "Internal write overran available records");
131 }
132 
TEST(TestIOCrash,OverwriteBufferComplexTest)133 TEST(TestIOCrash, OverwriteBufferComplexTest) {
134   static constexpr int bufferSize{8};
135   static char buffer[bufferSize];
136   static const char *format{"(Z1,Z1)"};
137   auto *cookie{IONAME(BeginInternalFormattedOutput)(
138       buffer, bufferSize, format, std::strlen(format))};
139   IONAME(OutputComplex32)(cookie, 1., 1.);
140   EXPECT_DEATH(IONAME(OutputComplex32)(cookie, 1., 1.),
141       "Internal write overran available records");
142 
143   std::memset(buffer, '\0', bufferSize);
144   cookie = IONAME(BeginInternalFormattedOutput)(
145       buffer, bufferSize, format, std::strlen(format));
146   IONAME(OutputComplex64)(cookie, 1., 1.);
147   EXPECT_DEATH(IONAME(OutputComplex64)(cookie, 1., 1.),
148       "Internal write overran available records");
149 }
150 
TEST(TestIOCrash,OverwriteBufferIntegerTest)151 TEST(TestIOCrash, OverwriteBufferIntegerTest) {
152   static constexpr int bufferSize{1};
153   static char buffer[bufferSize];
154   static const char *format{"(I1)"};
155   auto *cookie{IONAME(BeginInternalFormattedOutput)(
156       buffer, bufferSize, format, std::strlen(format))};
157   IONAME(OutputInteger64)(cookie, 0xdeadbeef);
158   ASSERT_DEATH(IONAME(OutputInteger64)(cookie, 0xdeadbeef),
159       "Internal write overran available records");
160 }
161 
162 //------------------------------------------------------------------------------
163 /// Test conformity issue reports in transformational intrinsics
164 //------------------------------------------------------------------------------
165 struct TestIntrinsicCrash : CrashHandlerFixture {};
166 
TEST(TestIntrinsicCrash,ConformityErrors)167 TEST(TestIntrinsicCrash, ConformityErrors) {
168   // ARRAY(2,3) and MASK(2,4) should trigger a runtime error.
169   auto array{MakeArray<TypeCategory::Integer, 4>(
170       std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
171   auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 4},
172       std::vector<std::uint8_t>{
173           false, true, true, false, false, true, true, true})};
174   StaticDescriptor<1, true> statDesc;
175   Descriptor &result{statDesc.descriptor()};
176 
177   ASSERT_DEATH(RTNAME(Pack)(result, *array, *mask, nullptr, __FILE__, __LINE__),
178       "Incompatible array arguments to PACK: dimension 2 of ARRAY= has extent "
179       "3 but MASK= has extent 4");
180 }
181