diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-07-28 18:16:39 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-07-28 18:27:27 -0400 |
commit | b66e993f1c742518d9b5e93b0d8a5f8255a4127c (patch) | |
tree | 55e6fed05333d2d37f34586726a342ed7f7dbc29 /src/mongo/unittest | |
parent | 314a22e93f283ab80e650618cbd3ed8babb8510f (diff) | |
download | mongo-b66e993f1c742518d9b5e93b0d8a5f8255a4127c.tar.gz |
SERVER-18579: Clang-Format - reformat code, no comment reflow
Diffstat (limited to 'src/mongo/unittest')
-rw-r--r-- | src/mongo/unittest/crutch.cpp | 42 | ||||
-rw-r--r-- | src/mongo/unittest/fixture_test.cpp | 125 | ||||
-rw-r--r-- | src/mongo/unittest/temp_dir.cpp | 103 | ||||
-rw-r--r-- | src/mongo/unittest/temp_dir.h | 61 | ||||
-rw-r--r-- | src/mongo/unittest/unittest-inl.h | 14 | ||||
-rw-r--r-- | src/mongo/unittest/unittest.cpp | 646 | ||||
-rw-r--r-- | src/mongo/unittest/unittest.h | 708 | ||||
-rw-r--r-- | src/mongo/unittest/unittest_helpers.cpp | 16 | ||||
-rw-r--r-- | src/mongo/unittest/unittest_helpers.h | 14 | ||||
-rw-r--r-- | src/mongo/unittest/unittest_main.cpp | 4 | ||||
-rw-r--r-- | src/mongo/unittest/unittest_test.cpp | 169 |
11 files changed, 953 insertions, 949 deletions
diff --git a/src/mongo/unittest/crutch.cpp b/src/mongo/unittest/crutch.cpp index 59859207e5b..a2cb2f7849b 100644 --- a/src/mongo/unittest/crutch.cpp +++ b/src/mongo/unittest/crutch.cpp @@ -41,36 +41,36 @@ namespace mongo { - bool inShutdown() { - return false; - } +bool inShutdown() { + return false; +} - class OperationContext; +class OperationContext; - DBClientBase* createDirectClient(OperationContext* txn) { - fassertFailed(17249); - return NULL; - } +DBClientBase* createDirectClient(OperationContext* txn) { + fassertFailed(17249); + return NULL; +} - bool haveLocalShardingInfo(const std::string& ns) { - return false; - } +bool haveLocalShardingInfo(const std::string& ns) { + return false; +} - void dbexit(ExitCode rc, const char *why) { - invariant(!"unittests shouldn't call dbexit"); - } +void dbexit(ExitCode rc, const char* why) { + invariant(!"unittests shouldn't call dbexit"); +} - void exitCleanly(ExitCode code) { - invariant(!"unittests shouldn't call exitCleanly"); - } +void exitCleanly(ExitCode code) { + invariant(!"unittests shouldn't call exitCleanly"); +} #ifdef _WIN32 - void signalShutdown() {} +void signalShutdown() {} namespace ntservice { - bool shouldStartService() { - return false; - } +bool shouldStartService() { + return false; +} } #endif diff --git a/src/mongo/unittest/fixture_test.cpp b/src/mongo/unittest/fixture_test.cpp index 3a31dab3219..f39b8536ba6 100644 --- a/src/mongo/unittest/fixture_test.cpp +++ b/src/mongo/unittest/fixture_test.cpp @@ -10,79 +10,76 @@ namespace { - class TestFixture : public mongo::unittest::Test { - protected: - int _myVar; - static int _num_set_ups; - static int _num_tear_downs; - - void setUp() { - _num_set_ups++; - _myVar = 10; - } - - void tearDown() { - _num_tear_downs++; - _myVar = 0; - } - - int inc() { - return ++_myVar; - } - - void throwSpecialException() { - throw FixtureExceptionForTesting(); - } - - }; - - int TestFixture::_num_set_ups = 0; - int TestFixture::_num_tear_downs = 0; - - // NOTE: - // Test cases should not be designed that depend on the order they appear. But because - // we're testing the test framework itself, we do not follow this rule here and require the - // following four tests to be in that order. - - // vvvvvvvvvvvvvvvvvvvvvvvv Do not add tests below - - // This needs to be the very first test. Please, see NOTE above. - TEST_F(TestFixture, SetUpTest) { - ASSERT_EQUALS(_num_set_ups, 1); - ASSERT_EQUALS(_num_tear_downs, 0); +class TestFixture : public mongo::unittest::Test { +protected: + int _myVar; + static int _num_set_ups; + static int _num_tear_downs; + + void setUp() { + _num_set_ups++; + _myVar = 10; } - // This needs to be the second test. Please, see NOTE above. - TEST_F(TestFixture, TearDownTest) { - ASSERT_EQUALS(_num_set_ups, 2); - ASSERT_EQUALS(_num_tear_downs, 1); + void tearDown() { + _num_tear_downs++; + _myVar = 0; } - // This needs to be the third/fourth test. Please, see NOTE above. We are - // finishing a test case by throwing an exception. Normally, the framework - // would treat this as an error. But what we'd like here is to make sure - // that the fixture tear down routines were called in that case. - TEST_F(TestFixture, Throwing) { - throwSpecialException(); + int inc() { + return ++_myVar; } - TEST_F(TestFixture, TearDownAfterThrowing ) { - // Make sure tear down was called in the test above this. - ASSERT_EQUALS(_num_tear_downs, 3); + + void throwSpecialException() { + throw FixtureExceptionForTesting(); } +}; - // ^^^^^^^^^^^^^^^^^^^^^^^^ Do not add test above +int TestFixture::_num_set_ups = 0; +int TestFixture::_num_tear_downs = 0; - // New tests may be added below. +// NOTE: +// Test cases should not be designed that depend on the order they appear. But because +// we're testing the test framework itself, we do not follow this rule here and require the +// following four tests to be in that order. - TEST_F(TestFixture, VariableAndMethodAccessTest) { - ASSERT_EQUALS(10, _myVar); - ASSERT_EQUALS(11, inc()); - } +// vvvvvvvvvvvvvvvvvvvvvvvv Do not add tests below - class EmptyFixture : public mongo::unittest::Test { - }; +// This needs to be the very first test. Please, see NOTE above. +TEST_F(TestFixture, SetUpTest) { + ASSERT_EQUALS(_num_set_ups, 1); + ASSERT_EQUALS(_num_tear_downs, 0); +} - TEST_F(EmptyFixture, EmptyTest) { - } +// This needs to be the second test. Please, see NOTE above. +TEST_F(TestFixture, TearDownTest) { + ASSERT_EQUALS(_num_set_ups, 2); + ASSERT_EQUALS(_num_tear_downs, 1); +} + +// This needs to be the third/fourth test. Please, see NOTE above. We are +// finishing a test case by throwing an exception. Normally, the framework +// would treat this as an error. But what we'd like here is to make sure +// that the fixture tear down routines were called in that case. +TEST_F(TestFixture, Throwing) { + throwSpecialException(); +} +TEST_F(TestFixture, TearDownAfterThrowing) { + // Make sure tear down was called in the test above this. + ASSERT_EQUALS(_num_tear_downs, 3); +} + +// ^^^^^^^^^^^^^^^^^^^^^^^^ Do not add test above + +// New tests may be added below. + +TEST_F(TestFixture, VariableAndMethodAccessTest) { + ASSERT_EQUALS(10, _myVar); + ASSERT_EQUALS(11, inc()); +} + +class EmptyFixture : public mongo::unittest::Test {}; + +TEST_F(EmptyFixture, EmptyTest) {} -} // unnamed namespace +} // unnamed namespace diff --git a/src/mongo/unittest/temp_dir.cpp b/src/mongo/unittest/temp_dir.cpp index 55850bad7bd..f70052e5863 100644 --- a/src/mongo/unittest/temp_dir.cpp +++ b/src/mongo/unittest/temp_dir.cpp @@ -44,71 +44,68 @@ namespace mongo { - using std::string; +using std::string; namespace unittest { - namespace str = mongoutils::str; - namespace moe = mongo::optionenvironment; +namespace str = mongoutils::str; +namespace moe = mongo::optionenvironment; namespace { - boost::filesystem::path defaultRoot; - - MONGO_GENERAL_STARTUP_OPTIONS_REGISTER(TempDirOptions)(InitializerContext* context) { - moe::startupOptions.addOptionChaining("tempPath", - "tempPath", - moe::String, - "directory to place mongo::TempDir subdirectories"); - return Status::OK(); +boost::filesystem::path defaultRoot; + +MONGO_GENERAL_STARTUP_OPTIONS_REGISTER(TempDirOptions)(InitializerContext* context) { + moe::startupOptions.addOptionChaining( + "tempPath", "tempPath", moe::String, "directory to place mongo::TempDir subdirectories"); + return Status::OK(); +} + +MONGO_INITIALIZER(SetTempDirDefaultRoot)(InitializerContext* context) { + if (moe::startupOptionsParsed.count("tempPath")) { + defaultRoot = moe::startupOptionsParsed["tempPath"].as<string>(); + } else { + defaultRoot = boost::filesystem::temp_directory_path(); } - MONGO_INITIALIZER(SetTempDirDefaultRoot)(InitializerContext* context) { - if (moe::startupOptionsParsed.count("tempPath")) { - defaultRoot = moe::startupOptionsParsed["tempPath"].as<string>(); - } else { - defaultRoot = boost::filesystem::temp_directory_path(); - } - - if (!boost::filesystem::exists(defaultRoot)) { - return Status(ErrorCodes::BadValue, - str::stream() << "Attempted to use a tempPath (" << defaultRoot.string() - << ") that doesn't exist"); - } - - if (!boost::filesystem::is_directory(defaultRoot)) { - return Status(ErrorCodes::BadValue, - str::stream() << "Attempted to use a tempPath (" << defaultRoot.string() - << ") that exists, but isn't a directory"); - } - return Status::OK(); + if (!boost::filesystem::exists(defaultRoot)) { + return Status(ErrorCodes::BadValue, + str::stream() << "Attempted to use a tempPath (" << defaultRoot.string() + << ") that doesn't exist"); } -} - TempDir::TempDir(const std::string& namePrefix) { - fassert(17146, namePrefix.find_first_of("/\\") == std::string::npos); + if (!boost::filesystem::is_directory(defaultRoot)) { + return Status(ErrorCodes::BadValue, + str::stream() << "Attempted to use a tempPath (" << defaultRoot.string() + << ") that exists, but isn't a directory"); + } + return Status::OK(); +} +} - // This gives the dir name 64 bits of randomness. - const boost::filesystem::path dirName = - boost::filesystem::unique_path(namePrefix + "-%%%%-%%%%-%%%%-%%%%"); +TempDir::TempDir(const std::string& namePrefix) { + fassert(17146, namePrefix.find_first_of("/\\") == std::string::npos); - _path = (defaultRoot / dirName).string(); + // This gives the dir name 64 bits of randomness. + const boost::filesystem::path dirName = + boost::filesystem::unique_path(namePrefix + "-%%%%-%%%%-%%%%-%%%%"); - bool createdNewDirectory = boost::filesystem::create_directory(_path); - if (!createdNewDirectory) { - error() << "unique path (" << _path << ") already existed"; - fassertFailed(17147); - } + _path = (defaultRoot / dirName).string(); - ::mongo::unittest::log() << "Created temporary directory: " << _path; + bool createdNewDirectory = boost::filesystem::create_directory(_path); + if (!createdNewDirectory) { + error() << "unique path (" << _path << ") already existed"; + fassertFailed(17147); } - TempDir::~TempDir() { - try { - boost::filesystem::remove_all(_path); - } - catch (const std::exception& e) { - warning() << "error encountered recursively deleting directory '" << _path << "': " - << e.what() << ". Ignoring and continuing."; - } + ::mongo::unittest::log() << "Created temporary directory: " << _path; +} + +TempDir::~TempDir() { + try { + boost::filesystem::remove_all(_path); + } catch (const std::exception& e) { + warning() << "error encountered recursively deleting directory '" << _path + << "': " << e.what() << ". Ignoring and continuing."; } -} // namespace unittest -} // namespace mongo +} +} // namespace unittest +} // namespace mongo diff --git a/src/mongo/unittest/temp_dir.h b/src/mongo/unittest/temp_dir.h index 070f0ea7217..a3dd3fcc2d2 100644 --- a/src/mongo/unittest/temp_dir.h +++ b/src/mongo/unittest/temp_dir.h @@ -32,36 +32,39 @@ namespace mongo { namespace unittest { +/** + * An RAII temporary directory that deletes itself and all contents files on scope exit. + */ +class TempDir { + MONGO_DISALLOW_COPYING(TempDir); + +public: + /** + * Creates a new unique temporary directory. + * + * Throws if this fails for any reason, such as bad permissions. + * + * The leaf of the directory path will start with namePrefix and have + * unspecified characters added to ensure uniqueness. + * + * namePrefix must not contain either / or \ + */ + explicit TempDir(const std::string& namePrefix); + /** - * An RAII temporary directory that deletes itself and all contents files on scope exit. + * Delete the directory and all contents. + * + * This only does best-effort. In particular no new files should be created in the directory + * once the TempDir goes out of scope. Any errors are logged and ignored. */ - class TempDir { - MONGO_DISALLOW_COPYING(TempDir); - public: - /** - * Creates a new unique temporary directory. - * - * Throws if this fails for any reason, such as bad permissions. - * - * The leaf of the directory path will start with namePrefix and have - * unspecified characters added to ensure uniqueness. - * - * namePrefix must not contain either / or \ - */ - explicit TempDir(const std::string& namePrefix); + ~TempDir(); - /** - * Delete the directory and all contents. - * - * This only does best-effort. In particular no new files should be created in the directory - * once the TempDir goes out of scope. Any errors are logged and ignored. - */ - ~TempDir(); - - const std::string& path() { return _path; } + const std::string& path() { + return _path; + } - private: - std::string _path; - }; -} // namespace unittest -} // namespace mongo +private: + std::string _path; +}; +} // namespace unittest +} // namespace mongo diff --git a/src/mongo/unittest/unittest-inl.h b/src/mongo/unittest/unittest-inl.h index 0ad891506a8..e0076f41751 100644 --- a/src/mongo/unittest/unittest-inl.h +++ b/src/mongo/unittest/unittest-inl.h @@ -29,13 +29,13 @@ #pragma once namespace mongo { - namespace unittest { +namespace unittest { - template <typename T> - Test::RegistrationAgent<T>::RegistrationAgent(const std::string& suiteName, - const std::string& testName) { - Suite::getSuite(suiteName)->add<T>(testName); - } +template <typename T> +Test::RegistrationAgent<T>::RegistrationAgent(const std::string& suiteName, + const std::string& testName) { + Suite::getSuite(suiteName)->add<T>(testName); +} - } // namespace mongo +} // namespace mongo } // namespace unittest diff --git a/src/mongo/unittest/unittest.cpp b/src/mongo/unittest/unittest.cpp index f484c70f079..11be73a16cb 100644 --- a/src/mongo/unittest/unittest.cpp +++ b/src/mongo/unittest/unittest.cpp @@ -48,386 +48,378 @@ namespace mongo { - using boost::shared_ptr; - using std::string; +using boost::shared_ptr; +using std::string; - namespace unittest { +namespace unittest { - namespace { - logger::MessageLogDomain* unittestOutput = - logger::globalLogManager()->getNamedDomain("unittest"); - - typedef std::map<std::string, boost::shared_ptr<Suite> > SuiteMap; +namespace { +logger::MessageLogDomain* unittestOutput = logger::globalLogManager()->getNamedDomain("unittest"); - inline SuiteMap& _allSuites() { - static SuiteMap allSuites; - return allSuites; - } +typedef std::map<std::string, boost::shared_ptr<Suite>> SuiteMap; - } // namespace +inline SuiteMap& _allSuites() { + static SuiteMap allSuites; + return allSuites; +} - logger::LogstreamBuilder log() { - return LogstreamBuilder(unittestOutput, getThreadName(), logger::LogSeverity::Log()); - } - - MONGO_INITIALIZER_WITH_PREREQUISITES(UnitTestOutput, ("GlobalLogManager", "default"))( - InitializerContext*) { +} // namespace - unittestOutput->attachAppender( - logger::MessageLogDomain::AppenderAutoPtr( - new logger::ConsoleAppender<logger::MessageLogDomain::Event>( - new logger::MessageEventDetailsEncoder))); - return Status::OK(); +logger::LogstreamBuilder log() { + return LogstreamBuilder(unittestOutput, getThreadName(), logger::LogSeverity::Log()); +} + +MONGO_INITIALIZER_WITH_PREREQUISITES(UnitTestOutput, + ("GlobalLogManager", "default"))(InitializerContext*) { + unittestOutput->attachAppender(logger::MessageLogDomain::AppenderAutoPtr( + new logger::ConsoleAppender<logger::MessageLogDomain::Event>( + new logger::MessageEventDetailsEncoder))); + return Status::OK(); +} + +class Result { +public: + Result(const std::string& name) + : _name(name), _rc(0), _tests(0), _fails(), _asserts(0), _millis(0) {} + + std::string toString() { + std::stringstream ss; + + char result[128]; + sprintf(result, + "%-30s | tests: %4d | fails: %4d | assert calls: %10d | time secs: %6.3f\n", + _name.c_str(), + _tests, + static_cast<int>(_fails.size()), + _asserts, + _millis / 1000.0); + ss << result; + + for (std::vector<std::string>::iterator i = _messages.begin(); i != _messages.end(); i++) { + ss << "\t" << *i << '\n'; } - class Result { - public: - Result( const std::string& name ) - : _name( name ) , _rc(0) , _tests(0) , _fails() , _asserts(0), _millis(0) {} - - std::string toString() { - std::stringstream ss; + return ss.str(); + } - char result[128]; - sprintf(result, - "%-30s | tests: %4d | fails: %4d | assert calls: %10d | time secs: %6.3f\n", - _name.c_str(), _tests, static_cast<int>(_fails.size()), _asserts, _millis/1000.0 ); - ss << result; + int rc() { + return _rc; + } - for ( std::vector<std::string>::iterator i=_messages.begin(); i!=_messages.end(); i++ ) { - ss << "\t" << *i << '\n'; - } + string _name; - return ss.str(); - } - - int rc() { - return _rc; - } - - string _name; + int _rc; + int _tests; + std::vector<std::string> _fails; + int _asserts; + int _millis; + std::vector<std::string> _messages; - int _rc; - int _tests; - std::vector<std::string> _fails; - int _asserts; - int _millis; - std::vector<std::string> _messages; + static Result* cur; +}; - static Result * cur; - }; +Result* Result::cur = 0; - Result* Result::cur = 0; +Test::Test() : _isCapturingLogMessages(false) {} - Test::Test() : _isCapturingLogMessages(false) {} - - Test::~Test() { - if (_isCapturingLogMessages) { - stopCapturingLogMessages(); - } - } +Test::~Test() { + if (_isCapturingLogMessages) { + stopCapturingLogMessages(); + } +} - void Test::run() { - setUp(); +void Test::run() { + setUp(); - // An uncaught exception does not prevent the tear down from running. But - // such an event still constitutes an error. To test this behavior we use a - // special exception here that when thrown does trigger the tear down but is - // not considered an error. - try { - _doTest(); - } - catch (FixtureExceptionForTesting&) { - tearDown(); - return; - } - catch (TestAssertionFailureException&) { - tearDown(); - throw; - } + // An uncaught exception does not prevent the tear down from running. But + // such an event still constitutes an error. To test this behavior we use a + // special exception here that when thrown does trigger the tear down but is + // not considered an error. + try { + _doTest(); + } catch (FixtureExceptionForTesting&) { + tearDown(); + return; + } catch (TestAssertionFailureException&) { + tearDown(); + throw; + } - tearDown(); - } + tearDown(); +} - void Test::setUp() {} - void Test::tearDown() {} +void Test::setUp() {} +void Test::tearDown() {} namespace { - class StringVectorAppender : public logger::MessageLogDomain::EventAppender { - public: - explicit StringVectorAppender(std::vector<std::string>* lines) : _lines(lines) {} - virtual ~StringVectorAppender() {} - virtual Status append(const logger::MessageLogDomain::Event& event) { - std::ostringstream _os; - if (!_encoder.encode(event, _os)) { - return Status(ErrorCodes::LogWriteFailed, "Failed to append to LogTestAppender."); - } - _lines->push_back(_os.str()); - return Status::OK(); +class StringVectorAppender : public logger::MessageLogDomain::EventAppender { +public: + explicit StringVectorAppender(std::vector<std::string>* lines) : _lines(lines) {} + virtual ~StringVectorAppender() {} + virtual Status append(const logger::MessageLogDomain::Event& event) { + std::ostringstream _os; + if (!_encoder.encode(event, _os)) { + return Status(ErrorCodes::LogWriteFailed, "Failed to append to LogTestAppender."); } - private: - logger::MessageEventDetailsEncoder _encoder; - std::vector<std::string>* _lines; - }; + _lines->push_back(_os.str()); + return Status::OK(); + } + +private: + logger::MessageEventDetailsEncoder _encoder; + std::vector<std::string>* _lines; +}; } // namespace - void Test::startCapturingLogMessages() { - invariant(!_isCapturingLogMessages); - _capturedLogMessages.clear(); - _captureAppenderHandle = logger::globalLogDomain()->attachAppender( - logger::MessageLogDomain::AppenderAutoPtr(new StringVectorAppender( - &_capturedLogMessages))); - _isCapturingLogMessages = true; - } - - void Test::stopCapturingLogMessages() { - invariant(_isCapturingLogMessages); - logger::globalLogDomain()->detachAppender(_captureAppenderHandle); - _isCapturingLogMessages = false; +void Test::startCapturingLogMessages() { + invariant(!_isCapturingLogMessages); + _capturedLogMessages.clear(); + _captureAppenderHandle = logger::globalLogDomain()->attachAppender( + logger::MessageLogDomain::AppenderAutoPtr(new StringVectorAppender(&_capturedLogMessages))); + _isCapturingLogMessages = true; +} + +void Test::stopCapturingLogMessages() { + invariant(_isCapturingLogMessages); + logger::globalLogDomain()->detachAppender(_captureAppenderHandle); + _isCapturingLogMessages = false; +} + +Suite::Suite(const std::string& name) : _name(name) { + registerSuite(name, this); +} + +Suite::~Suite() {} + +void Suite::add(const std::string& name, const TestFunction& testFn) { + _tests.push_back(boost::shared_ptr<TestHolder>(new TestHolder(name, testFn))); +} + +Result* Suite::run(const std::string& filter, int runsPerTest) { + LOG(1) << "\t about to setupTests" << std::endl; + setupTests(); + LOG(1) << "\t done setupTests" << std::endl; + + Timer timer; + Result* r = new Result(_name); + Result::cur = r; + + for (std::vector<boost::shared_ptr<TestHolder>>::iterator i = _tests.begin(); i != _tests.end(); + i++) { + boost::shared_ptr<TestHolder>& tc = *i; + if (filter.size() && tc->getName().find(filter) == std::string::npos) { + LOG(1) << "\t skipping test: " << tc->getName() << " because doesn't match filter" + << std::endl; + continue; } - Suite::Suite( const std::string& name ) : _name( name ) { - registerSuite( name , this ); - } - - Suite::~Suite() {} + r->_tests++; - void Suite::add(const std::string& name, const TestFunction& testFn) { - _tests.push_back(boost::shared_ptr<TestHolder>(new TestHolder(name, testFn))); - } + bool passes = false; - Result * Suite::run( const std::string& filter, int runsPerTest ) { - - LOG(1) << "\t about to setupTests" << std::endl; - setupTests(); - LOG(1) << "\t done setupTests" << std::endl; - - Timer timer; - Result * r = new Result( _name ); - Result::cur = r; - - for ( std::vector< boost::shared_ptr<TestHolder> >::iterator i=_tests.begin(); - i!=_tests.end(); i++ ) { - boost::shared_ptr<TestHolder>& tc = *i; - if ( filter.size() && tc->getName().find( filter ) == std::string::npos ) { - LOG(1) << "\t skipping test: " << tc->getName() << " because doesn't match filter" << std::endl; - continue; - } - - r->_tests++; - - bool passes = false; - - onCurrentTestNameChange( tc->getName() ); - - log() << "\t going to run test: " << tc->getName() << std::endl; - - std::stringstream err; - err << tc->getName() << "\t"; - - try { - for ( int x=0; x<runsPerTest; x++ ) - tc->run(); - passes = true; - } - catch ( const TestAssertionFailureException& ae ) { - err << ae.toString(); - } - catch ( const std::exception& e ) { - err << " std::exception: " << e.what() << " in test " << tc->getName(); - } - catch ( int x ) { - err << " caught int " << x << " in test " << tc->getName(); - } - - if ( ! passes ) { - std::string s = err.str(); - log() << "FAIL: " << s << std::endl; - r->_fails.push_back(tc->getName()); - r->_messages.push_back( s ); - } - } + onCurrentTestNameChange(tc->getName()); - if ( !r->_fails.empty() ) - r->_rc = 17; + log() << "\t going to run test: " << tc->getName() << std::endl; - r->_millis = timer.millis(); + std::stringstream err; + err << tc->getName() << "\t"; - onCurrentTestNameChange( "" ); - - log() << "\t DONE running tests" << std::endl; - - return r; + try { + for (int x = 0; x < runsPerTest; x++) + tc->run(); + passes = true; + } catch (const TestAssertionFailureException& ae) { + err << ae.toString(); + } catch (const std::exception& e) { + err << " std::exception: " << e.what() << " in test " << tc->getName(); + } catch (int x) { + err << " caught int " << x << " in test " << tc->getName(); } - int Suite::run( const std::vector<std::string>& suites , const std::string& filter , int runsPerTest ) { - - if (_allSuites().empty()) { - log() << "error: no suites registered."; - return EXIT_FAILURE; - } - - for ( unsigned int i = 0; i < suites.size(); i++ ) { - if ( _allSuites().count( suites[i] ) == 0 ) { - log() << "invalid test suite [" << suites[i] << "], use --list to see valid names" << std::endl; - return EXIT_FAILURE; - } - } - - std::vector<std::string> torun(suites); - - if ( torun.empty() ) { - for ( SuiteMap::const_iterator i = _allSuites().begin(); - i !=_allSuites().end(); ++i ) { + if (!passes) { + std::string s = err.str(); + log() << "FAIL: " << s << std::endl; + r->_fails.push_back(tc->getName()); + r->_messages.push_back(s); + } + } - torun.push_back( i->first ); - } - } + if (!r->_fails.empty()) + r->_rc = 17; - std::vector<Result*> results; + r->_millis = timer.millis(); - for ( std::vector<std::string>::iterator i=torun.begin(); i!=torun.end(); i++ ) { - std::string name = *i; - boost::shared_ptr<Suite>& s = _allSuites()[name]; - fassert( 16145, s != NULL ); + onCurrentTestNameChange(""); - log() << "going to run suite: " << name << std::endl; - results.push_back( s->run( filter, runsPerTest ) ); - } + log() << "\t DONE running tests" << std::endl; - log() << "**************************************************" << std::endl; - - int rc = 0; - - int tests = 0; - int asserts = 0; - int millis = 0; - - Result totals ("TOTALS"); - std::vector<std::string> failedSuites; - - Result::cur = NULL; - for ( std::vector<Result*>::iterator i=results.begin(); i!=results.end(); i++ ) { - Result* r = *i; - log() << r->toString(); - if ( abs( r->rc() ) > abs( rc ) ) - rc = r->rc(); - - tests += r->_tests; - if ( !r->_fails.empty() ) { - failedSuites.push_back(r->toString()); - for ( std::vector<std::string>::const_iterator j=r->_fails.begin(); - j!=r->_fails.end(); j++ ) { - const std::string& s = (*j); - totals._fails.push_back(r->_name + "/" + s); - } - } - asserts += r->_asserts; - millis += r->_millis; - - delete r; - } + return r; +} - totals._tests = tests; - totals._asserts = asserts; - totals._millis = millis; - - log() << totals.toString(); // includes endl - - // summary - if ( !totals._fails.empty() ) { - log() << "Failing tests:" << std::endl; - for ( std::vector<std::string>::const_iterator i=totals._fails.begin(); - i!=totals._fails.end(); i++ ) { - const std::string& s = (*i); - log() << "\t " << s << " Failed"; - } - log() << "FAILURE - " << totals._fails.size() << " tests in " - << failedSuites.size() << " suites failed"; - } - else { - log() << "SUCCESS - All tests in all suites passed"; - } +int Suite::run(const std::vector<std::string>& suites, const std::string& filter, int runsPerTest) { + if (_allSuites().empty()) { + log() << "error: no suites registered."; + return EXIT_FAILURE; + } - return rc; + for (unsigned int i = 0; i < suites.size(); i++) { + if (_allSuites().count(suites[i]) == 0) { + log() << "invalid test suite [" << suites[i] << "], use --list to see valid names" + << std::endl; + return EXIT_FAILURE; } + } - void Suite::registerSuite( const std::string& name , Suite* s ) { - boost::shared_ptr<Suite>& m = _allSuites()[name]; - fassert( 10162, !m ); - m.reset(s); - } + std::vector<std::string> torun(suites); - Suite* Suite::getSuite(const std::string& name) { - boost::shared_ptr<Suite>& result = _allSuites()[name]; - if (!result) { - // Suites are self-registering. - new Suite(name); - } - invariant(result); - return result.get(); + if (torun.empty()) { + for (SuiteMap::const_iterator i = _allSuites().begin(); i != _allSuites().end(); ++i) { + torun.push_back(i->first); } + } - void Suite::setupTests() {} + std::vector<Result*> results; - TestAssertionFailureException::TestAssertionFailureException( - const std::string& theFile, - unsigned theLine, - const std::string& theFailingExpression ) - : _file(theFile), _line(theLine), _message(theFailingExpression) {} + for (std::vector<std::string>::iterator i = torun.begin(); i != torun.end(); i++) { + std::string name = *i; + boost::shared_ptr<Suite>& s = _allSuites()[name]; + fassert(16145, s != NULL); - std::string TestAssertionFailureException::toString() const { - std::ostringstream os; - os << getMessage() << " @" << getFile() << ":" << getLine(); - return os.str(); - } + log() << "going to run suite: " << name << std::endl; + results.push_back(s->run(filter, runsPerTest)); + } - TestAssertionFailure::TestAssertionFailure(const std::string& file, - unsigned line, - const std::string& message) - : _exception(file, line, message), _enabled(false) {} + log() << "**************************************************" << std::endl; - TestAssertionFailure::TestAssertionFailure(const TestAssertionFailure& other) : - _exception(other._exception), _enabled(false) { + int rc = 0; - invariant(!other._enabled); - } + int tests = 0; + int asserts = 0; + int millis = 0; - TestAssertionFailure& TestAssertionFailure::operator=(const TestAssertionFailure& other) { - invariant(!_enabled); - invariant(!other._enabled); - _exception = other._exception; - return *this; - } + Result totals("TOTALS"); + std::vector<std::string> failedSuites; - TestAssertionFailure::~TestAssertionFailure() -#if __cplusplus >= 201103 - noexcept(false) -#endif - { - if (!_enabled) { - invariant(_stream.str().empty()); - return; - } - if (!_stream.str().empty()) { - _exception.setMessage(_exception.getMessage() + " " + _stream.str()); - } - throw _exception; - } + Result::cur = NULL; + for (std::vector<Result*>::iterator i = results.begin(); i != results.end(); i++) { + Result* r = *i; + log() << r->toString(); + if (abs(r->rc()) > abs(rc)) + rc = r->rc(); - std::ostream& TestAssertionFailure::stream() { - invariant(!_enabled); - _enabled = true; - return _stream; - } - - std::vector<std::string> getAllSuiteNames() { - std::vector<std::string> result; - for (SuiteMap::const_iterator i = _allSuites().begin(); i != _allSuites().end(); ++i) { - result.push_back(i->first); + tests += r->_tests; + if (!r->_fails.empty()) { + failedSuites.push_back(r->toString()); + for (std::vector<std::string>::const_iterator j = r->_fails.begin(); + j != r->_fails.end(); + j++) { + const std::string& s = (*j); + totals._fails.push_back(r->_name + "/" + s); } - return result; } - - } // namespace unittest + asserts += r->_asserts; + millis += r->_millis; + + delete r; + } + + totals._tests = tests; + totals._asserts = asserts; + totals._millis = millis; + + log() << totals.toString(); // includes endl + + // summary + if (!totals._fails.empty()) { + log() << "Failing tests:" << std::endl; + for (std::vector<std::string>::const_iterator i = totals._fails.begin(); + i != totals._fails.end(); + i++) { + const std::string& s = (*i); + log() << "\t " << s << " Failed"; + } + log() << "FAILURE - " << totals._fails.size() << " tests in " << failedSuites.size() + << " suites failed"; + } else { + log() << "SUCCESS - All tests in all suites passed"; + } + + return rc; +} + +void Suite::registerSuite(const std::string& name, Suite* s) { + boost::shared_ptr<Suite>& m = _allSuites()[name]; + fassert(10162, !m); + m.reset(s); +} + +Suite* Suite::getSuite(const std::string& name) { + boost::shared_ptr<Suite>& result = _allSuites()[name]; + if (!result) { + // Suites are self-registering. + new Suite(name); + } + invariant(result); + return result.get(); +} + +void Suite::setupTests() {} + +TestAssertionFailureException::TestAssertionFailureException( + const std::string& theFile, unsigned theLine, const std::string& theFailingExpression) + : _file(theFile), _line(theLine), _message(theFailingExpression) {} + +std::string TestAssertionFailureException::toString() const { + std::ostringstream os; + os << getMessage() << " @" << getFile() << ":" << getLine(); + return os.str(); +} + +TestAssertionFailure::TestAssertionFailure(const std::string& file, + unsigned line, + const std::string& message) + : _exception(file, line, message), _enabled(false) {} + +TestAssertionFailure::TestAssertionFailure(const TestAssertionFailure& other) + : _exception(other._exception), _enabled(false) { + invariant(!other._enabled); +} + +TestAssertionFailure& TestAssertionFailure::operator=(const TestAssertionFailure& other) { + invariant(!_enabled); + invariant(!other._enabled); + _exception = other._exception; + return *this; +} + +TestAssertionFailure::~TestAssertionFailure() +#if __cplusplus >= 201103 + noexcept(false) +#endif +{ + if (!_enabled) { + invariant(_stream.str().empty()); + return; + } + if (!_stream.str().empty()) { + _exception.setMessage(_exception.getMessage() + " " + _stream.str()); + } + throw _exception; +} + +std::ostream& TestAssertionFailure::stream() { + invariant(!_enabled); + _enabled = true; + return _stream; +} + +std::vector<std::string> getAllSuiteNames() { + std::vector<std::string> result; + for (SuiteMap::const_iterator i = _allSuites().begin(); i != _allSuites().end(); ++i) { + result.push_back(i->first); + } + return result; +} + +} // namespace unittest } // namespace mongo diff --git a/src/mongo/unittest/unittest.h b/src/mongo/unittest/unittest.h index e71e3f95fb4..75c192fad82 100644 --- a/src/mongo/unittest/unittest.h +++ b/src/mongo/unittest/unittest.h @@ -59,7 +59,9 @@ /** * Fails unless "EXPRESSION" is true. */ -#define ASSERT_TRUE(EXPRESSION) if (!(EXPRESSION)) FAIL("Expected: " #EXPRESSION) +#define ASSERT_TRUE(EXPRESSION) \ + if (!(EXPRESSION)) \ + FAIL("Expected: " #EXPRESSION) #define ASSERT(EXPRESSION) ASSERT_TRUE(EXPRESSION) /** @@ -80,8 +82,8 @@ /* * Binary comparison assertions. */ -#define ASSERT_EQUALS(a,b) ASSERT_EQ(a, b) -#define ASSERT_NOT_EQUALS(a,b) ASSERT_NE(a, b) +#define ASSERT_EQUALS(a, b) ASSERT_EQ(a, b) +#define ASSERT_NOT_EQUALS(a, b) ASSERT_NE(a, b) #define ASSERT_LESS_THAN(a, b) ASSERT_LT(a, b) #define ASSERT_NOT_LESS_THAN(a, b) ASSERT_GTE(a, b) #define ASSERT_GREATER_THAN(a, b) ASSERT_GT(a, b) @@ -89,8 +91,8 @@ #define ASSERT_LESS_THAN_OR_EQUALS(a, b) ASSERT_LTE(a, b) #define ASSERT_GREATER_THAN_OR_EQUALS(a, b) ASSERT_GTE(a, b) -#define ASSERT_EQ(a,b) _ASSERT_COMPARISON(EQ, a, b) -#define ASSERT_NE(a,b) _ASSERT_COMPARISON(NE, a, b) +#define ASSERT_EQ(a, b) _ASSERT_COMPARISON(EQ, a, b) +#define ASSERT_NE(a, b) _ASSERT_COMPARISON(NE, a, b) #define ASSERT_LT(a, b) _ASSERT_COMPARISON(LT, a, b) #define ASSERT_LTE(a, b) _ASSERT_COMPARISON(LTE, a, b) #define ASSERT_GT(a, b) _ASSERT_COMPARISON(GT, a, b) @@ -99,16 +101,16 @@ /** * Binary comparison utility macro. Do not use directly. */ -#define _ASSERT_COMPARISON(COMPARISON, a, b) \ - if (::mongo::unittest::ComparisonAssertion_##COMPARISON ca = ::mongo::unittest::ComparisonAssertion_##COMPARISON(__FILE__, __LINE__, #a, #b, a, b)) \ - ca.failure().stream() +#define _ASSERT_COMPARISON(COMPARISON, a, b) \ + if (::mongo::unittest::ComparisonAssertion_##COMPARISON ca = \ + ::mongo::unittest::ComparisonAssertion_##COMPARISON(__FILE__, __LINE__, #a, #b, a, b)) \ + ca.failure().stream() /** * Approximate equality assertion. Useful for comparisons on limited precision floating point * values. */ -#define ASSERT_APPROX_EQUAL(a, b, ABSOLUTE_ERR) \ - ASSERT_LTE(std::abs((a) - (b)), ABSOLUTE_ERR) +#define ASSERT_APPROX_EQUAL(a, b, ABSOLUTE_ERR) ASSERT_LTE(std::abs((a) - (b)), ABSOLUTE_ERR) /** * Verify that the evaluation of "EXPRESSION" throws an exception of type EXCEPTION_TYPE. @@ -117,19 +119,19 @@ * of a subtype of "EXCEPTION_TYPE", the test is considered a failure and further evaluation * halts. */ -#define ASSERT_THROWS(STATEMENT, EXCEPTION_TYPE) \ - ASSERT_THROWS_PRED(STATEMENT, \ - EXCEPTION_TYPE, \ - ::mongo::stdx::bind(::mongo::unittest::alwaysTrue)) +#define ASSERT_THROWS(STATEMENT, EXCEPTION_TYPE) \ + ASSERT_THROWS_PRED( \ + STATEMENT, EXCEPTION_TYPE, ::mongo::stdx::bind(::mongo::unittest::alwaysTrue)) /** * Behaves like ASSERT_THROWS, above, but also fails if calling what() on the thrown exception * does not return a string equal to EXPECTED_WHAT. */ -#define ASSERT_THROWS_WHAT(STATEMENT, EXCEPTION_TYPE, EXPECTED_WHAT) \ - ASSERT_THROWS_PRED(STATEMENT, \ - EXCEPTION_TYPE, \ - ::mongo::stdx::bind(std::equal_to<std::string>(), (EXPECTED_WHAT), \ +#define ASSERT_THROWS_WHAT(STATEMENT, EXCEPTION_TYPE, EXPECTED_WHAT) \ + ASSERT_THROWS_PRED(STATEMENT, \ + EXCEPTION_TYPE, \ + ::mongo::stdx::bind(std::equal_to<std::string>(), \ + (EXPECTED_WHAT), \ ::mongo::stdx::bind(&EXCEPTION_TYPE::what, \ ::mongo::stdx::placeholders::_1))) @@ -137,33 +139,29 @@ * Behaves like ASSERT_THROWS, above, but also fails if PREDICATE(ex) for the throw exception, ex, * is false. */ -#define ASSERT_THROWS_PRED(STATEMENT, EXCEPTION_TYPE, PREDICATE) do { \ - try { \ - STATEMENT; \ - FAIL("Expected statement " #STATEMENT \ - " to throw " #EXCEPTION_TYPE \ - " but it threw nothing."); \ - } catch (const EXCEPTION_TYPE& ex) { \ - if (!(PREDICATE(ex))) { \ - FAIL("Expected " #STATEMENT \ - " to throw an exception of type " \ - #EXCEPTION_TYPE \ - " where " #PREDICATE \ - "(ex) was true, but it was false."); \ - } \ - } \ +#define ASSERT_THROWS_PRED(STATEMENT, EXCEPTION_TYPE, PREDICATE) \ + do { \ + try { \ + STATEMENT; \ + FAIL("Expected statement " #STATEMENT " to throw " #EXCEPTION_TYPE \ + " but it threw nothing."); \ + } catch (const EXCEPTION_TYPE& ex) { \ + if (!(PREDICATE(ex))) { \ + FAIL("Expected " #STATEMENT " to throw an exception of type " #EXCEPTION_TYPE \ + " where " #PREDICATE "(ex) was true, but it was false."); \ + } \ + } \ } while (false) -#define ASSERT_STRING_CONTAINS(BIG_STRING, CONTAINS ) do { \ - std::string myString( BIG_STRING ); \ - if ( myString.find(CONTAINS) == std::string::npos ) { \ - std::string err( "Expected " #BIG_STRING " (" ); \ - err += myString; \ - err += std::string(") to contain " #CONTAINS ); \ - ::mongo::unittest::TestAssertionFailure(__FILE__, \ - __LINE__, \ - err).stream(); \ - } \ +#define ASSERT_STRING_CONTAINS(BIG_STRING, CONTAINS) \ + do { \ + std::string myString(BIG_STRING); \ + if (myString.find(CONTAINS) == std::string::npos) { \ + std::string err("Expected " #BIG_STRING " ("); \ + err += myString; \ + err += std::string(") to contain " #CONTAINS); \ + ::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, err).stream(); \ + } \ } while (false) /** @@ -175,15 +173,15 @@ * ASSERT_EQUALS(error_success, foo(invalidValue)); * } */ -#define TEST(CASE_NAME, TEST_NAME) \ - class _TEST_TYPE_NAME(CASE_NAME, TEST_NAME) : public ::mongo::unittest::Test { \ - private: \ - virtual void _doTest(); \ - \ - static const RegistrationAgent<_TEST_TYPE_NAME(CASE_NAME, TEST_NAME) > _agent; \ - }; \ - const ::mongo::unittest::Test::RegistrationAgent<_TEST_TYPE_NAME(CASE_NAME, TEST_NAME) > \ - _TEST_TYPE_NAME(CASE_NAME, TEST_NAME)::_agent(#CASE_NAME, #TEST_NAME); \ +#define TEST(CASE_NAME, TEST_NAME) \ + class _TEST_TYPE_NAME(CASE_NAME, TEST_NAME) : public ::mongo::unittest::Test { \ + private: \ + virtual void _doTest(); \ + \ + static const RegistrationAgent<_TEST_TYPE_NAME(CASE_NAME, TEST_NAME)> _agent; \ + }; \ + const ::mongo::unittest::Test::RegistrationAgent<_TEST_TYPE_NAME(CASE_NAME, TEST_NAME)> \ + _TEST_TYPE_NAME(CASE_NAME, TEST_NAME)::_agent(#CASE_NAME, #TEST_NAME); \ void _TEST_TYPE_NAME(CASE_NAME, TEST_NAME)::_doTest() /** @@ -202,324 +200,338 @@ * ASSERT_EQUALS(10, myVar); * } */ -#define TEST_F(FIXTURE_NAME, TEST_NAME) \ - class _TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME) : public FIXTURE_NAME { \ - private: \ - virtual void _doTest(); \ - \ - static const RegistrationAgent<_TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME) > _agent; \ - }; \ - const ::mongo::unittest::Test::RegistrationAgent<_TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME) > \ - _TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)::_agent(#FIXTURE_NAME, #TEST_NAME); \ +#define TEST_F(FIXTURE_NAME, TEST_NAME) \ + class _TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME) : public FIXTURE_NAME { \ + private: \ + virtual void _doTest(); \ + \ + static const RegistrationAgent<_TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)> _agent; \ + }; \ + const ::mongo::unittest::Test::RegistrationAgent<_TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)> \ + _TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)::_agent(#FIXTURE_NAME, #TEST_NAME); \ void _TEST_TYPE_NAME(FIXTURE_NAME, TEST_NAME)::_doTest() /** * Macro to construct a type name for a test, from its "CASE_NAME" and "TEST_NAME". * Do not use directly in test code. */ -#define _TEST_TYPE_NAME(CASE_NAME, TEST_NAME) \ - UnitTest__##CASE_NAME##__##TEST_NAME +#define _TEST_TYPE_NAME(CASE_NAME, TEST_NAME) UnitTest__##CASE_NAME##__##TEST_NAME namespace mongo { - namespace unittest { - - class Result; - - /** - * Gets a LogstreamBuilder for logging to the unittest log domain, which may have - * different target from the global log domain. - */ - mongo::logger::LogstreamBuilder log(); - - /** - * Type representing the function composing a test. - */ - typedef stdx::function<void (void)> TestFunction; - - /** - * Container holding a test function and its name. Suites - * contain lists of these. - */ - class TestHolder : private boost::noncopyable { - public: - TestHolder(const std::string& name, const TestFunction& fn) - : _name(name), _fn(fn) {} - - ~TestHolder() {} - void run() const { _fn(); } - std::string getName() const { return _name; } - - private: - std::string _name; - TestFunction _fn; - }; - - /** - * Base type for unit test fixtures. Also, the default fixture type used - * by the TEST() macro. - */ - class Test : private boost::noncopyable { - public: - Test(); - virtual ~Test(); - - void run(); - - protected: - /** - * Registration agent for adding tests to suites, used by TEST macro. - */ - template <typename T> - class RegistrationAgent : private boost::noncopyable { - public: - RegistrationAgent(const std::string& suiteName, const std::string& testName); - }; - - /** - * This exception class is used to exercise the testing framework itself. If a test - * case throws it, the framework would not consider it an error. - */ - class FixtureExceptionForTesting : public std::exception { - }; - - /** - * Starts capturing messages logged by code under test. - * - * Log messages will still also go to their default destination; this - * code simply adds an additional sink for log messages. - * - * Clears any previously captured log lines. - */ - void startCapturingLogMessages(); - - /** - * Stops capturing log messages logged by code under test. - */ - void stopCapturingLogMessages(); - - /** - * Gets a vector of strings, one log line per string, captured since - * the last call to startCapturingLogMessages() in this test. - */ - const std::vector<std::string>& getCapturedLogMessages() const { - return _capturedLogMessages; - } - - private: - /** - * Called on the test object before running the test. - */ - virtual void setUp(); - - /** - * Called on the test object after running the test. - */ - virtual void tearDown(); - - /** - * The test itself. - */ - virtual void _doTest() = 0; - - bool _isCapturingLogMessages; - std::vector<std::string> _capturedLogMessages; - logger::MessageLogDomain::AppenderHandle _captureAppenderHandle; - }; - - /** - * Representation of a collection of tests. - * - * One suite is constructed for each "CASE_NAME" when using the TEST macro. - * Additionally, tests that are part of dbtests are manually assigned to suites - * by the programmer by overriding setupTests() in a subclass of Suite. This - * approach is deprecated. - */ - class Suite : private boost::noncopyable { - public: - Suite( const std::string& name ); - virtual ~Suite(); - - template<class T> - void add() { add<T>(demangleName(typeid(T))); } - - template<class T , typename A > - void add( const A& a ) { - add(demangleName(typeid(T)), stdx::bind(&Suite::runTestObjectWithArg<T, A>, a)); - } - - template<class T> - void add(const std::string& name) { - add(name, &Suite::runTestObject<T>); - } - - void add(const std::string& name, const TestFunction& testFn); - - Result * run( const std::string& filter , int runsPerTest ); - - static int run( const std::vector<std::string>& suites , const std::string& filter , int runsPerTest ); - - /** - * Get a suite with the given name, creating it if necessary. - * - * The implementation of this function must be safe to call during the global static - * initialization block before main() executes. - */ - static Suite *getSuite(const std::string& name); - - protected: - virtual void setupTests(); - - private: - // TODO(C++11): Make this hold unique_ptrs. - typedef std::vector< boost::shared_ptr<TestHolder> > TestHolderList; - - template <typename T> - static void runTestObject() { - T testObj; - testObj.run(); - } - - template <typename T, typename A> - static void runTestObjectWithArg(const A& a) { - T testObj(a); - testObj.run(); - } - - std::string _name; - TestHolderList _tests; - bool _ran; - - void registerSuite( const std::string& name , Suite* s ); - }; - - // A type that makes it easy to declare a self registering suite for old style test - // declarations. Suites are self registering so this is *not* a memory leak. - template<typename T> - struct SuiteInstance { - SuiteInstance() { - new T; - } - - template<typename U> - SuiteInstance(const U& u) { - new T(u); - } - }; - - /** - * Exception thrown when a test assertion fails. - * - * Typically thrown by helpers in the TestAssertion class and its ilk, below. - * - * NOTE(schwerin): This intentionally does _not_ extend std::exception, so that code under - * test that (foolishly?) catches std::exception won't swallow test failures. Doesn't - * protect you from code that foolishly catches ..., but you do what you can. - */ - class TestAssertionFailureException { - public: - TestAssertionFailureException(const std::string& theFile, - unsigned theLine, - const std::string& theMessage); - - const std::string& getFile() const { return _file; } - unsigned getLine() const { return _line; } - const std::string& getMessage() const { return _message; } - void setMessage(const std::string& message) { _message = message; } - - std::string toString() const; - - private: - std::string _file; - unsigned _line; - std::string _message; - }; - - class TestAssertionFailure { - public: - TestAssertionFailure( - const std::string& file, unsigned line, const std::string& message); - TestAssertionFailure(const TestAssertionFailure& other); +namespace unittest { + +class Result; + +/** + * Gets a LogstreamBuilder for logging to the unittest log domain, which may have + * different target from the global log domain. + */ +mongo::logger::LogstreamBuilder log(); + +/** + * Type representing the function composing a test. + */ +typedef stdx::function<void(void)> TestFunction; + +/** + * Container holding a test function and its name. Suites + * contain lists of these. + */ +class TestHolder : private boost::noncopyable { +public: + TestHolder(const std::string& name, const TestFunction& fn) : _name(name), _fn(fn) {} + + ~TestHolder() {} + void run() const { + _fn(); + } + std::string getName() const { + return _name; + } + +private: + std::string _name; + TestFunction _fn; +}; + +/** + * Base type for unit test fixtures. Also, the default fixture type used + * by the TEST() macro. + */ +class Test : private boost::noncopyable { +public: + Test(); + virtual ~Test(); + + void run(); + +protected: + /** + * Registration agent for adding tests to suites, used by TEST macro. + */ + template <typename T> + class RegistrationAgent : private boost::noncopyable { + public: + RegistrationAgent(const std::string& suiteName, const std::string& testName); + }; + + /** + * This exception class is used to exercise the testing framework itself. If a test + * case throws it, the framework would not consider it an error. + */ + class FixtureExceptionForTesting : public std::exception {}; + + /** + * Starts capturing messages logged by code under test. + * + * Log messages will still also go to their default destination; this + * code simply adds an additional sink for log messages. + * + * Clears any previously captured log lines. + */ + void startCapturingLogMessages(); + + /** + * Stops capturing log messages logged by code under test. + */ + void stopCapturingLogMessages(); + + /** + * Gets a vector of strings, one log line per string, captured since + * the last call to startCapturingLogMessages() in this test. + */ + const std::vector<std::string>& getCapturedLogMessages() const { + return _capturedLogMessages; + } + +private: + /** + * Called on the test object before running the test. + */ + virtual void setUp(); + + /** + * Called on the test object after running the test. + */ + virtual void tearDown(); + + /** + * The test itself. + */ + virtual void _doTest() = 0; + + bool _isCapturingLogMessages; + std::vector<std::string> _capturedLogMessages; + logger::MessageLogDomain::AppenderHandle _captureAppenderHandle; +}; + +/** + * Representation of a collection of tests. + * + * One suite is constructed for each "CASE_NAME" when using the TEST macro. + * Additionally, tests that are part of dbtests are manually assigned to suites + * by the programmer by overriding setupTests() in a subclass of Suite. This + * approach is deprecated. + */ +class Suite : private boost::noncopyable { +public: + Suite(const std::string& name); + virtual ~Suite(); + + template <class T> + void add() { + add<T>(demangleName(typeid(T))); + } + + template <class T, typename A> + void add(const A& a) { + add(demangleName(typeid(T)), stdx::bind(&Suite::runTestObjectWithArg<T, A>, a)); + } + + template <class T> + void add(const std::string& name) { + add(name, &Suite::runTestObject<T>); + } + + void add(const std::string& name, const TestFunction& testFn); + + Result* run(const std::string& filter, int runsPerTest); + + static int run(const std::vector<std::string>& suites, + const std::string& filter, + int runsPerTest); + + /** + * Get a suite with the given name, creating it if necessary. + * + * The implementation of this function must be safe to call during the global static + * initialization block before main() executes. + */ + static Suite* getSuite(const std::string& name); + +protected: + virtual void setupTests(); + +private: + // TODO(C++11): Make this hold unique_ptrs. + typedef std::vector<boost::shared_ptr<TestHolder>> TestHolderList; + + template <typename T> + static void runTestObject() { + T testObj; + testObj.run(); + } + + template <typename T, typename A> + static void runTestObjectWithArg(const A& a) { + T testObj(a); + testObj.run(); + } + + std::string _name; + TestHolderList _tests; + bool _ran; + + void registerSuite(const std::string& name, Suite* s); +}; + +// A type that makes it easy to declare a self registering suite for old style test +// declarations. Suites are self registering so this is *not* a memory leak. +template <typename T> +struct SuiteInstance { + SuiteInstance() { + new T; + } + + template <typename U> + SuiteInstance(const U& u) { + new T(u); + } +}; + +/** + * Exception thrown when a test assertion fails. + * + * Typically thrown by helpers in the TestAssertion class and its ilk, below. + * + * NOTE(schwerin): This intentionally does _not_ extend std::exception, so that code under + * test that (foolishly?) catches std::exception won't swallow test failures. Doesn't + * protect you from code that foolishly catches ..., but you do what you can. + */ +class TestAssertionFailureException { +public: + TestAssertionFailureException(const std::string& theFile, + unsigned theLine, + const std::string& theMessage); + + const std::string& getFile() const { + return _file; + } + unsigned getLine() const { + return _line; + } + const std::string& getMessage() const { + return _message; + } + void setMessage(const std::string& message) { + _message = message; + } + + std::string toString() const; + +private: + std::string _file; + unsigned _line; + std::string _message; +}; + +class TestAssertionFailure { +public: + TestAssertionFailure(const std::string& file, unsigned line, const std::string& message); + TestAssertionFailure(const TestAssertionFailure& other); #if __cplusplus < 201103 - ~TestAssertionFailure(); + ~TestAssertionFailure(); #else - ~TestAssertionFailure() noexcept(false); + ~TestAssertionFailure() noexcept(false); #endif - TestAssertionFailure& operator=(const TestAssertionFailure& other); - - std::ostream& stream(); - private: - TestAssertionFailureException _exception; - std::ostringstream _stream; - bool _enabled; - }; - -#define DECLARE_COMPARISON_ASSERTION(NAME, OPERATOR) \ - class ComparisonAssertion_##NAME { \ - typedef void (ComparisonAssertion_##NAME::*bool_type)() const; \ - public: \ - template <typename A, typename B> \ - ComparisonAssertion_##NAME( \ - const std::string& theFile, \ - unsigned theLine, \ - const StringData& aExpression, \ - const StringData& bExpression, \ - const A& a, \ - const B& b) { \ - if (a OPERATOR b) { \ - return; \ - } \ - std::ostringstream os; \ - os << "Expected " << \ - aExpression << " " #OPERATOR " " << bExpression << \ - " (" << a << " " #OPERATOR " " << b << ")"; \ - _assertion.reset(new TestAssertionFailure( \ - theFile, \ - theLine, \ - os.str())); \ - } \ - operator bool_type() const { \ - return _assertion.get() ? &ComparisonAssertion_##NAME::comparison_failed : NULL; \ - } \ - TestAssertionFailure failure() { return *_assertion; } \ - private: \ - void comparison_failed() const {} \ - boost::shared_ptr<TestAssertionFailure> _assertion; \ + TestAssertionFailure& operator=(const TestAssertionFailure& other); + + std::ostream& stream(); + +private: + TestAssertionFailureException _exception; + std::ostringstream _stream; + bool _enabled; +}; + +#define DECLARE_COMPARISON_ASSERTION(NAME, OPERATOR) \ + class ComparisonAssertion_##NAME { \ + typedef void (ComparisonAssertion_##NAME::*bool_type)() const; \ + \ + public: \ + template <typename A, typename B> \ + ComparisonAssertion_##NAME(const std::string& theFile, \ + unsigned theLine, \ + const StringData& aExpression, \ + const StringData& bExpression, \ + const A& a, \ + const B& b) { \ + if (a OPERATOR b) { \ + return; \ + } \ + std::ostringstream os; \ + os << "Expected " << aExpression << " " #OPERATOR " " << bExpression << " (" << a \ + << " " #OPERATOR " " << b << ")"; \ + _assertion.reset(new TestAssertionFailure(theFile, theLine, os.str())); \ + } \ + operator bool_type() const { \ + return _assertion.get() ? &ComparisonAssertion_##NAME::comparison_failed : NULL; \ + } \ + TestAssertionFailure failure() { \ + return *_assertion; \ + } \ + \ + private: \ + void comparison_failed() const {} \ + boost::shared_ptr<TestAssertionFailure> _assertion; \ } -DECLARE_COMPARISON_ASSERTION(EQ, ==); -DECLARE_COMPARISON_ASSERTION(NE, !=); -DECLARE_COMPARISON_ASSERTION(LT, <); -DECLARE_COMPARISON_ASSERTION(LTE, <=); -DECLARE_COMPARISON_ASSERTION(GT, >); -DECLARE_COMPARISON_ASSERTION(GTE, >=); +DECLARE_COMPARISON_ASSERTION(EQ, == ); +DECLARE_COMPARISON_ASSERTION(NE, != ); +DECLARE_COMPARISON_ASSERTION(LT, < ); +DECLARE_COMPARISON_ASSERTION(LTE, <= ); +DECLARE_COMPARISON_ASSERTION(GT, > ); +DECLARE_COMPARISON_ASSERTION(GTE, >= ); #undef DECLARE_COMPARISON_ASSERTION - /** - * Get the value out of a StatusWith<T>, or throw an exception if it is not OK. - */ - template <typename T> - const T& assertGet(const StatusWith<T>& swt) { - ASSERT_OK(swt.getStatus()); - return swt.getValue(); - } +/** + * Get the value out of a StatusWith<T>, or throw an exception if it is not OK. + */ +template <typename T> +const T& assertGet(const StatusWith<T>& swt) { + ASSERT_OK(swt.getStatus()); + return swt.getValue(); +} - /** - * Hack to support the runaway test observer in dbtests. This is a hook that - * unit test running harnesses (unittest_main and dbtests) must implement. - */ - void onCurrentTestNameChange( const std::string& testName ); +/** + * Hack to support the runaway test observer in dbtests. This is a hook that + * unit test running harnesses (unittest_main and dbtests) must implement. + */ +void onCurrentTestNameChange(const std::string& testName); - /** - * Return a list of suite names. - */ - std::vector<std::string> getAllSuiteNames(); +/** + * Return a list of suite names. + */ +std::vector<std::string> getAllSuiteNames(); - inline bool alwaysTrue() { return true; } +inline bool alwaysTrue() { + return true; +} - } // namespace unittest +} // namespace unittest } // namespace mongo #include "mongo/unittest/unittest-inl.h" diff --git a/src/mongo/unittest/unittest_helpers.cpp b/src/mongo/unittest/unittest_helpers.cpp index 9931a6a8d23..7d20f432b0c 100644 --- a/src/mongo/unittest/unittest_helpers.cpp +++ b/src/mongo/unittest/unittest_helpers.cpp @@ -37,13 +37,13 @@ namespace mongo { - std::ostream& operator<<(std::ostream &s, const OpTime &ot) { - s << ot.toString(); - return s; - } +std::ostream& operator<<(std::ostream& s, const OpTime& ot) { + s << ot.toString(); + return s; +} - std::ostream& operator<<(std::ostream &s, const Date_t &t) { - s << t.toString(); - return s; - } +std::ostream& operator<<(std::ostream& s, const Date_t& t) { + s << t.toString(); + return s; +} } diff --git a/src/mongo/unittest/unittest_helpers.h b/src/mongo/unittest/unittest_helpers.h index 0bcf4adb84f..9457ab1610f 100644 --- a/src/mongo/unittest/unittest_helpers.h +++ b/src/mongo/unittest/unittest_helpers.h @@ -31,13 +31,13 @@ #include <ostream> namespace mongo { - class OpTime; - struct Date_t; +class OpTime; +struct Date_t; - // So that you can ASSERT_EQUALS two OpTimes - std::ostream& operator<<(std::ostream& s, const OpTime& ot); +// So that you can ASSERT_EQUALS two OpTimes +std::ostream& operator<<(std::ostream& s, const OpTime& ot); - // So that you can ASSERT_EQUALS two Date_ts - std::ostream& operator<<(std::ostream& s, const Date_t& t); +// So that you can ASSERT_EQUALS two Date_ts +std::ostream& operator<<(std::ostream& s, const Date_t& t); -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/unittest/unittest_main.cpp b/src/mongo/unittest/unittest_main.cpp index da24270bf94..56b371d3289 100644 --- a/src/mongo/unittest/unittest_main.cpp +++ b/src/mongo/unittest/unittest_main.cpp @@ -34,10 +34,10 @@ #include "mongo/unittest/unittest.h" #include "mongo/util/signal_handlers_synchronous.h" -int main( int argc, char **argv, char **envp ) { +int main(int argc, char** argv, char** envp) { ::mongo::setupSynchronousSignalHandlers(); ::mongo::runGlobalInitializersOrDie(argc, argv, envp); return ::mongo::unittest::Suite::run(std::vector<std::string>(), "", 1); } -void mongo::unittest::onCurrentTestNameChange( const std::string &testName ) {} +void mongo::unittest::onCurrentTestNameChange(const std::string& testName) {} diff --git a/src/mongo/unittest/unittest_test.cpp b/src/mongo/unittest/unittest_test.cpp index 1b10883200f..80d0cad1813 100644 --- a/src/mongo/unittest/unittest_test.cpp +++ b/src/mongo/unittest/unittest_test.cpp @@ -40,95 +40,98 @@ #include "mongo/stdx/functional.h" namespace { - namespace stdx = mongo::stdx; +namespace stdx = mongo::stdx; - bool containsPattern(const std::string& pattern, const std::string& value) { - return value.find(pattern) != std::string::npos; - } +bool containsPattern(const std::string& pattern, const std::string& value) { + return value.find(pattern) != std::string::npos; +} -#define ASSERT_TEST_FAILS(TEST_STMT) \ +#define ASSERT_TEST_FAILS(TEST_STMT) \ ASSERT_THROWS(TEST_STMT, mongo::unittest::TestAssertionFailureException) -#define ASSERT_TEST_FAILS_MATCH(TEST_STMT, PATTERN) \ - ASSERT_THROWS_PRED( \ - TEST_STMT, \ - mongo::unittest::TestAssertionFailureException, \ - stdx::bind(containsPattern, \ - PATTERN, \ - stdx::bind(&mongo::unittest::TestAssertionFailureException::getMessage, \ - stdx::placeholders::_1))) - - TEST(UnitTestSelfTest, DoNothing) { - } - - void throwSomething() { throw std::exception(); } - - TEST(UnitTestSelfTest, TestAssertThrowsSuccess) { - ASSERT_THROWS(throwSomething(), ::std::exception); +#define ASSERT_TEST_FAILS_MATCH(TEST_STMT, PATTERN) \ + ASSERT_THROWS_PRED( \ + TEST_STMT, \ + mongo::unittest::TestAssertionFailureException, \ + stdx::bind(containsPattern, \ + PATTERN, \ + stdx::bind(&mongo::unittest::TestAssertionFailureException::getMessage, \ + stdx::placeholders::_1))) + +TEST(UnitTestSelfTest, DoNothing) {} + +void throwSomething() { + throw std::exception(); +} + +TEST(UnitTestSelfTest, TestAssertThrowsSuccess) { + ASSERT_THROWS(throwSomething(), ::std::exception); +} + +class MyException { +public: + std::string what() const { + return "whatever"; } - - class MyException { - public: - std::string what() const { return "whatever"; } - }; - - TEST(UnitTestSelfTest, TestAssertThrowsWhatSuccess) { - ASSERT_THROWS_WHAT(throw MyException(), MyException, "whatever"); +}; + +TEST(UnitTestSelfTest, TestAssertThrowsWhatSuccess) { + ASSERT_THROWS_WHAT(throw MyException(), MyException, "whatever"); +} + +TEST(UnitTestSelfTest, TestSuccessfulNumericComparisons) { + ASSERT_EQUALS(1LL, 1.0); + ASSERT_NOT_EQUALS(1LL, 0.5); + ASSERT_LESS_THAN(1, 5); + ASSERT_LESS_THAN_OR_EQUALS(1, 5); + ASSERT_LESS_THAN_OR_EQUALS(5, 5); + ASSERT_GREATER_THAN(5, 1); + ASSERT_GREATER_THAN_OR_EQUALS(5, 1); + ASSERT_GREATER_THAN_OR_EQUALS(5, 5); + ASSERT_APPROX_EQUAL(5, 6, 1); +} + +TEST(UnitTestSelfTest, TestNumericComparisonFailures) { + ASSERT_TEST_FAILS(ASSERT_EQUALS(10, 1LL)); + ASSERT_TEST_FAILS(ASSERT_NOT_EQUALS(10, 10LL)); + ASSERT_TEST_FAILS(ASSERT_LESS_THAN(10, 10LL)); + ASSERT_TEST_FAILS(ASSERT_GREATER_THAN(10, 10LL)); + ASSERT_TEST_FAILS(ASSERT_NOT_LESS_THAN(9, 10LL)); + ASSERT_TEST_FAILS(ASSERT_NOT_GREATER_THAN(1, 0LL)); + ASSERT_TEST_FAILS(ASSERT_APPROX_EQUAL(5.0, 6.1, 1)); + if (std::numeric_limits<double>::has_quiet_NaN) { + ASSERT_TEST_FAILS(ASSERT_APPROX_EQUAL(5, std::numeric_limits<double>::quiet_NaN(), 1)); } - - TEST(UnitTestSelfTest, TestSuccessfulNumericComparisons) { - ASSERT_EQUALS(1LL, 1.0); - ASSERT_NOT_EQUALS(1LL, 0.5); - ASSERT_LESS_THAN(1, 5); - ASSERT_LESS_THAN_OR_EQUALS(1, 5); - ASSERT_LESS_THAN_OR_EQUALS(5, 5); - ASSERT_GREATER_THAN(5, 1); - ASSERT_GREATER_THAN_OR_EQUALS(5, 1); - ASSERT_GREATER_THAN_OR_EQUALS(5, 5); - ASSERT_APPROX_EQUAL(5, 6, 1); - } - - TEST(UnitTestSelfTest, TestNumericComparisonFailures) { - ASSERT_TEST_FAILS(ASSERT_EQUALS(10, 1LL)); - ASSERT_TEST_FAILS(ASSERT_NOT_EQUALS(10, 10LL)); - ASSERT_TEST_FAILS(ASSERT_LESS_THAN(10, 10LL)); - ASSERT_TEST_FAILS(ASSERT_GREATER_THAN(10, 10LL)); - ASSERT_TEST_FAILS(ASSERT_NOT_LESS_THAN(9, 10LL)); - ASSERT_TEST_FAILS(ASSERT_NOT_GREATER_THAN(1, 0LL)); - ASSERT_TEST_FAILS(ASSERT_APPROX_EQUAL(5.0, 6.1, 1)); - if (std::numeric_limits<double>::has_quiet_NaN) { - ASSERT_TEST_FAILS(ASSERT_APPROX_EQUAL(5, std::numeric_limits<double>::quiet_NaN(), 1)); - } - if (std::numeric_limits<double>::has_infinity) { - ASSERT_TEST_FAILS(ASSERT_APPROX_EQUAL(5, std::numeric_limits<double>::infinity(), 1)); - } - } - - TEST(UnitTestSelfTest, TestStringComparisons) { - ASSERT_EQUALS(std::string("hello"), "hello"); - ASSERT_EQUALS("hello", std::string("hello")); - - ASSERT_NOT_EQUALS(std::string("hello"), "good bye!"); - ASSERT_NOT_EQUALS("hello", std::string("good bye!")); - - ASSERT_TEST_FAILS(ASSERT_NOT_EQUALS(std::string("hello"), "hello")); - ASSERT_TEST_FAILS(ASSERT_NOT_EQUALS("hello", std::string("hello"))); - - ASSERT_TEST_FAILS(ASSERT_EQUALS(std::string("hello"), "good bye!")); - ASSERT_TEST_FAILS(ASSERT_EQUALS("hello", std::string("good bye!"))); - } - - TEST(UnitTestSelfTest, TestStreamingIntoFailures) { - ASSERT_TEST_FAILS_MATCH(ASSERT_TRUE(false) << "Told you so", "Told you so"); - ASSERT_TEST_FAILS_MATCH(ASSERT(false) << "Told you so", "Told you so"); - ASSERT_TEST_FAILS_MATCH(ASSERT_FALSE(true) << "Told you so", "Told you so"); - ASSERT_TEST_FAILS_MATCH(ASSERT_EQUALS(1, 2) << "Told you so", "Told you so"); - ASSERT_TEST_FAILS_MATCH(FAIL("Foo") << "Told you so", "Told you so"); - } - - TEST(UnitTestSelfTest, TestNoDoubleEvaluation) { - int i = 0; - ASSERT_TEST_FAILS_MATCH(ASSERT_EQ(0, ++i), "(0 == 1)"); + if (std::numeric_limits<double>::has_infinity) { + ASSERT_TEST_FAILS(ASSERT_APPROX_EQUAL(5, std::numeric_limits<double>::infinity(), 1)); } +} + +TEST(UnitTestSelfTest, TestStringComparisons) { + ASSERT_EQUALS(std::string("hello"), "hello"); + ASSERT_EQUALS("hello", std::string("hello")); + + ASSERT_NOT_EQUALS(std::string("hello"), "good bye!"); + ASSERT_NOT_EQUALS("hello", std::string("good bye!")); + + ASSERT_TEST_FAILS(ASSERT_NOT_EQUALS(std::string("hello"), "hello")); + ASSERT_TEST_FAILS(ASSERT_NOT_EQUALS("hello", std::string("hello"))); + + ASSERT_TEST_FAILS(ASSERT_EQUALS(std::string("hello"), "good bye!")); + ASSERT_TEST_FAILS(ASSERT_EQUALS("hello", std::string("good bye!"))); +} + +TEST(UnitTestSelfTest, TestStreamingIntoFailures) { + ASSERT_TEST_FAILS_MATCH(ASSERT_TRUE(false) << "Told you so", "Told you so"); + ASSERT_TEST_FAILS_MATCH(ASSERT(false) << "Told you so", "Told you so"); + ASSERT_TEST_FAILS_MATCH(ASSERT_FALSE(true) << "Told you so", "Told you so"); + ASSERT_TEST_FAILS_MATCH(ASSERT_EQUALS(1, 2) << "Told you so", "Told you so"); + ASSERT_TEST_FAILS_MATCH(FAIL("Foo") << "Told you so", "Told you so"); +} + +TEST(UnitTestSelfTest, TestNoDoubleEvaluation) { + int i = 0; + ASSERT_TEST_FAILS_MATCH(ASSERT_EQ(0, ++i), "(0 == 1)"); +} } // namespace |