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; 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 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(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 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 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