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

debug-test.c++ (15097B)


      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 #ifndef _GNU_SOURCE
     23 #define _GNU_SOURCE
     24 #endif
     25 
     26 #include "debug.h"
     27 #include "exception.h"
     28 #include <kj/compat/gtest.h>
     29 #include <string>
     30 #include <stdio.h>
     31 #include <signal.h>
     32 #include <errno.h>
     33 #include <string.h>
     34 #include <exception>
     35 #include <stdlib.h>
     36 
     37 #include "miniposix.h"
     38 
     39 #if !_WIN32
     40 #include <sys/wait.h>
     41 #endif
     42 
     43 #if _MSC_VER && !defined(__clang__)
     44 #pragma warning(disable: 4996)
     45 // Warns that sprintf() is buffer-overrunny. Yeah, I know, it's cool.
     46 #endif
     47 
     48 namespace kj {
     49 namespace _ {  // private
     50 namespace {
     51 
     52 class MockException {};
     53 
     54 class MockExceptionCallback: public ExceptionCallback {
     55 public:
     56   ~MockExceptionCallback() {}
     57 
     58   std::string text;
     59 
     60   int outputPipe = -1;
     61 
     62   bool forkForDeathTest() {
     63     // This is called when exceptions are disabled.  We fork the process instead and then expect
     64     // the child to die.
     65 
     66 #if _WIN32
     67     // Windows doesn't support fork() or anything like it. Just skip the test.
     68     return false;
     69 
     70 #else
     71     int pipeFds[2];
     72     KJ_SYSCALL(pipe(pipeFds));
     73     pid_t child = fork();
     74     if (child == 0) {
     75       // This is the child!
     76       close(pipeFds[0]);
     77       outputPipe = pipeFds[1];
     78       text.clear();
     79       return true;
     80     } else {
     81       close(pipeFds[1]);
     82 
     83       // Read child error messages into our local buffer.
     84       char buf[1024];
     85       for (;;) {
     86         ssize_t n = read(pipeFds[0], buf, sizeof(buf));
     87         if (n < 0) {
     88           if (errno == EINTR) {
     89             continue;
     90           } else {
     91             break;
     92           }
     93         } else if (n == 0) {
     94           break;
     95         } else {
     96           text.append(buf, n);
     97         }
     98       }
     99 
    100       close(pipeFds[0]);
    101 
    102       // Get exit status.
    103       int status;
    104       KJ_SYSCALL(waitpid(child, &status, 0));
    105 
    106       EXPECT_TRUE(WIFEXITED(status));
    107       EXPECT_EQ(74, WEXITSTATUS(status));
    108 
    109       return false;
    110     }
    111 #endif  // _WIN32, else
    112   }
    113 
    114   void flush() {
    115     if (outputPipe != -1) {
    116       const char* pos = &*text.begin();
    117       const char* end = pos + text.size();
    118 
    119       while (pos < end) {
    120         miniposix::ssize_t n = miniposix::write(outputPipe, pos, end - pos);
    121         if (n < 0) {
    122           if (errno == EINTR) {
    123             continue;
    124           } else {
    125             break;  // Give up on error.
    126           }
    127         }
    128         pos += n;
    129       }
    130 
    131       text.clear();
    132     }
    133   }
    134 
    135   void onRecoverableException(Exception&& exception) override {
    136     text += "recoverable exception: ";
    137     auto what = str(exception);
    138     // Discard the stack trace.
    139     const char* end = strstr(what.cStr(), "\nstack: ");
    140     if (end == nullptr) {
    141       text += what.cStr();
    142     } else {
    143       text.append(what.cStr(), end);
    144     }
    145     text += '\n';
    146     flush();
    147   }
    148 
    149   void onFatalException(Exception&& exception) override {
    150     text += "fatal exception: ";
    151     auto what = str(exception);
    152     // Discard the stack trace.
    153     const char* end = strstr(what.cStr(), "\nstack: ");
    154     if (end == nullptr) {
    155       text += what.cStr();
    156     } else {
    157       text.append(what.cStr(), end);
    158     }
    159     text += '\n';
    160     flush();
    161 #if KJ_NO_EXCEPTIONS
    162     if (outputPipe >= 0) {
    163       // This is a child process.  We got what we want, now exit quickly without writing any
    164       // additional messages, with a status code that the parent will interpret as "exited in the
    165       // way we expected".
    166       _exit(74);
    167     }
    168 #else
    169     throw MockException();
    170 #endif
    171   }
    172 
    173   void logMessage(LogSeverity severity, const char* file, int line, int contextDepth,
    174                   String&& text) override {
    175     this->text += "log message: ";
    176     text = str(file, ":", line, ":+", contextDepth, ": ", severity, ": ", mv(text));
    177     this->text.append(text.begin(), text.end());
    178     this->text.append("\n");
    179   }
    180 };
    181 
    182 #define EXPECT_LOG_EQ(f, expText) do { \
    183   std::string text; \
    184   { \
    185     MockExceptionCallback mockCallback; \
    186     f(); \
    187     text = kj::mv(mockCallback.text); \
    188   } \
    189   EXPECT_EQ(expText, text); \
    190 } while(0)
    191 
    192 #if KJ_NO_EXCEPTIONS
    193 #define EXPECT_FATAL(code) if (mockCallback.forkForDeathTest()) { code; abort(); }
    194 #else
    195 #define EXPECT_FATAL(code) \
    196   try { code; KJ_FAIL_EXPECT("expected exception"); } \
    197   catch (MockException e) {} \
    198   catch (...) { KJ_FAIL_EXPECT("wrong exception"); }
    199 #endif
    200 
    201 std::string fileLine(std::string file, int line) {
    202   file = trimSourceFilename(file.c_str()).cStr();
    203 
    204   file += ':';
    205   char buffer[32];
    206   sprintf(buffer, "%d", line);
    207   file += buffer;
    208   return file;
    209 }
    210 
    211 TEST(Debug, Log) {
    212   int line;
    213 
    214   EXPECT_LOG_EQ([&](){
    215     KJ_LOG(WARNING, "Hello world!"); line = __LINE__;
    216   }, "log message: " + fileLine(__FILE__, line) + ":+0: warning: Hello world!\n");
    217 
    218   int i = 123;
    219   const char* str = "foo";
    220 
    221   EXPECT_LOG_EQ([&](){
    222     KJ_LOG(ERROR, i, str); line = __LINE__;
    223   }, "log message: " + fileLine(__FILE__, line) + ":+0: error: i = 123; str = foo\n");
    224 
    225   // kj::str() expressions are included literally.
    226   EXPECT_LOG_EQ([&](){
    227     KJ_LOG(ERROR, kj::str(i, str), "x"); line = __LINE__;
    228   }, "log message: " + fileLine(__FILE__, line) + ":+0: error: 123foo; x\n");
    229 
    230   EXPECT_LOG_EQ([&](){
    231     KJ_DBG("Some debug text."); line = __LINE__;
    232   }, "log message: " + fileLine(__FILE__, line) + ":+0: debug: Some debug text.\n");
    233 
    234   // INFO logging is disabled by default.
    235   EXPECT_LOG_EQ([&](){
    236     KJ_LOG(INFO, "Info."); line = __LINE__;
    237   }, "");
    238 
    239   // Enable it.
    240   Debug::setLogLevel(Debug::Severity::INFO);
    241   EXPECT_LOG_EQ([&](){
    242     KJ_LOG(INFO, "Some text."); line = __LINE__;
    243   }, "log message: " + fileLine(__FILE__, line) + ":+0: info: Some text.\n");
    244 
    245   // Back to default.
    246   Debug::setLogLevel(Debug::Severity::WARNING);
    247 
    248   EXPECT_LOG_EQ([&](){
    249     KJ_ASSERT(1 == 1);
    250   }, "");
    251 
    252   EXPECT_LOG_EQ([&](){
    253     EXPECT_FATAL(KJ_ASSERT(1 == 2)); line = __LINE__;
    254   }, "fatal exception: " + fileLine(__FILE__, line) + ": failed: expected 1 == 2 [1 == 2]\n");
    255 
    256   KJ_ASSERT(1 == 1) {
    257     ADD_FAILURE() << "Shouldn't call recovery code when check passes.";
    258     break;
    259   };
    260 
    261   bool recovered = false;
    262   EXPECT_LOG_EQ([&](){
    263     KJ_ASSERT(1 == 2, "1 is not 2") { recovered = true; break; } line = __LINE__;
    264   }, (
    265     "recoverable exception: " + fileLine(__FILE__, line) + ": "
    266     "failed: expected 1 == 2 [1 == 2]; 1 is not 2\n"
    267   ));
    268   EXPECT_TRUE(recovered);
    269 
    270   EXPECT_LOG_EQ([&](){
    271     EXPECT_FATAL(KJ_ASSERT(1 == 2, i, "hi", str)); line = __LINE__;
    272   }, (
    273     "fatal exception: " + fileLine(__FILE__, line) + ": "
    274         "failed: expected 1 == 2 [1 == 2]; i = 123; hi; str = foo\n"
    275   ));
    276 
    277   EXPECT_LOG_EQ([&](){
    278     EXPECT_FATAL(KJ_REQUIRE(1 == 2, i, "hi", str)); line = __LINE__;
    279   }, (
    280     "fatal exception: " + fileLine(__FILE__, line) + ": "
    281         "failed: expected 1 == 2 [1 == 2]; i = 123; hi; str = foo\n"
    282   ));
    283 
    284   EXPECT_LOG_EQ([&](){
    285     EXPECT_FATAL(KJ_FAIL_ASSERT("foo")); line = __LINE__;
    286   }, "fatal exception: " + fileLine(__FILE__, line) + ": failed: foo\n");
    287 }
    288 
    289 TEST(Debug, Exception) {
    290   int i = 123;
    291 
    292   int line = __LINE__; Exception exception = KJ_EXCEPTION(DISCONNECTED, "foo", i);
    293 
    294   EXPECT_EQ(Exception::Type::DISCONNECTED, exception.getType());
    295   EXPECT_TRUE(kj::StringPtr(__FILE__).endsWith(exception.getFile()));
    296   EXPECT_EQ(line, exception.getLine());
    297   EXPECT_EQ("foo; i = 123", exception.getDescription());
    298 }
    299 
    300 TEST(Debug, Catch) {
    301   int line;
    302 
    303   {
    304     // Catch recoverable as kj::Exception.
    305     Maybe<Exception> exception = kj::runCatchingExceptions([&](){
    306       line = __LINE__; KJ_FAIL_ASSERT("foo") { break; }
    307     });
    308 
    309     KJ_IF_MAYBE(e, exception) {
    310       String what = str(*e);
    311       KJ_IF_MAYBE(eol, what.findFirst('\n')) {
    312         what = kj::str(what.slice(0, *eol));
    313       }
    314       std::string text(what.cStr());
    315       EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
    316     } else {
    317       ADD_FAILURE() << "Expected exception.";
    318     }
    319   }
    320 
    321 #if !KJ_NO_EXCEPTIONS
    322   {
    323     // Catch fatal as kj::Exception.
    324     Maybe<Exception> exception = kj::runCatchingExceptions([&](){
    325       line = __LINE__; KJ_FAIL_ASSERT("foo");
    326     });
    327 
    328     KJ_IF_MAYBE(e, exception) {
    329       String what = str(*e);
    330       KJ_IF_MAYBE(eol, what.findFirst('\n')) {
    331         what = kj::str(what.slice(0, *eol));
    332       }
    333       std::string text(what.cStr());
    334       EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
    335     } else {
    336       ADD_FAILURE() << "Expected exception.";
    337     }
    338   }
    339 
    340   {
    341     // Catch as std::exception.
    342     try {
    343       line = __LINE__; KJ_FAIL_ASSERT("foo");
    344       KJ_KNOWN_UNREACHABLE(ADD_FAILURE() << "Expected exception.");
    345     } catch (const std::exception& e) {
    346       kj::StringPtr what = e.what();
    347       std::string text;
    348       KJ_IF_MAYBE(eol, what.findFirst('\n')) {
    349         text.assign(what.cStr(), *eol);
    350       } else {
    351         text.assign(what.cStr());
    352       }
    353       EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
    354     }
    355   }
    356 #endif
    357 }
    358 
    359 int mockSyscall(int i, int error = 0) {
    360   errno = error;
    361   return i;
    362 }
    363 
    364 TEST(Debug, Syscall) {
    365   int line;
    366 
    367   int i = 123;
    368   const char* str = "foo";
    369 
    370   EXPECT_LOG_EQ([&](){
    371     KJ_SYSCALL(mockSyscall(0));
    372     KJ_SYSCALL(mockSyscall(1));
    373   }, "");
    374 
    375   EXPECT_LOG_EQ([&](){
    376     EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, EBADF), i, "bar", str)); line = __LINE__;
    377   }, (
    378     "fatal exception: " + fileLine(__FILE__, line) +
    379         ": failed: mockSyscall(-1, EBADF): " + strerror(EBADF) +
    380         "; i = 123; bar; str = foo\n"
    381   ));
    382 
    383   EXPECT_LOG_EQ([&](){
    384     EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ECONNRESET), i, "bar", str)); line = __LINE__;
    385   }, (
    386     "fatal exception: " + fileLine(__FILE__, line) +
    387         ": disconnected: mockSyscall(-1, ECONNRESET): " + strerror(ECONNRESET) +
    388         "; i = 123; bar; str = foo\n"
    389   ));
    390 
    391   EXPECT_LOG_EQ([&](){
    392     EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ENOMEM), i, "bar", str)); line = __LINE__;
    393   }, (
    394     "fatal exception: " + fileLine(__FILE__, line) +
    395         ": overloaded: mockSyscall(-1, ENOMEM): " + strerror(ENOMEM) +
    396         "; i = 123; bar; str = foo\n"
    397   ));
    398 
    399   EXPECT_LOG_EQ([&](){
    400     EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ENOSYS), i, "bar", str)); line = __LINE__;
    401   }, (
    402     "fatal exception: " + fileLine(__FILE__, line) +
    403         ": unimplemented: mockSyscall(-1, ENOSYS): " + strerror(ENOSYS) +
    404         "; i = 123; bar; str = foo\n"
    405   ));
    406 
    407   int result = 0;
    408   bool recovered = false;
    409   EXPECT_LOG_EQ([&](){
    410     KJ_SYSCALL(result = mockSyscall(-2, EBADF), i, "bar", str) { recovered = true; break; } line = __LINE__;
    411   }, (
    412     "recoverable exception: " + fileLine(__FILE__, line) +
    413         ": failed: mockSyscall(-2, EBADF): " + strerror(EBADF) +
    414         "; i = 123; bar; str = foo\n"
    415   ));
    416   EXPECT_EQ(-2, result);
    417   EXPECT_TRUE(recovered);
    418 }
    419 
    420 TEST(Debug, Context) {
    421   int line;
    422   int line2;
    423   int cline;
    424   int cline2;
    425 
    426   EXPECT_LOG_EQ([&](){
    427     KJ_CONTEXT("foo"); cline = __LINE__;
    428 
    429     KJ_LOG(WARNING, "blah"); line = __LINE__;
    430     EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line2 = __LINE__;
    431   }, (
    432     "log message: " + fileLine(__FILE__, cline) + ":+0: info: context: foo\n\n"
    433         "log message: " + fileLine(__FILE__, line) + ":+1: warning: blah\n"
    434         "fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
    435          + fileLine(__FILE__, line2) + ": failed: bar\n"
    436   ));
    437 
    438   EXPECT_LOG_EQ([&](){
    439     KJ_CONTEXT("foo"); cline = __LINE__;
    440     {
    441       int i = 123;
    442       const char* str = "qux";
    443       KJ_CONTEXT("baz", i, "corge", str); cline2 = __LINE__;
    444 
    445       EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
    446     }
    447   }, (
    448     "fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
    449         + fileLine(__FILE__, cline2) + ": context: baz; i = 123; corge; str = qux\n"
    450         + fileLine(__FILE__, line) + ": failed: bar\n"
    451   ));
    452 
    453   EXPECT_LOG_EQ([&](){
    454     KJ_CONTEXT("foo"); cline = __LINE__;
    455     {
    456       int i = 123;
    457       const char* str = "qux";
    458       KJ_CONTEXT("baz", i, "corge", str); cline2 = __LINE__;
    459     }
    460     {
    461       KJ_CONTEXT("grault"); cline2 = __LINE__;
    462       EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
    463     }
    464   }, (
    465     "fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
    466         + fileLine(__FILE__, cline2) + ": context: grault\n"
    467         + fileLine(__FILE__, line) + ": failed: bar\n"
    468   ));
    469 }
    470 
    471 KJ_TEST("magic assert stringification") {
    472   {
    473     auto exception = KJ_ASSERT_NONNULL(kj::runCatchingExceptions([&]() {
    474       int foo = 123;
    475       int bar = 456;
    476       KJ_ASSERT(foo == bar) { break; }
    477     }));
    478 
    479     KJ_EXPECT(exception.getDescription() == "expected foo == bar [123 == 456]");
    480   }
    481 
    482   {
    483     auto exception = KJ_ASSERT_NONNULL(kj::runCatchingExceptions([&]() {
    484       auto foo = kj::str("hello");
    485       auto bar = kj::str("world!");
    486       KJ_ASSERT(foo == bar, foo.size(), bar.size()) { break; }
    487     }));
    488 
    489     KJ_EXPECT(exception.getDescription() ==
    490         "expected foo == bar [hello == world!]; foo.size() = 5; bar.size() = 6");
    491   }
    492 
    493   {
    494     auto exception = KJ_ASSERT_NONNULL(kj::runCatchingExceptions([&]() {
    495       KJ_ASSERT(kj::str("hello") == kj::str("world!")) { break; }
    496     }));
    497 
    498     KJ_EXPECT(exception.getDescription() ==
    499         "expected kj::str(\"hello\") == kj::str(\"world!\") [hello == world!]");
    500   }
    501 
    502   {
    503     auto exception = KJ_ASSERT_NONNULL(kj::runCatchingExceptions([&]() {
    504       int foo = 123;
    505       int bar = 456;
    506       KJ_ASSERT((foo == bar)) { break; }
    507     }));
    508 
    509     KJ_EXPECT(exception.getDescription() == "expected (foo == bar)");
    510   }
    511 
    512   // Test use of << on left side, which could create confusion.
    513   {
    514     auto exception = KJ_ASSERT_NONNULL(kj::runCatchingExceptions([&]() {
    515       int foo = 123;
    516       int bar = 456;
    517       KJ_ASSERT(foo << 2 == bar) { break; }
    518     }));
    519 
    520     KJ_EXPECT(exception.getDescription() == "expected foo << 2 == bar [492 == 456]");
    521   }
    522 
    523   // Test use of & on left side.
    524   {
    525     int foo = 4;
    526     KJ_ASSERT(foo & 4);
    527 
    528     auto exception = KJ_ASSERT_NONNULL(kj::runCatchingExceptions([&]() {
    529       KJ_ASSERT(foo & 2) { break; }
    530     }));
    531 
    532     KJ_EXPECT(exception.getDescription() == "expected foo & 2");
    533   }
    534 }
    535 
    536 }  // namespace
    537 }  // namespace _ (private)
    538 }  // namespace kj