debug-test.c++ 12.9 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// 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:
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// 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.
21

Kenton Varda's avatar
Kenton Varda committed
22
#include "debug.h"
23 24 25 26
#include "exception.h"
#include <gtest/gtest.h>
#include <string>
#include <stdio.h>
27
#include <signal.h>
28 29
#include <errno.h>
#include <string.h>
30
#include <exception>
31

32 33
#include "miniposix.h"

34
#if !_WIN32
35
#include <sys/wait.h>
36
#endif
37

38 39 40 41 42
#if _MSC_VER
#pragma warning(disable: 4996)
// Warns that sprintf() is buffer-overrunny. Yeah, I know, it's cool.
#endif

43
namespace kj {
44
namespace _ {  // private
45 46 47 48 49 50 51 52 53 54
namespace {

class MockException {};

class MockExceptionCallback: public ExceptionCallback {
public:
  ~MockExceptionCallback() {}

  std::string text;

55 56 57 58 59 60
  int outputPipe = -1;

  bool forkForDeathTest() {
    // This is called when exceptions are disabled.  We fork the process instead and then expect
    // the child to die.

61 62 63 64 65
#if _WIN32
    // Windows doesn't support fork() or anything like it. Just skip the test.
    return false;

#else
66
    int pipeFds[2];
Kenton Varda's avatar
Kenton Varda committed
67
    KJ_SYSCALL(pipe(pipeFds));
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
    pid_t child = fork();
    if (child == 0) {
      // This is the child!
      close(pipeFds[0]);
      outputPipe = pipeFds[1];
      return true;
    } else {
      close(pipeFds[1]);

      // Read child error messages into our local buffer.
      char buf[1024];
      for (;;) {
        ssize_t n = read(pipeFds[0], buf, sizeof(buf));
        if (n < 0) {
          if (errno == EINTR) {
            continue;
          } else {
            break;
          }
        } else if (n == 0) {
          break;
        } else {
          text.append(buf, n);
        }
      }

      close(pipeFds[0]);

      // Get exit status.
      int status;
      do {
        if (waitpid(child, &status, 0) < 0) {
          if (errno == EINTR) {
            continue;
          } else {
            ADD_FAILURE() << "waidpid: " << strerror(errno);
            return false;
          }
        }
      } while (false);

      EXPECT_TRUE(WIFEXITED(status));
      EXPECT_EQ(74, WEXITSTATUS(status));

      return false;
    }
114
#endif  // _WIN32, else
115 116 117 118 119 120 121 122
  }

  void flush() {
    if (outputPipe != -1) {
      const char* pos = &*text.begin();
      const char* end = pos + text.size();

      while (pos < end) {
123
        miniposix::ssize_t n = miniposix::write(outputPipe, pos, end - pos);
124 125 126 127 128 129 130 131 132 133 134 135 136 137
        if (n < 0) {
          if (errno == EINTR) {
            continue;
          } else {
            break;  // Give up on error.
          }
        }
        pos += n;
      }

      text.clear();
    }
  }

138 139
  void onRecoverableException(Exception&& exception) override {
    text += "recoverable exception: ";
140
    auto what = str(exception);
141 142
    // Discard the stack trace.
    const char* end = strstr(what.cStr(), "\nstack: ");
143
    if (end == nullptr) {
144
      text += what.cStr();
145
    } else {
146
      text.append(what.cStr(), end);
147
    }
148
    text += '\n';
149
    flush();
150 151 152 153
  }

