text: //===-- flang/unittests/RuntimeGTest/CrashHandlerFixture.cpp ----*- C++ -*-===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  //
  /// Selected APIs are tested here to support development of unit tests for other
  /// runtime components and ensure the test fixture handles crashes as we expect.
  //
  //===----------------------------------------------------------------------===//
  #include "CrashHandlerFixture.h"
  #include "../../runtime/terminator.h"
  #include "flang/Runtime/io-api.h"
  #include <gtest/gtest.h>
  
  using namespace Fortran::runtime;
  using namespace Fortran::runtime::io;
  
  //------------------------------------------------------------------------------
  /// Test crashes through direct calls to terminator methods
  //------------------------------------------------------------------------------
  struct TestTerminator : CrashHandlerFixture {};
  
  #define TEST_CRASH_HANDLER_MESSAGE \
    "Intentionally crashing runtime for unit test"
  
  TEST(TestTerminator, CrashTest) {
    static Fortran::runtime::Terminator t;
    ASSERT_DEATH(t.Crash(TEST_CRASH_HANDLER_MESSAGE), TEST_CRASH_HANDLER_MESSAGE);
  }
  
  #undef TEST_CRASH_HANDLER_MESSAGE
  
  TEST(TestTerminator, CheckFailedLocationTest) {
    static Fortran::runtime::Terminator t;
    ASSERT_DEATH(t.CheckFailed("predicate", "someFileName", 789),
        "RUNTIME_CHECK\\(predicate\\) failed at someFileName\\(789\\)");
  }
  
  TEST(TestTerminator, CheckFailedTest) {
    static Fortran::runtime::Terminator t;
    ASSERT_DEATH(t.CheckFailed("predicate"),
        "RUNTIME_CHECK\\(predicate\\) failed at \\(null\\)\\(0\\)");
  }
  
  //------------------------------------------------------------------------------
  /// Test misuse of io api
  //------------------------------------------------------------------------------
  struct TestIOCrash : CrashHandlerFixture {};
  
  TEST(TestIOCrash, FormatDescriptorWriteMismatchTest) {
    static constexpr int bufferSize{4};
    static char buffer[bufferSize];
    static const char *format{"(A4)"};
    auto *cookie{IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format))};
    ASSERT_DEATH(IONAME(OutputInteger64)(cookie, 0xfeedface),
        "Data edit descriptor 'A' may not be used with an INTEGER data item");
  }
  
  TEST(TestIOCrash, InvalidFormatCharacterTest) {
    static constexpr int bufferSize{1};
    static char buffer[bufferSize];
    static const char *format{"(C1)"};
    auto *cookie{IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format))};
    ASSERT_DEATH(IONAME(OutputInteger64)(cookie, 0xfeedface),
        "Unknown 'C' edit descriptor in FORMAT");
  }
  
  //------------------------------------------------------------------------------
  /// Test buffer overwrites with Output* functions
  /// Each test performs the tested IO operation correctly first, before causing
  /// an overwrite to demonstrate that the failure is caused by the overwrite and
  /// not a misuse of the API.
  //------------------------------------------------------------------------------
  TEST(TestIOCrash, OverwriteBufferAsciiTest) {
    static constexpr int bufferSize{4};
    static char buffer[bufferSize];
    static const char *format{"(A4)"};
    auto *cookie{IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format))};
    IONAME(OutputAscii)(cookie, "four", bufferSize);
    ASSERT_DEATH(IONAME(OutputAscii)(cookie, "Too many characters!", 20),
        "Internal write overran available records");
  }
  
  TEST(TestIOCrash, OverwriteBufferCharacterTest) {
    static constexpr int bufferSize{1};
    static char buffer[bufferSize];
    static const char *format{"(A1)"};
    auto *cookie{IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format))};
    IONAME(OutputCharacter)(cookie, "a", 1);
    ASSERT_DEATH(IONAME(OutputCharacter)(cookie, "a", 1),
        "Internal write overran available records");
  }
  
  TEST(TestIOCrash, OverwriteBufferLogicalTest) {
    static constexpr int bufferSize{1};
    static char buffer[bufferSize];
    static const char *format{"(L1)"};
    auto *cookie{IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format))};
    IONAME(OutputLogical)(cookie, true);
    ASSERT_DEATH(IONAME(OutputLogical)(cookie, true),
        "Internal write overran available records");
  }
  
  TEST(TestIOCrash, OverwriteBufferRealTest) {
    static constexpr int bufferSize{1};
    static char buffer[bufferSize];
    static const char *format{"(F1)"};
    auto *cookie{IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format))};
    IONAME(OutputReal32)(cookie, 1.);
    EXPECT_DEATH(IONAME(OutputReal32)(cookie, 1.),
        "Internal write overran available records");
  
    std::memset(buffer, '\0', bufferSize);
    cookie = IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format));
    IONAME(OutputReal64)(cookie, 1.);
    EXPECT_DEATH(IONAME(OutputReal64)(cookie, 1.),
        "Internal write overran available records");
  }
  
  TEST(TestIOCrash, OverwriteBufferComplexTest) {
    static constexpr int bufferSize{8};
    static char buffer[bufferSize];
    static const char *format{"(Z1,Z1)"};
    auto *cookie{IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format))};
    IONAME(OutputComplex32)(cookie, 1., 1.);
    EXPECT_DEATH(IONAME(OutputComplex32)(cookie, 1., 1.),
        "Internal write overran available records");
  
    std::memset(buffer, '\0', bufferSize);
    cookie = IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format));
    IONAME(OutputComplex64)(cookie, 1., 1.);
    EXPECT_DEATH(IONAME(OutputComplex64)(cookie, 1., 1.),
        "Internal write overran available records");
  }
  
  TEST(TestIOCrash, OverwriteBufferIntegerTest) {
    static constexpr int bufferSize{1};
    static char buffer[bufferSize];
    static const char *format{"(I1)"};
    auto *cookie{IONAME(BeginInternalFormattedOutput)(
        buffer, bufferSize, format, std::strlen(format))};
    IONAME(OutputInteger64)(cookie, 0xdeadbeef);
    ASSERT_DEATH(IONAME(OutputInteger64)(cookie, 0xdeadbeef),
        "Internal write overran available records");
  }
  