text: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
  // Licensed under the MIT License:
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  // THE SOFTWARE.
  
  #include "exception.h"
  #include "debug.h"
  #include "kj/compat/gtest.h"
  #include <stdexcept>
  #include <stdint.h>
  
  namespace kj {
  namespace _ {  // private
  namespace {
  
  TEST(Exception, TrimSourceFilename) {
  #if _WIN32
    EXPECT_TRUE(trimSourceFilename(__FILE__) == "kj/exception-test.c++" ||
                trimSourceFilename(__FILE__) == "kj\\exception-test.c++");
  #else
    EXPECT_EQ(trimSourceFilename(__FILE__), "kj/exception-test.c++");
  #endif
  }
  
  TEST(Exception, RunCatchingExceptions) {
    bool recovered = false;
    Maybe<Exception> e = kj::runCatchingExceptions([&]() {
      KJ_FAIL_ASSERT("foo") {
        break;
      }
      recovered = true;
    });
  
  #if KJ_NO_EXCEPTIONS
    EXPECT_TRUE(recovered);
  #else
    EXPECT_FALSE(recovered);
  #endif
  
    KJ_IF_MAYBE(ex, e) {
      EXPECT_EQ("foo", ex->getDescription());
    } else {
      ADD_FAILURE() << "Expected exception";
    }
  }
  
  #if !KJ_NO_EXCEPTIONS
  TEST(Exception, RunCatchingExceptionsStdException) {
    Maybe<Exception> e = kj::runCatchingExceptions([&]() {
      throw std::logic_error("foo");
    });
  
    KJ_IF_MAYBE(ex, e) {
      EXPECT_EQ("std::exception: foo", ex->getDescription());
    } else {
      ADD_FAILURE() << "Expected exception";
    }
  }
  
  TEST(Exception, RunCatchingExceptionsOtherException) {
    Maybe<Exception> e = kj::runCatchingExceptions([&]() {
      throw 123;
    });
  
    KJ_IF_MAYBE(ex, e) {
  #if __GNUC__ && !KJ_NO_RTTI
      EXPECT_EQ("unknown non-KJ exception of type: int", ex->getDescription());
  #else
      EXPECT_EQ("unknown non-KJ exception", ex->getDescription());
  #endif
    } else {
      ADD_FAILURE() << "Expected exception";
    }
  }
  #endif
  
  #if !KJ_NO_EXCEPTIONS
  // We skip this test when exceptions are disabled because making it no-exceptions-safe defeats
  // the purpose of the test: recoverable exceptions won't throw inside a destructor in the first
  // place.
  
  class ThrowingDestructor: public UnwindDetector {
  public:
    ~ThrowingDestructor() noexcept(false) {
      catchExceptionsIfUnwinding([]() {
        KJ_FAIL_ASSERT("this is a test, not a real bug");
      });
    }
  };
  
  TEST(Exception, UnwindDetector) {
    // If no other exception is happening, ThrowingDestructor's destructor throws one.
    Maybe<Exception> e = kj::runCatchingExceptions([&]() {
      ThrowingDestructor t;
    });
  
    KJ_IF_MAYBE(ex, e) {
      EXPECT_EQ("this is a test, not a real bug", ex->getDescription());
    } else {
      ADD_FAILURE() << "Expected exception";
    }
  
    // If another exception is happening, ThrowingDestructor's destructor's exception is squelched.
    e = kj::runCatchingExceptions([&]() {
      ThrowingDestructor t;
      KJ_FAIL_ASSERT("baz") {
        break;
      }
    });
  
    KJ_IF_MAYBE(ex, e) {
      EXPECT_EQ("baz", ex->getDescription());
    } else {
      ADD_FAILURE() << "Expected exception";
    }
  }
  #endif
  
  #if !__MINGW32__  // Inexplicably crashes when exception is thrown from constructor.
  TEST(Exception, ExceptionCallbackMustBeOnStack) {
    KJ_EXPECT_THROW_MESSAGE("must be allocated on the stack", new ExceptionCallback);
  }
  #endif  // !__MINGW32__
  
  #if !KJ_NO_EXCEPTIONS
  TEST(Exception, ScopeSuccessFail) {
    bool success = false;
    bool failure = false;
  
    {
      KJ_ON_SCOPE_SUCCESS(success = true);
      KJ_ON_SCOPE_FAILURE(failure = true);
  
      EXPECT_FALSE(success);
      EXPECT_FALSE(failure);
    }
  
    EXPECT_TRUE(success);
    EXPECT_FALSE(failure);
  
    success = false;
    failure = false;
  
    try {
      KJ_ON_SCOPE_SUCCESS(success = true);
      KJ_ON_SCOPE_FAILURE(failure = true);
  
      EXPECT_FALSE(success);
      EXPECT_FALSE(failure);
  
      throw 1;
    } catch (int) {}
  
    EXPECT_FALSE(success);
    EXPECT_TRUE(failure);
  }
  #endif
  
  #if __GNUG__ || defined(__clang__)
  kj::String testStackTrace() __attribute__((noinline));
  #elif _MSC_VER
  __declspec(noinline) kj::String testStackTrace();
  #endif
  
  kj::String testStackTrace() {
    // getStackTrace() normally skips its immediate caller, so we wrap it in another layer.
    return getStackTrace();
  }
  
  KJ_TEST("getStackTrace() returns correct line number, not line + 1") {
    // Backtraces normally produce the return address of each stack frame, but that's usually the
    // address immediately after the one that made the call. As a result, it used to be that stack
    // traces often pointed to the line after the one that made a call, which was confusing. This
    // checks that this bug is fixed.
    //
    // This is not a very robust test, because:
    // 1) Since symbolic stack traces are not available in many situations (e.g. release builds
    //    lacking debug symbols, systems where addr2line isn't present, etc.), we only check that
    //    the stack trace does *not* contain the *wrong* value, rather than checking that it does
    //    contain the right one.
    // 2) This test only detects the problem if the call instruction to testStackTrace() is the
    //    *last* instruction attributed to its line of code. Whether or not this is true seems to be
    //    dependent on obscure compiler behavior. For example, below, it could only be the case if
    //    RVO is applied -- but in my testing, RVO does seem to be applied here. I tried several
    //    variations involving passing via an output parameter or a global variable rather than
    //    returning, but found some variations detected the problem and others didn't, essentially
    //    at random.
  
    auto trace = testStackTrace();
    auto wrong = kj::str("exception-test.c++:", __LINE__);
  
    KJ_ASSERT(strstr(trace.cStr(), wrong.cStr()) == nullptr, trace, wrong);
  }
  
  #if !KJ_NO_EXCEPTIONS
  KJ_TEST("InFlightExceptionIterator works") {
    bool caught = false;
    try {
      KJ_DEFER({
        try {
          KJ_FAIL_ASSERT("bar");
        } catch (const kj::Exception& e) {
          InFlightExceptionIterator iter;
          KJ_IF_MAYBE(e2, iter.next()) {
            KJ_EXPECT(e2 == &e, e2->getDescription());
          } else {
            KJ_FAIL_EXPECT("missing first exception");
          }
  
          KJ_IF_MAYBE(e2, iter.next()) {
            KJ_EXPECT(e2->getDescription() == "foo", e2->getDescription());
          } else {
            KJ_FAIL_EXPECT("missing second exception");
          }
  
          KJ_EXPECT(iter.next() == nullptr, "more than two exceptions");
  
          caught = true;
        }
      });
      KJ_FAIL_ASSERT("foo");
    } catch (const kj::Exception& e) {
      // expected
    }
  
    KJ_EXPECT(caught);
  }
  #endif
  
  KJ_TEST("computeRelativeTrace") {
    auto testCase = [](uint expectedPrefix,
                       ArrayPtr<const uintptr_t> trace, ArrayPtr<const uintptr_t> relativeTo) {
      auto tracePtr = KJ_MAP(x, trace) { return (void*)x; };
      auto relativeToPtr = KJ_MAP(x, relativeTo) { return (void*)x; };
  
      auto result = computeRelativeTrace(tracePtr, relativeToPtr);
      KJ_EXPECT(result.begin() == tracePtr.begin());
  
      KJ_EXPECT(result.size() == expectedPrefix, trace, relativeTo, result);
    };
  
    testCase(8,
        {1, 2, 3, 4, 5, 6, 7, 8},
        {8, 7, 6, 5, 4, 3, 2, 1});
  
    testCase(5,
        {1, 2, 3, 4, 5, 6, 7, 8},
        {8, 7, 6, 5, 5, 6, 7, 8});
  
    testCase(5,
        {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
        {8, 7, 6, 5, 5, 6, 7, 8});
  
    testCase(5,
        {1, 2, 3, 4, 5, 6, 7, 8, 6, 7, 8},
        {8, 7, 6, 5, 5, 6, 7, 8});
  
    testCase(9,
        {1, 2, 3, 4, 5, 6, 7, 8, 5, 5, 6, 7, 8},
        {8, 7, 6, 5, 5, 6, 7, 8});
  
    testCase(5,
        {1, 2, 3, 4, 5, 5, 6, 7, 8, 5, 6, 7, 8},
        {8, 7, 6, 5, 5, 6, 7, 8});
  
    testCase(5,
        {1, 2, 3, 4, 5, 6, 7, 8},
        {8, 7, 6, 5, 5, 6, 7, 8, 7, 8});
  
    testCase(5,
        {1, 2, 3, 4, 5, 6, 7, 8},
        {8, 7, 6, 5, 6, 7, 8, 7, 8});
  }
  
  }  // namespace
  }  // namespace _ (private)
  }  // namespace kj
  