1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 /*
32 * A C++ unit testing framework.
33 *
34 * For examples of basic usage, see mongo/unittest/unittest_test.cpp.
35 */
36
37 #pragma once
38
39 #include <cmath>
40 #include <sstream>
41 #include <string>
42 #include <utility>
43 #include <vector>
44
45 #include "mongo/base/status_with.h"
46 #include "mongo/logger/logstream_builder.h"
47 #include "mongo/logger/message_log_domain.h"
48 #include "mongo/stdx/functional.h"
49 #include "mongo/unittest/bson_test_util.h"
50 #include "mongo/unittest/unittest_helpers.h"
51 #include "mongo/util/assert_util.h"
52 #include "mongo/util/mongoutils/str.h"
53
54 /**
55 * Fail unconditionally, reporting the given message.
56 */
57 #define FAIL(MESSAGE) ::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, MESSAGE).stream()
58
59 /**
60 * Fails unless "EXPRESSION" is true.
61 */
62 #define ASSERT_TRUE(EXPRESSION) \
63 if (!(EXPRESSION)) \
64 FAIL("Expected: " #EXPRESSION)
65 #define ASSERT(EXPRESSION) ASSERT_TRUE(EXPRESSION)
66
67 /**
68 * Fails if "EXPRESSION" is true.
69 */
70 #define ASSERT_FALSE(EXPRESSION) ASSERT(!(EXPRESSION))
71
72 /**
73 * Asserts that a Status code is OK.
74 */
75 #define ASSERT_OK(EXPRESSION) ASSERT_EQUALS(::mongo::Status::OK(), (EXPRESSION))
76
77 /**
78 * Asserts that a status code is anything but OK.
79 */
80 #define ASSERT_NOT_OK(EXPRESSION) ASSERT_NOT_EQUALS(::mongo::Status::OK(), (EXPRESSION))
81
82 /*
83 * Binary comparison assertions.
84 */
85 #define ASSERT_EQUALS(a, b) ASSERT_EQ(a, b)
86 #define ASSERT_NOT_EQUALS(a, b) ASSERT_NE(a, b)
87 #define ASSERT_LESS_THAN(a, b) ASSERT_LT(a, b)
88 #define ASSERT_NOT_LESS_THAN(a, b) ASSERT_GTE(a, b)
89 #define ASSERT_GREATER_THAN(a, b) ASSERT_GT(a, b)
90 #define ASSERT_NOT_GREATER_THAN(a, b) ASSERT_LTE(a, b)
91 #define ASSERT_LESS_THAN_OR_EQUALS(a, b) ASSERT_LTE(a, b)
92 #define ASSERT_GREATER_THAN_OR_EQUALS(a, b) ASSERT_GTE(a, b)
93
94 #define ASSERT_EQ(a, b) _ASSERT_COMPARISON(EQ, a, b)
95 #define ASSERT_NE(a, b) _ASSERT_COMPARISON(NE, a, b)
96 #define ASSERT_LT(a, b) _ASSERT_COMPARISON(LT, a, b)
97 #define ASSERT_LTE(a, b) _ASSERT_COMPARISON(LTE, a, b)
98 #define ASSERT_GT(a, b) _ASSERT_COMPARISON(GT, a, b)
99 #define ASSERT_GTE(a, b) _ASSERT_COMPARISON(GTE, a, b)
100
101 /**
102 * Binary comparison utility macro. Do not use directly.
103 */
104 #define _ASSERT_COMPARISON(COMPARISON, a, b) \
105 if (::mongo::unittest::ComparisonAssertion_##COMPARISON ca = \
106 ::mongo::unittest::ComparisonAssertion_##COMPARISON(__FILE__, __LINE__, #a, #b, a, b)) \
107 ca.failure().stream()
108
109 /**
110 * Approximate equality assertion. Useful for comparisons on limited precision floating point
111 * values.
112 */
113 #define ASSERT_APPROX_EQUAL(a, b, ABSOLUTE_ERR) ASSERT_LTE(std::abs((a) - (b)), ABSOLUTE_ERR)
114
115 /**
116 * Verify that the evaluation of "EXPRESSION" throws an exception of type EXCEPTION_TYPE.
117 *
118 * If "EXPRESSION" throws no exception, or one that is neither of type "EXCEPTION_TYPE" nor
119 * of a subtype of "EXCEPTION_TYPE", the test is considered a failure and further evaluation
120 * halts.
121 */
122 #define ASSERT_THROWS(STATEMENT, EXCEPTION_TYPE) \
123 ASSERT_THROWS_WITH_CHECK(STATEMENT, EXCEPTION_TYPE, ([](const EXCEPTION_TYPE&) {}))
124
125 /**
126 * Behaves like ASSERT_THROWS, above, but also fails if calling what() on the thrown exception
127 * does not return a string equal to EXPECTED_WHAT.
128 */
129 #define ASSERT_THROWS_WHAT(STATEMENT, EXCEPTION_TYPE, EXPECTED_WHAT) \
130 ASSERT_THROWS_WITH_CHECK(STATEMENT, EXCEPTION_TYPE, ([&](const EXCEPTION_TYPE& ex) { \
131 ASSERT_EQ(::mongo::StringData(ex.what()), \
132 ::mongo::StringData(EXPECTED_WHAT)); \
133 }))
134
135 /**
136 * Behaves like ASSERT_THROWS, above, but also fails if calling getCode() on the thrown exception
137 * does not return an error code equal to EXPECTED_CODE.
138 */
139 #define ASSERT_THROWS_CODE(STATEMENT, EXCEPTION_TYPE, EXPECTED_CODE) \
140 ASSERT_THROWS_WITH_CHECK(STATEMENT, EXCEPTION_TYPE, ([&](const EXCEPTION_TYPE& ex) { \
141 ASSERT_EQ(ex.toStatus().code(), EXPECTED_CODE); \
142 }))
143
144 /**
145 * Behaves like ASSERT_THROWS, above, but also fails if calling getCode() on the thrown exception
146 * does not return an error code equal to EXPECTED_CODE or if calling what() on the thrown exception
147 * does not return a string equal to EXPECTED_WHAT.
148 */
149 #define ASSERT_THROWS_CODE_AND_WHAT(STATEMENT, EXCEPTION_TYPE, EXPECTED_CODE, EXPECTED_WHAT) \
150 ASSERT_THROWS_WITH_CHECK(STATEMENT, EXCEPTION_TYPE, ([&](const EXCEPTION_TYPE& ex) { \
151 ASSERT_EQ(ex.toStatus().code(), EXPECTED_CODE); \
152 ASSERT_EQ(::mongo::StringData(ex.what()), \
153 ::mongo::StringData(EXPECTED_WHAT)); \
154 }))
155
156 /**
157 * Behaves like ASSERT_THROWS, above, but also calls CHECK(caughtException) which may contain
158 * additional assertions.
159 */
160 #define ASSERT_THROWS_WITH_CHECK(STATEMENT, EXCEPTION_TYPE, CHECK) \
161 do { \
162 try { \
163 STATEMENT; \
164 FAIL("Expected statement " #STATEMENT " to throw " #EXCEPTION_TYPE \
165 " but it threw nothing."); \
166 } catch (const EXCEPTION_TYPE& ex) { \
167 CHECK(ex); \
168 } \
169 } while (false)
170
171 #define ASSERT_STRING_CONTAINS(BIG_STRING, CONTAINS) \
172 do { \
173 std::string myString(BIG_STRING); \
174 std::string myContains(CONTAINS); \
175 if (myString.find(myContains) == std::string::npos) { \
176 ::mongoutils::str::stream err; \
177 err << "Expected to find " #CONTAINS " (" << myContains << ") in " #BIG_STRING " (" \
178 << myString << ")"; \
179 ::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, err).stream(); \
180 } \
181 } while (false)
182
183 /**
184 * Construct a single test, named "TEST_NAME" within the test case "CASE_NAME".
185 *
186 * Usage:
187 *
188 * TEST(MyModuleTests, TestThatFooFailsOnErrors) {
189 * ASSERT_EQUALS(error_success, foo(invalidValue));
190 * }
191 */
192 #define TEST(CASE_NAME, TEST_NAME) \
193 class _TEST_TYPE_NAME(CASE_NAME, TEST_NAME) : public ::mongo::unittest::Test { \
194 private: \
195 virtual void _doTest(); \
196 \
197 static const RegistrationAgent<_TEST_TYPE_NAME(CASE_NAME, TEST_NAME)> _agent; \
198 }; \
199 const ::mongo::unittest::Test::RegistrationAgent<_TEST_TYPE_NAME(CASE_NAME, TEST_NAME)> \
200 _TEST_TYPE_NAME(CASE_NAME, TEST_NAME)::_agent(#CASE_NAME, #TEST_NAME); \
201 void _TEST_TYPE_NAME(CASE_NAME, TEST_NAME)::_doTest()
202
203 /**
204 * Construct a single test named TEST_NAME that has access to a common class (a "fixture")
205 * named "FIXTURE_NAME".
206 *
207 * Usage:
208 *
209 * class FixtureClass : public mongo::unittest::Test {
210 * protected:
211 * int myVar;
212 * void setUp() { myVar = 10; }
213 * };
214 *
215 * TEST(FixtureClass, TestThatUsesFixture) {
216 * ASSERT_EQUALS(10, myVar);
217 * }
218 */
219 #define TEST_F(FIXTURE_NAME, TEST_NAME) \
220 class _TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME) : public FIXTURE_NAME { \
221 private: \
222 virtual void _doTest(); \
223 \
224 static const RegistrationAgent<_TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)> _agent; \
225 }; \
226 const ::mongo::unittest::Test::RegistrationAgent<_TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)> \
227 _TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)::_agent(#FIXTURE_NAME, #TEST_NAME); \
228 void _TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)::_doTest()
229
230 /**
231 * Macro to construct a type name for a test, from its "CASE_NAME" and "TEST_NAME".
232 * Do not use directly in test code.
233 */
234 #define _TEST_TYPE_NAME(CASE_NAME, TEST_NAME) UnitTest__##CASE_NAME##__##TEST_NAME
235
236 namespace mongo {
237
238 namespace unittest {
239
240 class Result;
241
242 /**
243 * Gets a LogstreamBuilder for logging to the unittest log domain, which may have
244 * different target from the global log domain.
245 */
246 mongo::logger::LogstreamBuilder log();
247
248 /**
249 * Type representing the function composing a test.
250 */
251 typedef stdx::function<void(void)> TestFunction;
252
253 /**
254 * Container holding a test function and its name. Suites
255 * contain lists of these.
256 */
257 class TestHolder {
258 MONGO_DISALLOW_COPYING(TestHolder);
259
260 public:
TestHolder(const std::string & name,const TestFunction & fn)261 TestHolder(const std::string& name, const TestFunction& fn) : _name(name), _fn(fn) {}
262
~TestHolder()263 ~TestHolder() {}
run()264 void run() const {
265 _fn();
266 }
getName()267 std::string getName() const {
268 return _name;
269 }
270
271 private:
272 std::string _name;
273 TestFunction _fn;
274 };
275
276 /**
277 * Base type for unit test fixtures. Also, the default fixture type used
278 * by the TEST() macro.
279 */
280 class Test {
281 MONGO_DISALLOW_COPYING(Test);
282
283 public:
284 Test();
285 virtual ~Test();
286
287 void run();
288
289 protected:
290 /**
291 * Registration agent for adding tests to suites, used by TEST macro.
292 */
293 template <typename T>
294 class RegistrationAgent {
295 MONGO_DISALLOW_COPYING(RegistrationAgent);
296
297 public:
298 RegistrationAgent(const std::string& suiteName, const std::string& testName);
299 std::string getSuiteName() const;
300 std::string getTestName() const;
301
302 private:
303 const std::string _suiteName;
304 const std::string _testName;
305 };
306
307 /**
308 * This exception class is used to exercise the testing framework itself. If a test
309 * case throws it, the framework would not consider it an error.
310 */
311 class FixtureExceptionForTesting : public std::exception {};
312
313 /**
314 * Starts capturing messages logged by code under test.
315 *
316 * Log messages will still also go to their default destination; this
317 * code simply adds an additional sink for log messages.
318 *
319 * Clears any previously captured log lines.
320 */
321 void startCapturingLogMessages();
322
323 /**
324 * Stops capturing log messages logged by code under test.
325 */
326 void stopCapturingLogMessages();
327
328 /**
329 * Gets a vector of strings, one log line per string, captured since
330 * the last call to startCapturingLogMessages() in this test.
331 */
getCapturedLogMessages()332 const std::vector<std::string>& getCapturedLogMessages() const {
333 return _capturedLogMessages;
334 }
335
336 /**
337 * Returns the number of collected log lines containing "needle".
338 */
339 int64_t countLogLinesContaining(const std::string& needle);
340
341 /**
342 * Prints the captured log lines.
343 */
344 void printCapturedLogLines() const;
345
346 private:
347 /**
348 * Called on the test object before running the test.
349 */
350 virtual void setUp();
351
352 /**
353 * Called on the test object after running the test.
354 */
355 virtual void tearDown();
356
357 /**
358 * The test itself.
359 */
360 virtual void _doTest() = 0;
361
362 bool _isCapturingLogMessages;
363 std::vector<std::string> _capturedLogMessages;
364 logger::MessageLogDomain::AppenderHandle _captureAppenderHandle;
365 logger::MessageLogDomain::AppenderAutoPtr _captureAppender;
366 };
367
368 /**
369 * Representation of a collection of tests.
370 *
371 * One suite is constructed for each "CASE_NAME" when using the TEST macro.
372 * Additionally, tests that are part of dbtests are manually assigned to suites
373 * by the programmer by overriding setupTests() in a subclass of Suite. This
374 * approach is deprecated.
375 */
376 class Suite {
377 MONGO_DISALLOW_COPYING(Suite);
378
379 public:
380 Suite(const std::string& name);
381 virtual ~Suite();
382
383 template <class T>
add()384 void add() {
385 add<T>(demangleName(typeid(T)));
386 }
387
388 template <class T, typename A>
add(const A & a)389 void add(const A& a) {
390 add(demangleName(typeid(T)), stdx::bind(&Suite::runTestObjectWithArg<T, A>, a));
391 }
392
393 template <class T>
add(const std::string & name)394 void add(const std::string& name) {
395 add(name, &Suite::runTestObject<T>);
396 }
397
398 void add(const std::string& name, const TestFunction& testFn);
399
400 Result* run(const std::string& filter, int runsPerTest);
401
402 static int run(const std::vector<std::string>& suites,
403 const std::string& filter,
404 int runsPerTest);
405
406 /**
407 * Get a suite with the given name, creating it if necessary.
408 *
409 * The implementation of this function must be safe to call during the global static
410 * initialization block before main() executes.
411 */
412 static Suite* getSuite(const std::string& name);
413
414 protected:
415 virtual void setupTests();
416
417 private:
418 // TODO(C++11): Make this hold unique_ptrs.
419 typedef std::vector<std::shared_ptr<TestHolder>> TestHolderList;
420
421 template <typename T>
runTestObject()422 static void runTestObject() {
423 T testObj;
424 testObj.run();
425 }
426
427 template <typename T, typename A>
runTestObjectWithArg(const A & a)428 static void runTestObjectWithArg(const A& a) {
429 T testObj(a);
430 testObj.run();
431 }
432
433 std::string _name;
434 TestHolderList _tests;
435 bool _ran;
436
437 void registerSuite(const std::string& name, Suite* s);
438 };
439
440 // A type that makes it easy to declare a self registering suite for old style test
441 // declarations. Suites are self registering so this is *not* a memory leak.
442 template <typename T>
443 struct SuiteInstance {
SuiteInstanceSuiteInstance444 SuiteInstance() {
445 new T;
446 }
447
448 template <typename U>
SuiteInstanceSuiteInstance449 SuiteInstance(const U& u) {
450 new T(u);
451 }
452 };
453
454 /**
455 * Exception thrown when a test assertion fails.
456 *
457 * Typically thrown by helpers in the TestAssertion class and its ilk, below.
458 *
459 * NOTE(schwerin): This intentionally does _not_ extend std::exception, so that code under
460 * test that (foolishly?) catches std::exception won't swallow test failures. Doesn't
461 * protect you from code that foolishly catches ..., but you do what you can.
462 */
463 class TestAssertionFailureException {
464 public:
465 TestAssertionFailureException(const std::string& theFile,
466 unsigned theLine,
467 const std::string& theMessage);
468
getFile()469 const std::string& getFile() const {
470 return _file;
471 }
getLine()472 unsigned getLine() const {
473 return _line;
474 }
getMessage()475 const std::string& getMessage() const {
476 return _message;
477 }
setMessage(const std::string & message)478 void setMessage(const std::string& message) {
479 _message = message;
480 }
481
what()482 const std::string& what() const {
483 return getMessage();
484 }
485
486 std::string toString() const;
487
488 private:
489 std::string _file;
490 unsigned _line;
491 std::string _message;
492 };
493
494 class TestAssertionFailure {
495 public:
496 TestAssertionFailure(const std::string& file, unsigned line, const std::string& message);
497 TestAssertionFailure(const TestAssertionFailure& other);
498 ~TestAssertionFailure() noexcept(false);
499
500 TestAssertionFailure& operator=(const TestAssertionFailure& other);
501
502 std::ostream& stream();
503
504 private:
505 TestAssertionFailureException _exception;
506 std::ostringstream _stream;
507 bool _enabled;
508 };
509
510 #define DECLARE_COMPARISON_ASSERTION(NAME, OPERATOR) \
511 class ComparisonAssertion_##NAME { \
512 typedef void (ComparisonAssertion_##NAME::*bool_type)() const; \
513 \
514 public: \
515 template <typename A, typename B> \
516 ComparisonAssertion_##NAME(const std::string& theFile, \
517 unsigned theLine, \
518 StringData aExpression, \
519 StringData bExpression, \
520 const A& a, \
521 const B& b) { \
522 if (a OPERATOR b) { \
523 return; \
524 } \
525 std::ostringstream os; \
526 os << "Expected " << aExpression << " " #OPERATOR " " << bExpression << " (" << a \
527 << " " #OPERATOR " " << b << ")"; \
528 _assertion.reset(new TestAssertionFailure(theFile, theLine, os.str())); \
529 } \
530 operator bool_type() const { \
531 return _assertion.get() ? &ComparisonAssertion_##NAME::comparison_failed : NULL; \
532 } \
533 TestAssertionFailure failure() { \
534 return *_assertion; \
535 } \
536 \
537 private: \
538 void comparison_failed() const {} \
539 std::shared_ptr<TestAssertionFailure> _assertion; \
540 }
541
542 DECLARE_COMPARISON_ASSERTION(EQ, ==);
543 DECLARE_COMPARISON_ASSERTION(NE, !=);
544 DECLARE_COMPARISON_ASSERTION(LT, <);
545 DECLARE_COMPARISON_ASSERTION(LTE, <=);
546 DECLARE_COMPARISON_ASSERTION(GT, >);
547 DECLARE_COMPARISON_ASSERTION(GTE, >=);
548 #undef DECLARE_COMPARISON_ASSERTION
549
550 /**
551 * Get the value out of a StatusWith<T>, or throw an exception if it is not OK.
552 */
553 template <typename T>
assertGet(const StatusWith<T> & swt)554 const T& assertGet(const StatusWith<T>& swt) {
555 ASSERT_OK(swt.getStatus());
556 return swt.getValue();
557 }
558
559 template <typename T>
assertGet(StatusWith<T> && swt)560 T assertGet(StatusWith<T>&& swt) {
561 ASSERT_OK(swt.getStatus());
562 return std::move(swt.getValue());
563 }
564
565 /**
566 * Return a list of suite names.
567 */
568 std::vector<std::string> getAllSuiteNames();
569
570
alwaysTrue()571 inline bool alwaysTrue() {
572 return true;
573 }
574
575 } // namespace unittest
576 } // namespace mongo
577
578 #include "mongo/unittest/unittest-inl.h"
579