1 | //===-- unittests/Runtime/RuntimeCrashTest.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 "flang-rt/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, InvalidFormatCharacterTest) { |
57 | static constexpr int bufferSize{1}; |
58 | static char buffer[bufferSize]; |
59 | static const char *format{"(C1)" }; |
60 | auto *cookie{IONAME(BeginInternalFormattedOutput)( |
61 | buffer, bufferSize, format, std::strlen(format))}; |
62 | ASSERT_DEATH(IONAME(OutputInteger64)(cookie, 0xfeedface), |
63 | "Unknown 'C' edit descriptor in FORMAT" ); |
64 | } |
65 | |
66 | //------------------------------------------------------------------------------ |
67 | /// Test buffer overwrites with Output* functions |
68 | /// Each test performs the tested IO operation correctly first, before causing |
69 | /// an overwrite to demonstrate that the failure is caused by the overwrite and |
70 | /// not a misuse of the API. |
71 | //------------------------------------------------------------------------------ |
72 | TEST(TestIOCrash, OverwriteBufferAsciiTest) { |
73 | static constexpr int bufferSize{4}; |
74 | static char buffer[bufferSize]; |
75 | static const char *format{"(A4)" }; |
76 | auto *cookie{IONAME(BeginInternalFormattedOutput)( |
77 | buffer, bufferSize, format, std::strlen(format))}; |
78 | IONAME(OutputAscii)(cookie, "four" , bufferSize); |
79 | ASSERT_DEATH(IONAME(OutputAscii)(cookie, "Too many characters!" , 20), |
80 | "Internal write overran available records" ); |
81 | } |
82 | |
83 | TEST(TestIOCrash, OverwriteBufferCharacterTest) { |
84 | static constexpr int bufferSize{1}; |
85 | static char buffer[bufferSize]; |
86 | static const char *format{"(A1)" }; |
87 | auto *cookie{IONAME(BeginInternalFormattedOutput)( |
88 | buffer, bufferSize, format, std::strlen(format))}; |
89 | IONAME(OutputCharacter)(cookie, "a" , 1); |
90 | ASSERT_DEATH(IONAME(OutputCharacter)(cookie, "a" , 1), |
91 | "Internal write overran available records" ); |
92 | } |
93 | |
94 | TEST(TestIOCrash, OverwriteBufferLogicalTest) { |
95 | static constexpr int bufferSize{1}; |
96 | static char buffer[bufferSize]; |
97 | static const char *format{"(L1)" }; |
98 | auto *cookie{IONAME(BeginInternalFormattedOutput)( |
99 | buffer, bufferSize, format, std::strlen(format))}; |
100 | IONAME(OutputLogical)(cookie, true); |
101 | ASSERT_DEATH(IONAME(OutputLogical)(cookie, true), |
102 | "Internal write overran available records" ); |
103 | } |
104 | |
105 | TEST(TestIOCrash, OverwriteBufferRealTest) { |
106 | static constexpr int bufferSize{1}; |
107 | static char buffer[bufferSize]; |
108 | static const char *format{"(F1)" }; |
109 | auto *cookie{IONAME(BeginInternalFormattedOutput)( |
110 | buffer, bufferSize, format, std::strlen(format))}; |
111 | IONAME(OutputReal32)(cookie, 1.); |
112 | EXPECT_DEATH(IONAME(OutputReal32)(cookie, 1.), |
113 | "Internal write overran available records" ); |
114 | |
115 | std::memset(s: buffer, c: '\0', n: bufferSize); |
116 | cookie = IONAME(BeginInternalFormattedOutput)( |
117 | buffer, bufferSize, format, std::strlen(s: format)); |
118 | IONAME(OutputReal64)(cookie, 1.); |
119 | EXPECT_DEATH(IONAME(OutputReal64)(cookie, 1.), |
120 | "Internal write overran available records" ); |
121 | } |
122 | |
123 | TEST(TestIOCrash, OverwriteBufferComplexTest) { |
124 | static constexpr int bufferSize{8}; |
125 | static char buffer[bufferSize]; |
126 | static const char *format{"(Z1,Z1)" }; |
127 | auto *cookie{IONAME(BeginInternalFormattedOutput)( |
128 | buffer, bufferSize, format, std::strlen(format))}; |
129 | IONAME(OutputComplex32)(cookie, 1., 1.); |
130 | EXPECT_DEATH(IONAME(OutputComplex32)(cookie, 1., 1.), |
131 | "Internal write overran available records" ); |
132 | |
133 | std::memset(s: buffer, c: '\0', n: bufferSize); |
134 | cookie = IONAME(BeginInternalFormattedOutput)( |
135 | buffer, bufferSize, format, std::strlen(s: format)); |
136 | IONAME(OutputComplex64)(cookie, 1., 1.); |
137 | EXPECT_DEATH(IONAME(OutputComplex64)(cookie, 1., 1.), |
138 | "Internal write overran available records" ); |
139 | } |
140 | |
141 | TEST(TestIOCrash, OverwriteBufferIntegerTest) { |
142 | static constexpr int bufferSize{1}; |
143 | static char buffer[bufferSize]; |
144 | static const char *format{"(I1)" }; |
145 | auto *cookie{IONAME(BeginInternalFormattedOutput)( |
146 | buffer, bufferSize, format, std::strlen(format))}; |
147 | IONAME(OutputInteger64)(cookie, 0xdeadbeef); |
148 | ASSERT_DEATH(IONAME(OutputInteger64)(cookie, 0xdeadbeef), |
149 | "Internal write overran available records" ); |
150 | } |
151 | |
152 | //------------------------------------------------------------------------------ |
153 | /// Test conformity issue reports in transformational intrinsics |
154 | //------------------------------------------------------------------------------ |
155 | struct TestIntrinsicCrash : CrashHandlerFixture {}; |
156 | |
157 | TEST(TestIntrinsicCrash, ConformityErrors) { |
158 | // ARRAY(2,3) and MASK(2,4) should trigger a runtime error. |
159 | auto array{MakeArray<TypeCategory::Integer, 4>( |
160 | std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})}; |
161 | auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 4}, |
162 | std::vector<std::uint8_t>{ |
163 | false, true, true, false, false, true, true, true})}; |
164 | StaticDescriptor<1, true> statDesc; |
165 | Descriptor &result{statDesc.descriptor()}; |
166 | |
167 | ASSERT_DEATH(RTNAME(Pack)(result, *array, *mask, nullptr, __FILE__, __LINE__), |
168 | "Incompatible array arguments to PACK: dimension 2 of ARRAY= has extent " |
169 | "3 but MASK= has extent 4" ); |
170 | } |
171 | |