  void onFatalException(Exception&& exception) override {
    text += "fatal exception: ";
154
    auto what = str(exception);
155 156
    // Discard the stack trace.
    const char* end = strstr(what.cStr(), "\nstack: ");
157
    if (end == nullptr) {
158
      text += what.cStr();
159
    } else {
160
      text.append(what.cStr(), end);
161
    }
162
    text += '\n';
163 164 165 166 167 168 169 170 171
    flush();
#if KJ_NO_EXCEPTIONS
    if (outputPipe >= 0) {
      // This is a child process.  We got what we want, now exit quickly without writing any
      // additional messages, with a status code that the parent will interpret as "exited in the
      // way we expected".
      _exit(74);
    }
#else
172
    throw MockException();
173
#endif
174 175
  }

176
  void logMessage(const char* file, int line, int contextDepth, String&& text) override {
177
    this->text += "log message: ";
178
    text = str(file, ":", line, ":+", contextDepth, ": ", mv(text));
179 180 181 182
    this->text.append(text.begin(), text.end());
  }
};

183 184 185 186 187 188
#if KJ_NO_EXCEPTIONS
#define EXPECT_FATAL(code) if (mockCallback.forkForDeathTest()) { code; abort(); }
#else
#define EXPECT_FATAL(code) EXPECT_THROW(code, MockException);
#endif

189 190 191 192 193 194 195 196
std::string fileLine(std::string file, int line) {
  file += ':';
  char buffer[32];
  sprintf(buffer, "%d", line);
  file += buffer;
  return file;
}

197
TEST(Debug, Log) {
198 199 200
  MockExceptionCallback mockCallback;
  int line;

201
  KJ_LOG(WARNING, "Hello world!"); line = __LINE__;
202
  EXPECT_EQ("log message: " + fileLine(__FILE__, line) + ":+0: warning: Hello world!\n",
203 204 205 206 207 208
            mockCallback.text);
  mockCallback.text.clear();

  int i = 123;
  const char* str = "foo";

209
  KJ_LOG(ERROR, i, str); line = __LINE__;
210
  EXPECT_EQ("log message: " + fileLine(__FILE__, line) + ":+0: error: i = 123; str = foo\n",
211 212 213
            mockCallback.text);
  mockCallback.text.clear();

214
  KJ_DBG("Some debug text."); line = __LINE__;
215
  EXPECT_EQ("log message: " + fileLine(__FILE__, line) + ":+0: debug: Some debug text.\n",
216 217 218 219 220 221 222 223 224
            mockCallback.text);
  mockCallback.text.clear();

  // INFO logging is disabled by default.
  KJ_LOG(INFO, "Info."); line = __LINE__;
  EXPECT_EQ("", mockCallback.text);
  mockCallback.text.clear();

  // Enable it.
Kenton Varda's avatar
Kenton Varda committed
225
  Debug::setLogLevel(Debug::Severity::INFO);
226
  KJ_LOG(INFO, "Some text."); line = __LINE__;
227
  EXPECT_EQ("log message: " + fileLine(__FILE__, line) + ":+0: info: Some text.\n",
228 229 230 231
            mockCallback.text);
  mockCallback.text.clear();

  // Back to default.
Kenton Varda's avatar
Kenton Varda committed
232
  Debug::setLogLevel(Debug::Severity::WARNING);
233

234
  KJ_ASSERT(1 == 1);
235
  EXPECT_FATAL(KJ_ASSERT(1 == 2)); line = __LINE__;
236
  EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": failed: expected "
237 238 239
            "1 == 2\n", mockCallback.text);
  mockCallback.text.clear();

240
  KJ_ASSERT(1 == 1) {
241
    ADD_FAILURE() << "Shouldn't call recovery code when check passes.";
242
    break;
243 244 245
  };

  bool recovered = false;
246
  KJ_ASSERT(1 == 2, "1 is not 2") { recovered = true; break; } line = __LINE__;
247
  EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) + ": failed: expected "
248 249 250 251
            "1 == 2; 1 is not 2\n", mockCallback.text);
  EXPECT_TRUE(recovered);
  mockCallback.text.clear();

252
  EXPECT_FATAL(KJ_ASSERT(1 == 2, i, "hi", str)); line = __LINE__;
253
  EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": failed: expected "
254 255 256
            "1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
  mockCallback.text.clear();

257
  EXPECT_FATAL(KJ_REQUIRE(1 == 2, i, "hi", str)); line = __LINE__;
258
  EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": failed: expected "
259 260
            "1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
  mockCallback.text.clear();
261

262
  EXPECT_FATAL(KJ_FAIL_ASSERT("foo")); line = __LINE__;
263
  EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": failed: foo\n",
264 265 266 267
            mockCallback.text);
  mockCallback.text.clear();
}

268 269 270 271 272 273 274 275 276 277 278
TEST(Debug, Exception) {
  int i = 123;

  int line = __LINE__; Exception exception = KJ_EXCEPTION(DISCONNECTED, "foo", i);

  EXPECT_EQ(Exception::Type::DISCONNECTED, exception.getType());
  EXPECT_STREQ(__FILE__, exception.getFile());
  EXPECT_EQ(line, exception.getLine());
  EXPECT_EQ("foo; i = 123", exception.getDescription());
}

