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