capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

exception-test.c++ (8682B)


      1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #include "exception.h"
     23 #include "debug.h"
     24 #include <kj/compat/gtest.h>
     25 #include <stdexcept>
     26 #include <stdint.h>
     27 
     28 namespace kj {
     29 namespace _ {  // private
     30 namespace {
     31 
     32 TEST(Exception, TrimSourceFilename) {
     33 #if _WIN32
     34   EXPECT_TRUE(trimSourceFilename(__FILE__) == "kj/exception-test.c++" ||
     35               trimSourceFilename(__FILE__) == "kj\\exception-test.c++");
     36 #else
     37   EXPECT_EQ(trimSourceFilename(__FILE__), "kj/exception-test.c++");
     38 #endif
     39 }
     40 
     41 TEST(Exception, RunCatchingExceptions) {
     42   bool recovered = false;
     43   Maybe<Exception> e = kj::runCatchingExceptions([&]() {
     44     KJ_FAIL_ASSERT("foo") {
     45       break;
     46     }
     47     recovered = true;
     48   });
     49 
     50 #if KJ_NO_EXCEPTIONS
     51   EXPECT_TRUE(recovered);
     52 #else
     53   EXPECT_FALSE(recovered);
     54 #endif
     55 
     56   KJ_IF_MAYBE(ex, e) {
     57     EXPECT_EQ("foo", ex->getDescription());
     58   } else {
     59     ADD_FAILURE() << "Expected exception";
     60   }
     61 }
     62 
     63 #if !KJ_NO_EXCEPTIONS
     64 TEST(Exception, RunCatchingExceptionsStdException) {
     65   Maybe<Exception> e = kj::runCatchingExceptions([&]() {
     66     throw std::logic_error("foo");
     67   });
     68 
     69   KJ_IF_MAYBE(ex, e) {
     70     EXPECT_EQ("std::exception: foo", ex->getDescription());
     71   } else {
     72     ADD_FAILURE() << "Expected exception";
     73   }
     74 }
     75 
     76 TEST(Exception, RunCatchingExceptionsOtherException) {
     77   Maybe<Exception> e = kj::runCatchingExceptions([&]() {
     78     throw 123;
     79   });
     80 
     81   KJ_IF_MAYBE(ex, e) {
     82 #if __GNUC__ && !KJ_NO_RTTI
     83     EXPECT_EQ("unknown non-KJ exception of type: int", ex->getDescription());
     84 #else
     85     EXPECT_EQ("unknown non-KJ exception", ex->getDescription());
     86 #endif
     87   } else {
     88     ADD_FAILURE() << "Expected exception";
     89   }
     90 }
     91 #endif
     92 
     93 #if !KJ_NO_EXCEPTIONS
     94 // We skip this test when exceptions are disabled because making it no-exceptions-safe defeats
     95 // the purpose of the test: recoverable exceptions won't throw inside a destructor in the first
     96 // place.
     97 
     98 class ThrowingDestructor: public UnwindDetector {
     99 public:
    100   ~ThrowingDestructor() noexcept(false) {
    101     catchExceptionsIfUnwinding([]() {
    102       KJ_FAIL_ASSERT("this is a test, not a real bug");
    103     });
    104   }
    105 };
    106 
    107 TEST(Exception, UnwindDetector) {
    108   // If no other exception is happening, ThrowingDestructor's destructor throws one.
    109   Maybe<Exception> e = kj::runCatchingExceptions([&]() {
    110     ThrowingDestructor t;
    111   });
    112 
    113   KJ_IF_MAYBE(ex, e) {
    114     EXPECT_EQ("this is a test, not a real bug", ex->getDescription());
    115   } else {
    116     ADD_FAILURE() << "Expected exception";
    117   }
    118 
    119   // If another exception is happening, ThrowingDestructor's destructor's exception is squelched.
    120   e = kj::runCatchingExceptions([&]() {
    121     ThrowingDestructor t;
    122     KJ_FAIL_ASSERT("baz") {
    123       break;
    124     }
    125   });
    126 
    127   KJ_IF_MAYBE(ex, e) {
    128     EXPECT_EQ("baz", ex->getDescription());
    129   } else {
    130     ADD_FAILURE() << "Expected exception";
    131   }
    132 }
    133 #endif
    134 
    135 #if !__MINGW32__  // Inexplicably crashes when exception is thrown from constructor.
    136 TEST(Exception, ExceptionCallbackMustBeOnStack) {
    137   KJ_EXPECT_THROW_MESSAGE("must be allocated on the stack", new ExceptionCallback);
    138 }
    139 #endif  // !__MINGW32__
    140 
    141 #if !KJ_NO_EXCEPTIONS
    142 TEST(Exception, ScopeSuccessFail) {
    143   bool success = false;
    144   bool failure = false;
    145 
    146   {
    147     KJ_ON_SCOPE_SUCCESS(success = true);
    148     KJ_ON_SCOPE_FAILURE(failure = true);
    149 
    150     EXPECT_FALSE(success);
    151     EXPECT_FALSE(failure);
    152   }
    153 
    154   EXPECT_TRUE(success);
    155   EXPECT_FALSE(failure);
    156 
    157   success = false;
    158   failure = false;
    159 
    160   try {
    161     KJ_ON_SCOPE_SUCCESS(success = true);
    162     KJ_ON_SCOPE_FAILURE(failure = true);
    163 
    164     EXPECT_FALSE(success);
    165     EXPECT_FALSE(failure);
    166 
    167     throw 1;
    168   } catch (int) {}
    169 
    170   EXPECT_FALSE(success);
    171   EXPECT_TRUE(failure);
    172 }
    173 #endif
    174 
    175 #if __GNUG__ || defined(__clang__)
    176 kj::String testStackTrace() __attribute__((noinline));
    177 #elif _MSC_VER
    178 __declspec(noinline) kj::String testStackTrace();
    179 #endif
    180 
    181 kj::String testStackTrace() {
    182   // getStackTrace() normally skips its immediate caller, so we wrap it in another layer.
    183   return getStackTrace();
    184 }
    185 
    186 KJ_TEST("getStackTrace() returns correct line number, not line + 1") {
    187   // Backtraces normally produce the return address of each stack frame, but that's usually the
    188   // address immediately after the one that made the call. As a result, it used to be that stack
    189   // traces often pointed to the line after the one that made a call, which was confusing. This
    190   // checks that this bug is fixed.
    191   //
    192   // This is not a very robust test, because:
    193   // 1) Since symbolic stack traces are not available in many situations (e.g. release builds
    194   //    lacking debug symbols, systems where addr2line isn't present, etc.), we only check that
    195   //    the stack trace does *not* contain the *wrong* value, rather than checking that it does
    196   //    contain the right one.
    197   // 2) This test only detects the problem if the call instruction to testStackTrace() is the
    198   //    *last* instruction attributed to its line of code. Whether or not this is true seems to be
    199   //    dependent on obscure complier behavior. For example, below, it could only be the case if
    200   //    RVO is applied -- but in my testing, RVO does seem to be applied here. I tried several
    201   //    variations involving passing via an output parameter or a global variable rather than
    202   //    returning, but found some variations detected the problem and others didn't, essentially
    203   //    at random.
    204 
    205   auto trace = testStackTrace();
    206   auto wrong = kj::str("exception-test.c++:", __LINE__);
    207 
    208   KJ_ASSERT(strstr(trace.cStr(), wrong.cStr()) == nullptr, trace, wrong);
    209 }
    210 
    211 #if !KJ_NO_EXCEPTIONS
    212 KJ_TEST("InFlightExceptionIterator works") {
    213   bool caught = false;
    214   try {
    215     KJ_DEFER({
    216       try {
    217         KJ_FAIL_ASSERT("bar");
    218       } catch (const kj::Exception& e) {
    219         InFlightExceptionIterator iter;
    220         KJ_IF_MAYBE(e2, iter.next()) {
    221           KJ_EXPECT(e2 == &e, e2->getDescription());
    222         } else {
    223           KJ_FAIL_EXPECT("missing first exception");
    224         }
    225 
    226         KJ_IF_MAYBE(e2, iter.next()) {
    227           KJ_EXPECT(e2->getDescription() == "foo", e2->getDescription());
    228         } else {
    229           KJ_FAIL_EXPECT("missing second exception");
    230         }
    231 
    232         KJ_EXPECT(iter.next() == nullptr, "more than two exceptions");
    233 
    234         caught = true;
    235       }
    236     });
    237     KJ_FAIL_ASSERT("foo");
    238   } catch (const kj::Exception& e) {
    239     // expected
    240   }
    241 
    242   KJ_EXPECT(caught);
    243 }
    244 #endif
    245 
    246 KJ_TEST("computeRelativeTrace") {
    247   auto testCase = [](uint expectedPrefix,
    248                      ArrayPtr<const uintptr_t> trace, ArrayPtr<const uintptr_t> relativeTo) {
    249     auto tracePtr = KJ_MAP(x, trace) { return (void*)x; };
    250     auto relativeToPtr = KJ_MAP(x, relativeTo) { return (void*)x; };
    251 
    252     auto result = computeRelativeTrace(tracePtr, relativeToPtr);
    253     KJ_EXPECT(result.begin() == tracePtr.begin());
    254 
    255     KJ_EXPECT(result.size() == expectedPrefix, trace, relativeTo, result);
    256   };
    257 
    258   testCase(8,
    259       {1, 2, 3, 4, 5, 6, 7, 8},
    260       {8, 7, 6, 5, 4, 3, 2, 1});
    261 
    262   testCase(5,
    263       {1, 2, 3, 4, 5, 6, 7, 8},
    264       {8, 7, 6, 5, 5, 6, 7, 8});
    265 
    266   testCase(5,
    267       {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
    268       {8, 7, 6, 5, 5, 6, 7, 8});
    269 
    270   testCase(5,
    271       {1, 2, 3, 4, 5, 6, 7, 8, 6, 7, 8},
    272       {8, 7, 6, 5, 5, 6, 7, 8});
    273 
    274   testCase(9,
    275       {1, 2, 3, 4, 5, 6, 7, 8, 5, 5, 6, 7, 8},
    276       {8, 7, 6, 5, 5, 6, 7, 8});
    277 
    278   testCase(5,
    279       {1, 2, 3, 4, 5, 5, 6, 7, 8, 5, 6, 7, 8},
    280       {8, 7, 6, 5, 5, 6, 7, 8});
    281 
    282   testCase(5,
    283       {1, 2, 3, 4, 5, 6, 7, 8},
    284       {8, 7, 6, 5, 5, 6, 7, 8, 7, 8});
    285 
    286   testCase(5,
    287       {1, 2, 3, 4, 5, 6, 7, 8},
    288       {8, 7, 6, 5, 6, 7, 8, 7, 8});
    289 }
    290 
    291 }  // namespace
    292 }  // namespace _ (private)
    293 }  // namespace kj