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 | |
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 | |
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 | |
45 | TEST(TestTerminator, CheckFailedTest) { |
46 | static Fortran::runtime::Terminator t("someFileName" ); |
47 | ASSERT_DEATH(t.CheckFailed("predicate" ), |
48 | "RUNTIME_CHECK\\(predicate\\) failed at someFileName\\(0\\)" ); |
49 | } |
50 | |
51 | //------------------------------------------------------------------------------ |
52 | /// Test misuse of io api |
53 | //------------------------------------------------------------------------------ |
54 | struct TestIOCrash : CrashHandlerFixture {}; |
55 | |
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 | |
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 | //------------------------------------------------------------------------------ |
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 | |
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 | |
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 | |
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(s: buffer, c: '\0', n: bufferSize); |
126 | cookie = IONAME(BeginInternalFormattedOutput)( |
127 | buffer, bufferSize, format, std::strlen(s: format)); |
128 | IONAME(OutputReal64)(cookie, 1.); |
129 | EXPECT_DEATH(IONAME(OutputReal64)(cookie, 1.), |
130 | "Internal write overran available records" ); |
131 | } |
132 | |
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(s: buffer, c: '\0', n: bufferSize); |
144 | cookie = IONAME(BeginInternalFormattedOutput)( |
145 | buffer, bufferSize, format, std::strlen(s: format)); |
146 | IONAME(OutputComplex64)(cookie, 1., 1.); |
147 | EXPECT_DEATH(IONAME(OutputComplex64)(cookie, 1., 1.), |
148 | "Internal write overran available records" ); |
149 | } |
150 | |
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 | |
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 | |