279
TEST(Debug, Catch) {
280 281
  int line;

282 283 284 285 286 287 288 289
  {
    // Catch recoverable as kj::Exception.
    Maybe<Exception> exception = kj::runCatchingExceptions([&](){
      line = __LINE__; KJ_FAIL_ASSERT("foo") { break; }
    });

    KJ_IF_MAYBE(e, exception) {
      String what = str(*e);
290 291 292 293
      KJ_IF_MAYBE(eol, what.findFirst('\n')) {
        what = kj::str(what.slice(0, *eol));
      }
      std::string text(what.cStr());
294
      EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
295 296 297
    } else {
      ADD_FAILURE() << "Expected exception.";
    }
298 299
  }

300
#if !KJ_NO_EXCEPTIONS
301 302 303 304 305 306 307 308
  {
    // Catch fatal as kj::Exception.
    Maybe<Exception> exception = kj::runCatchingExceptions([&](){
      line = __LINE__; KJ_FAIL_ASSERT("foo");
    });

    KJ_IF_MAYBE(e, exception) {
      String what = str(*e);
309 310 311 312
      KJ_IF_MAYBE(eol, what.findFirst('\n')) {
        what = kj::str(what.slice(0, *eol));
      }
      std::string text(what.cStr());
313
      EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
314 315 316 317 318 319 320 321 322 323 324
    } else {
      ADD_FAILURE() << "Expected exception.";
    }
  }

  {
    // Catch as std::exception.
    try {
      line = __LINE__; KJ_FAIL_ASSERT("foo");
      ADD_FAILURE() << "Expected exception.";
    } catch (const std::exception& e) {
325
      kj::StringPtr what = e.what();
Kenton Varda's avatar
Kenton Varda committed
326
      std::string text;
327 328 329 330 331
      KJ_IF_MAYBE(eol, what.findFirst('\n')) {
        text.assign(what.cStr(), *eol);
      } else {
        text.assign(what.cStr());
      }
332
      EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
333
    }
334
  }
335
#endif
336 337
}

338 339 340 341 342
int mockSyscall(int i, int error = 0) {
  errno = error;
  return i;
}

343
TEST(Debug, Syscall) {
344 345 346 347 348 349
  MockExceptionCallback mockCallback;
  int line;

  int i = 123;
  const char* str = "foo";

350 351 352 353 354
  KJ_SYSCALL(mockSyscall(0));
  KJ_SYSCALL(mockSyscall(1));

  EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, EBADF), i, "bar", str)); line = __LINE__;
  EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) +
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
            ": failed: mockSyscall(-1, EBADF): " + strerror(EBADF) +
            "; i = 123; bar; str = foo\n", mockCallback.text);
  mockCallback.text.clear();

  EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ECONNRESET), i, "bar", str)); line = __LINE__;
  EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) +
            ": disconnected: mockSyscall(-1, ECONNRESET): " + strerror(ECONNRESET) +
            "; i = 123; bar; str = foo\n", mockCallback.text);
  mockCallback.text.clear();

  EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ENOMEM), i, "bar", str)); line = __LINE__;
  EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) +
            ": overloaded: mockSyscall(-1, ENOMEM): " + strerror(ENOMEM) +
            "; i = 123; bar; str = foo\n", mockCallback.text);
  mockCallback.text.clear();

  EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ENOSYS), i, "bar", str)); line = __LINE__;
  EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) +
            ": unimplemented: mockSyscall(-1, ENOSYS): " + strerror(ENOSYS) +
374
            "; i = 123; bar; str = foo\n", mockCallback.text);
375 376 377 378
  mockCallback.text.clear();

  int result = 0;
  bool recovered = false;
379 380
  KJ_SYSCALL(result = mockSyscall(-2, EBADF), i, "bar", str) { recovered = true; break; } line = __LINE__;
  EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) +
381
            ": failed: mockSyscall(-2, EBADF): " + strerror(EBADF) +
382
            "; i = 123; bar; str = foo\n", mockCallback.text);
383
  EXPECT_EQ(-2, result);
384
  EXPECT_TRUE(recovered);
385 386
}

387
TEST(Debug, Context) {
388 389 390
  MockExceptionCallback mockCallback;

  {
391
    KJ_CONTEXT("foo"); int cline = __LINE__;
392

393 394 395 396 397 398
    KJ_LOG(WARNING, "blah"); int line = __LINE__;
    EXPECT_EQ("log message: " + fileLine(__FILE__, cline) + ":+0: context: foo\n"
              "log message: " + fileLine(__FILE__, line) + ":+1: warning: blah\n",
              mockCallback.text);
    mockCallback.text.clear();

399
    EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
400
    EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
401
              + fileLine(__FILE__, line) + ": failed: bar\n",
402 403 404 405 406 407
              mockCallback.text);
    mockCallback.text.clear();

    {
      int i = 123;
      const char* str = "qux";
408
      KJ_CONTEXT("baz", i, "corge", str); int cline2 = __LINE__;
409
      EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
410 411 412

      EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
                + fileLine(__FILE__, cline2) + ": context: baz; i = 123; corge; str = qux\n"
413
                + fileLine(__FILE__, line) + ": failed: bar\n",
414 415 416 417 418
                mockCallback.text);
      mockCallback.text.clear();
    }

    {
419
      KJ_CONTEXT("grault"); int cline2 = __LINE__;
420
      EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
421 422 423

      EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
                + fileLine(__FILE__, cline2) + ": context: grault\n"
424
                + fileLine(__FILE__, line) + ": failed: bar\n",
425 426 427 428 429 430
                mockCallback.text);
      mockCallback.text.clear();
    }
  }
}

431
}  // namespace
432
}  // namespace _ (private)
433
}  // namespace kj