blob: 49ddbc0a5d8c1cc8958c927de4ab7a22d5722e6f [file] [log] [blame]
Austin Schuh906616c2019-01-21 20:25:11 -08001// Copyright (c) 2009, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30// Author: Shinichiro Hamaji
31// (based on googletest: http://code.google.com/p/googletest/)
32
33#ifdef GOOGLETEST_H__
34#error You must not include this file twice.
35#endif
36#define GOOGLETEST_H__
37
38#include "utilities.h"
39
40#include <ctype.h>
41#include <setjmp.h>
42#include <time.h>
43
44#include <map>
45#include <sstream>
46#include <string>
47#include <vector>
48
49#include <stdio.h>
50#include <stdlib.h>
51
52#include <sys/types.h>
53#include <sys/stat.h>
54#include <fcntl.h>
55#ifdef HAVE_UNISTD_H
56# include <unistd.h>
57#endif
58
59#include "base/commandlineflags.h"
60
61using std::map;
62using std::string;
63using std::vector;
64
65_START_GOOGLE_NAMESPACE_
66
67extern GOOGLE_GLOG_DLL_DECL void (*g_logging_fail_func)();
68
69_END_GOOGLE_NAMESPACE_
70
71#undef GOOGLE_GLOG_DLL_DECL
72#define GOOGLE_GLOG_DLL_DECL
73
74static inline string GetTempDir() {
75#ifndef OS_WINDOWS
76 return "/tmp";
77#else
78 char tmp[MAX_PATH];
79 GetTempPathA(MAX_PATH, tmp);
80 return tmp;
81#endif
82}
83
84#if defined(OS_WINDOWS) && defined(_MSC_VER) && !defined(TEST_SRC_DIR)
85// The test will run in glog/vsproject/<project name>
86// (e.g., glog/vsproject/logging_unittest).
87static const char TEST_SRC_DIR[] = "../..";
88#elif !defined(TEST_SRC_DIR)
89# warning TEST_SRC_DIR should be defined in config.h
90static const char TEST_SRC_DIR[] = ".";
91#endif
92
93static const uint32_t PTR_TEST_VALUE = 0x12345678;
94
95DEFINE_string(test_tmpdir, GetTempDir(), "Dir we use for temp files");
96DEFINE_string(test_srcdir, TEST_SRC_DIR,
97 "Source-dir root, needed to find glog_unittest_flagfile");
98DEFINE_bool(run_benchmark, false, "If true, run benchmarks");
99#ifdef NDEBUG
100DEFINE_int32(benchmark_iters, 100000000, "Number of iterations per benchmark");
101#else
102DEFINE_int32(benchmark_iters, 100000, "Number of iterations per benchmark");
103#endif
104
105#ifdef HAVE_LIB_GTEST
106# include <gtest/gtest.h>
107// Use our ASSERT_DEATH implementation.
108# undef ASSERT_DEATH
109# undef ASSERT_DEBUG_DEATH
110using testing::InitGoogleTest;
111#else
112
113_START_GOOGLE_NAMESPACE_
114
115void InitGoogleTest(int*, char**);
116
117void InitGoogleTest(int*, char**) {}
118
119// The following is some bare-bones testing infrastructure
120
121#define EXPECT_TRUE(cond) \
122 do { \
123 if (!(cond)) { \
124 fprintf(stderr, "Check failed: %s\n", #cond); \
125 exit(1); \
126 } \
127 } while (0)
128
129#define EXPECT_FALSE(cond) EXPECT_TRUE(!(cond))
130
131#define EXPECT_OP(op, val1, val2) \
132 do { \
133 if (!((val1) op (val2))) { \
134 fprintf(stderr, "Check failed: %s %s %s\n", #val1, #op, #val2); \
135 exit(1); \
136 } \
137 } while (0)
138
139#define EXPECT_EQ(val1, val2) EXPECT_OP(==, val1, val2)
140#define EXPECT_NE(val1, val2) EXPECT_OP(!=, val1, val2)
141#define EXPECT_GT(val1, val2) EXPECT_OP(>, val1, val2)
142#define EXPECT_LT(val1, val2) EXPECT_OP(<, val1, val2)
143
144#define EXPECT_NAN(arg) \
145 do { \
146 if (!isnan(arg)) { \
147 fprintf(stderr, "Check failed: isnan(%s)\n", #arg); \
148 exit(1); \
149 } \
150 } while (0)
151
152#define EXPECT_INF(arg) \
153 do { \
154 if (!isinf(arg)) { \
155 fprintf(stderr, "Check failed: isinf(%s)\n", #arg); \
156 exit(1); \
157 } \
158 } while (0)
159
160#define EXPECT_DOUBLE_EQ(val1, val2) \
161 do { \
162 if (((val1) < (val2) - 0.001 || (val1) > (val2) + 0.001)) { \
163 fprintf(stderr, "Check failed: %s == %s\n", #val1, #val2); \
164 exit(1); \
165 } \
166 } while (0)
167
168#define EXPECT_STREQ(val1, val2) \
169 do { \
170 if (strcmp((val1), (val2)) != 0) { \
171 fprintf(stderr, "Check failed: streq(%s, %s)\n", #val1, #val2); \
172 exit(1); \
173 } \
174 } while (0)
175
176vector<void (*)()> g_testlist; // the tests to run
177
178#define TEST(a, b) \
179 struct Test_##a##_##b { \
180 Test_##a##_##b() { g_testlist.push_back(&Run); } \
181 static void Run() { FlagSaver fs; RunTest(); } \
182 static void RunTest(); \
183 }; \
184 static Test_##a##_##b g_test_##a##_##b; \
185 void Test_##a##_##b::RunTest()
186
187
188static inline int RUN_ALL_TESTS() {
189 vector<void (*)()>::const_iterator it;
190 for (it = g_testlist.begin(); it != g_testlist.end(); ++it) {
191 (*it)();
192 }
193 fprintf(stderr, "Passed %d tests\n\nPASS\n", (int)g_testlist.size());
194 return 0;
195}
196
197_END_GOOGLE_NAMESPACE_
198
199#endif // ! HAVE_LIB_GTEST
200
201_START_GOOGLE_NAMESPACE_
202
203static bool g_called_abort;
204static jmp_buf g_jmp_buf;
205static inline void CalledAbort() {
206 g_called_abort = true;
207 longjmp(g_jmp_buf, 1);
208}
209
210#ifdef OS_WINDOWS
211// TODO(hamaji): Death test somehow doesn't work in Windows.
212#define ASSERT_DEATH(fn, msg)
213#else
214#define ASSERT_DEATH(fn, msg) \
215 do { \
216 g_called_abort = false; \
217 /* in logging.cc */ \
218 void (*original_logging_fail_func)() = g_logging_fail_func; \
219 g_logging_fail_func = &CalledAbort; \
220 if (!setjmp(g_jmp_buf)) fn; \
221 /* set back to their default */ \
222 g_logging_fail_func = original_logging_fail_func; \
223 if (!g_called_abort) { \
224 fprintf(stderr, "Function didn't die (%s): %s\n", msg, #fn); \
225 exit(1); \
226 } \
227 } while (0)
228#endif
229
230#ifdef NDEBUG
231#define ASSERT_DEBUG_DEATH(fn, msg)
232#else
233#define ASSERT_DEBUG_DEATH(fn, msg) ASSERT_DEATH(fn, msg)
234#endif // NDEBUG
235
236// Benchmark tools.
237
238#define BENCHMARK(n) static BenchmarkRegisterer __benchmark_ ## n (#n, &n);
239
240map<string, void (*)(int)> g_benchlist; // the benchmarks to run
241
242class BenchmarkRegisterer {
243 public:
244 BenchmarkRegisterer(const char* name, void (*function)(int iters)) {
245 EXPECT_TRUE(g_benchlist.insert(std::make_pair(name, function)).second);
246 }
247};
248
249static inline void RunSpecifiedBenchmarks() {
250 if (!FLAGS_run_benchmark) {
251 return;
252 }
253
254 int iter_cnt = FLAGS_benchmark_iters;
255 puts("Benchmark\tTime(ns)\tIterations");
256 for (map<string, void (*)(int)>::const_iterator iter = g_benchlist.begin();
257 iter != g_benchlist.end();
258 ++iter) {
259 clock_t start = clock();
260 iter->second(iter_cnt);
261 double elapsed_ns =
262 ((double)clock() - start) / CLOCKS_PER_SEC * 1000*1000*1000;
263 printf("%s\t%8.2lf\t%10d\n",
264 iter->first.c_str(), elapsed_ns / iter_cnt, iter_cnt);
265 }
266 puts("");
267}
268
269// ----------------------------------------------------------------------
270// Golden file functions
271// ----------------------------------------------------------------------
272
273class CapturedStream {
274 public:
275 CapturedStream(int fd, const string & filename) :
276 fd_(fd),
277 uncaptured_fd_(-1),
278 filename_(filename) {
279 Capture();
280 }
281
282 ~CapturedStream() {
283 if (uncaptured_fd_ != -1) {
284 CHECK(close(uncaptured_fd_) != -1);
285 }
286 }
287
288 // Start redirecting output to a file
289 void Capture() {
290 // Keep original stream for later
291 CHECK(uncaptured_fd_ == -1) << ", Stream " << fd_ << " already captured!";
292 uncaptured_fd_ = dup(fd_);
293 CHECK(uncaptured_fd_ != -1);
294
295 // Open file to save stream to
296 int cap_fd = open(filename_.c_str(),
297 O_CREAT | O_TRUNC | O_WRONLY,
298 S_IRUSR | S_IWUSR);
299 CHECK(cap_fd != -1);
300
301 // Send stdout/stderr to this file
302 fflush(NULL);
303 CHECK(dup2(cap_fd, fd_) != -1);
304 CHECK(close(cap_fd) != -1);
305 }
306
307 // Remove output redirection
308 void StopCapture() {
309 // Restore original stream
310 if (uncaptured_fd_ != -1) {
311 fflush(NULL);
312 CHECK(dup2(uncaptured_fd_, fd_) != -1);
313 }
314 }
315
316 const string & filename() const { return filename_; }
317
318 private:
319 int fd_; // file descriptor being captured
320 int uncaptured_fd_; // where the stream was originally being sent to
321 string filename_; // file where stream is being saved
322};
323static CapturedStream * s_captured_streams[STDERR_FILENO+1];
324// Redirect a file descriptor to a file.
325// fd - Should be STDOUT_FILENO or STDERR_FILENO
326// filename - File where output should be stored
327static inline void CaptureTestOutput(int fd, const string & filename) {
328 CHECK((fd == STDOUT_FILENO) || (fd == STDERR_FILENO));
329 CHECK(s_captured_streams[fd] == NULL);
330 s_captured_streams[fd] = new CapturedStream(fd, filename);
331}
332static inline void CaptureTestStderr() {
333 CaptureTestOutput(STDERR_FILENO, FLAGS_test_tmpdir + "/captured.err");
334}
335// Return the size (in bytes) of a file
336static inline size_t GetFileSize(FILE * file) {
337 fseek(file, 0, SEEK_END);
338 return static_cast<size_t>(ftell(file));
339}
340// Read the entire content of a file as a string
341static inline string ReadEntireFile(FILE * file) {
342 const size_t file_size = GetFileSize(file);
343 char * const buffer = new char[file_size];
344
345 size_t bytes_last_read = 0; // # of bytes read in the last fread()
346 size_t bytes_read = 0; // # of bytes read so far
347
348 fseek(file, 0, SEEK_SET);
349
350 // Keep reading the file until we cannot read further or the
351 // pre-determined file size is reached.
352 do {
353 bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file);
354 bytes_read += bytes_last_read;
355 } while (bytes_last_read > 0 && bytes_read < file_size);
356
357 const string content = string(buffer, buffer+bytes_read);
358 delete[] buffer;
359
360 return content;
361}
362// Get the captured stdout (when fd is STDOUT_FILENO) or stderr (when
363// fd is STDERR_FILENO) as a string
364static inline string GetCapturedTestOutput(int fd) {
365 CHECK(fd == STDOUT_FILENO || fd == STDERR_FILENO);
366 CapturedStream * const cap = s_captured_streams[fd];
367 CHECK(cap)
368 << ": did you forget CaptureTestStdout() or CaptureTestStderr()?";
369
370 // Make sure everything is flushed.
371 cap->StopCapture();
372
373 // Read the captured file.
374 FILE * const file = fopen(cap->filename().c_str(), "r");
375 const string content = ReadEntireFile(file);
376 fclose(file);
377
378 delete cap;
379 s_captured_streams[fd] = NULL;
380
381 return content;
382}
383// Get the captured stderr of a test as a string.
384static inline string GetCapturedTestStderr() {
385 return GetCapturedTestOutput(STDERR_FILENO);
386}
387
388// Check if the string is [IWEF](\d{4}|DATE)
389static inline bool IsLoggingPrefix(const string& s) {
390 if (s.size() != 5) return false;
391 if (!strchr("IWEF", s[0])) return false;
392 for (int i = 1; i <= 4; ++i) {
393 if (!isdigit(s[i]) && s[i] != "DATE"[i-1]) return false;
394 }
395 return true;
396}
397
398// Convert log output into normalized form.
399//
400// Example:
401// I0102 030405 logging_unittest.cc:345] RAW: vlog -1
402// => IDATE TIME__ logging_unittest.cc:LINE] RAW: vlog -1
403static inline string MungeLine(const string& line) {
404 std::istringstream iss(line);
405 string before, logcode_date, time, thread_lineinfo;
406 iss >> logcode_date;
407 while (!IsLoggingPrefix(logcode_date)) {
408 before += " " + logcode_date;
409 if (!(iss >> logcode_date)) {
410 // We cannot find the header of log output.
411 return before;
412 }
413 }
414 if (!before.empty()) before += " ";
415 iss >> time;
416 iss >> thread_lineinfo;
417 CHECK(!thread_lineinfo.empty());
418 if (thread_lineinfo[thread_lineinfo.size() - 1] != ']') {
419 // We found thread ID.
420 string tmp;
421 iss >> tmp;
422 CHECK(!tmp.empty());
423 CHECK_EQ(']', tmp[tmp.size() - 1]);
424 thread_lineinfo = "THREADID " + tmp;
425 }
426 size_t index = thread_lineinfo.find(':');
427 CHECK_NE(string::npos, index);
428 thread_lineinfo = thread_lineinfo.substr(0, index+1) + "LINE]";
429 string rest;
430 std::getline(iss, rest);
431 return (before + logcode_date[0] + "DATE TIME__ " + thread_lineinfo +
432 MungeLine(rest));
433}
434
435static inline void StringReplace(string* str,
436 const string& oldsub,
437 const string& newsub) {
438 size_t pos = str->find(oldsub);
439 if (pos != string::npos) {
440 str->replace(pos, oldsub.size(), newsub.c_str());
441 }
442}
443
444static inline string Munge(const string& filename) {
445 FILE* fp = fopen(filename.c_str(), "rb");
446 CHECK(fp != NULL) << filename << ": couldn't open";
447 char buf[4096];
448 string result;
449 while (fgets(buf, 4095, fp)) {
450 string line = MungeLine(buf);
451 char null_str[256];
452 char ptr_str[256];
453 sprintf(null_str, "%p", static_cast<void*>(NULL));
454 sprintf(ptr_str, "%p", reinterpret_cast<void*>(PTR_TEST_VALUE));
455
456 StringReplace(&line, "__NULLP__", null_str);
457 StringReplace(&line, "__PTRTEST__", ptr_str);
458
459 StringReplace(&line, "__SUCCESS__", StrError(0));
460 StringReplace(&line, "__ENOENT__", StrError(ENOENT));
461 StringReplace(&line, "__EINTR__", StrError(EINTR));
462 StringReplace(&line, "__ENXIO__", StrError(ENXIO));
463 StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC));
464 result += line + "\n";
465 }
466 fclose(fp);
467 return result;
468}
469
470static inline void WriteToFile(const string& body, const string& file) {
471 FILE* fp = fopen(file.c_str(), "wb");
472 fwrite(body.data(), 1, body.size(), fp);
473 fclose(fp);
474}
475
476static inline bool MungeAndDiffTestStderr(const string& golden_filename) {
477 CapturedStream* cap = s_captured_streams[STDERR_FILENO];
478 CHECK(cap) << ": did you forget CaptureTestStderr()?";
479
480 cap->StopCapture();
481
482 // Run munge
483 const string captured = Munge(cap->filename());
484 const string golden = Munge(golden_filename);
485 if (captured != golden) {
486 fprintf(stderr,
487 "Test with golden file failed. We'll try to show the diff:\n");
488 string munged_golden = golden_filename + ".munged";
489 WriteToFile(golden, munged_golden);
490 string munged_captured = cap->filename() + ".munged";
491 WriteToFile(captured, munged_captured);
492#ifdef OS_WINDOWS
493 string diffcmd("fc " + munged_golden + " " + munged_captured);
494#else
495 string diffcmd("diff -u " + munged_golden + " " + munged_captured);
496#endif
497 if (system(diffcmd.c_str()) != 0) {
498 fprintf(stderr, "diff command was failed.\n");
499 }
500 unlink(munged_golden.c_str());
501 unlink(munged_captured.c_str());
502 return false;
503 }
504 LOG(INFO) << "Diff was successful";
505 return true;
506}
507
508// Save flags used from logging_unittest.cc.
509#ifndef HAVE_LIB_GFLAGS
510struct FlagSaver {
511 FlagSaver()
512 : v_(FLAGS_v),
513 stderrthreshold_(FLAGS_stderrthreshold),
514 logtostderr_(FLAGS_logtostderr),
515 alsologtostderr_(FLAGS_alsologtostderr) {}
516 ~FlagSaver() {
517 FLAGS_v = v_;
518 FLAGS_stderrthreshold = stderrthreshold_;
519 FLAGS_logtostderr = logtostderr_;
520 FLAGS_alsologtostderr = alsologtostderr_;
521 }
522 int v_;
523 int stderrthreshold_;
524 bool logtostderr_;
525 bool alsologtostderr_;
526};
527#endif
528
529class Thread {
530 public:
531 virtual ~Thread() {}
532
533 void SetJoinable(bool) {}
534#if defined(OS_WINDOWS) && !defined(OS_CYGWIN)
535 void Start() {
536 handle_ = CreateThread(NULL,
537 0,
538 (LPTHREAD_START_ROUTINE)&Thread::InvokeThread,
539 (LPVOID)this,
540 0,
541 &th_);
542 CHECK(handle_) << "CreateThread";
543 }
544 void Join() {
545 WaitForSingleObject(handle_, INFINITE);
546 }
547#elif defined(HAVE_PTHREAD)
548 void Start() {
549 pthread_create(&th_, NULL, &Thread::InvokeThread, this);
550 }
551 void Join() {
552 pthread_join(th_, NULL);
553 }
554#else
555# error No thread implementation.
556#endif
557
558 protected:
559 virtual void Run() = 0;
560
561 private:
562 static void* InvokeThread(void* self) {
563 ((Thread*)self)->Run();
564 return NULL;
565 }
566
567#if defined(OS_WINDOWS) && !defined(OS_CYGWIN)
568 HANDLE handle_;
569 DWORD th_;
570#else
571 pthread_t th_;
572#endif
573};
574
575static inline void SleepForMilliseconds(int t) {
576#ifndef OS_WINDOWS
577 usleep(t * 1000);
578#else
579 Sleep(t);
580#endif
581}
582
583// Add hook for operator new to ensure there are no memory allocation.
584
585void (*g_new_hook)() = NULL;
586
587_END_GOOGLE_NAMESPACE_
588
589void* operator new(size_t size) {
590 if (GOOGLE_NAMESPACE::g_new_hook) {
591 GOOGLE_NAMESPACE::g_new_hook();
592 }
593 return malloc(size);
594}
595
596void* operator new[](size_t size) {
597 return ::operator new(size);
598}
599
600void operator delete(void* p) {
601 free(p);
602}
603
604void operator delete[](void* p) {
605 ::operator delete(p);
606}