summaryrefslogtreecommitdiff
path: root/src/mongo/unittest
diff options
context:
space:
mode:
authorAndy Schwerin <schwerin@mongodb.com>2014-10-09 10:28:20 -0400
committerAndy Schwerin <schwerin@mongodb.com>2014-10-09 10:28:20 -0400
commit1c9d735cc6d439eb1b5e0b8f92c81d0b88b53a53 (patch)
tree728901d9142c9f09f27dffc54dc5b1f8e520da50 /src/mongo/unittest
parent2eaa2d50def6702f5df83ec5ab97e92c3dc8b476 (diff)
downloadmongo-1c9d735cc6d439eb1b5e0b8f92c81d0b88b53a53.tar.gz
SERVER-15567 Allow streaming into unittest assertions.
Diffstat (limited to 'src/mongo/unittest')
-rw-r--r--src/mongo/unittest/unittest-inl.h9
-rw-r--r--src/mongo/unittest/unittest.cpp40
-rw-r--r--src/mongo/unittest/unittest.h223
-rw-r--r--src/mongo/unittest/unittest_test.cpp14
4 files changed, 119 insertions, 167 deletions
diff --git a/src/mongo/unittest/unittest-inl.h b/src/mongo/unittest/unittest-inl.h
index cdd2b0cbaa0..0ad891506a8 100644
--- a/src/mongo/unittest/unittest-inl.h
+++ b/src/mongo/unittest/unittest-inl.h
@@ -37,14 +37,5 @@ namespace mongo {
Suite::getSuite(suiteName)->add<T>(testName);
}
- template<typename A, typename B>
- std::string ComparisonAssertion::getComparisonFailureMessage(const std::string &theOperator,
- const A &a, const B &b) {
- std::ostringstream os;
- os << "Expected " << _aexp << " " << theOperator << " " << _bexp
- << " (" << a << " " << theOperator << " " << b << ")";
- return os.str();
- }
-
} // namespace mongo
} // namespace unittest
diff --git a/src/mongo/unittest/unittest.cpp b/src/mongo/unittest/unittest.cpp
index feb9a2aeb05..64f63fa4cc2 100644
--- a/src/mongo/unittest/unittest.cpp
+++ b/src/mongo/unittest/unittest.cpp
@@ -371,38 +371,42 @@ namespace {
TestAssertionFailure::TestAssertionFailure(const std::string& file,
unsigned line,
const std::string& message)
- : _exception(file, line, 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 (!_stream.str().empty())
+ 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;
}
- TestAssertion::TestAssertion( const char* file, unsigned line )
- : _file( file ), _line( line ) {
-
- ++Result::cur->_asserts;
- }
-
- TestAssertion::~TestAssertion() {}
-
- void TestAssertion::fail( const std::string& message ) const {
- throw TestAssertionFailureException( _file, _line, message );
- }
-
- ComparisonAssertion::ComparisonAssertion( const char* aexp, const char* bexp,
- const char* file, unsigned line )
- : TestAssertion( file, line ), _aexp( aexp ), _bexp( bexp ) {}
-
std::vector<std::string> getAllSuiteNames() {
std::vector<std::string> result;
for (SuiteMap::const_iterator i = _allSuites().begin(); i != _allSuites().end(); ++i) {
diff --git a/src/mongo/unittest/unittest.h b/src/mongo/unittest/unittest.h
index a7a1c1a9b60..374b6674e90 100644
--- a/src/mongo/unittest/unittest.h
+++ b/src/mongo/unittest/unittest.h
@@ -54,54 +54,61 @@
/**
* Fail unconditionally, reporting the given message.
*/
-#define FAIL(MESSAGE) ::mongo::unittest::TestAssertion( __FILE__ , __LINE__ ).fail( (MESSAGE) )
+#define FAIL(MESSAGE) ::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, MESSAGE).stream()
/**
* Fails unless "EXPRESSION" is true.
*/
-#define ASSERT_TRUE(EXPRESSION) if (!(EXPRESSION)) ::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, "Expected: " #EXPRESSION).stream()
+#define ASSERT_TRUE(EXPRESSION) if (!(EXPRESSION)) FAIL("Expected: " #EXPRESSION)
#define ASSERT(EXPRESSION) ASSERT_TRUE(EXPRESSION)
/**
- * Assert that a Status code is OK.
+ * Fails if "EXPRESSION" is true.
*/
-#define ASSERT_OK(EXPRESSION) ASSERT_EQUALS(::mongo::Status::OK(), (EXPRESSION))
+#define ASSERT_FALSE(EXPRESSION) ASSERT(!(EXPRESSION))
/**
- * Assert that a status code is anything but OK.
+ * Asserts that a Status code is OK.
*/
-#define ASSERT_NOT_OK(EXPRESSION) ASSERT_NOT_EQUALS(::mongo::Status::OK(), (EXPRESSION))
+#define ASSERT_OK(EXPRESSION) ASSERT_EQUALS(::mongo::Status::OK(), (EXPRESSION))
/**
- * Fails if "EXPRESSION" is true.
+ * Asserts that a status code is anything but OK.
*/
-#define ASSERT_FALSE(EXPRESSION) if (EXPRESSION) \
-::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, "Expected: !(" #EXPRESSION ")").stream()
+#define ASSERT_NOT_OK(EXPRESSION) ASSERT_NOT_EQUALS(::mongo::Status::OK(), (EXPRESSION))
/*
* Binary comparison assertions.
*/
-#define ASSERT_EQUALS(a,b) _ASSERT_COMPARISON(Equal, a, b)
-#define ASSERT_NOT_EQUALS(a,b) _ASSERT_COMPARISON(NotEqual, a, b)
-#define ASSERT_LESS_THAN(a, b) _ASSERT_COMPARISON(LessThan, a, b)
-#define ASSERT_NOT_LESS_THAN(a, b) _ASSERT_COMPARISON(NotLessThan, a, b)
-#define ASSERT_GREATER_THAN(a, b) _ASSERT_COMPARISON(GreaterThan, a, b)
-#define ASSERT_NOT_GREATER_THAN(a, b) _ASSERT_COMPARISON(NotGreaterThan, a, b)
-#define ASSERT_LESS_THAN_OR_EQUALS(a, b) ASSERT_NOT_GREATER_THAN(a, b)
-#define ASSERT_GREATER_THAN_OR_EQUALS(a, b) ASSERT_NOT_LESS_THAN(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)
+#define ASSERT_NOT_GREATER_THAN(a, b) ASSERT_LTE(a, b)
+#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_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)
+#define ASSERT_GTE(a, b) _ASSERT_COMPARISON(GTE, a, b)
/**
* Binary comparison utility macro. Do not use directly.
*/
-#define _ASSERT_COMPARISON(COMPARISON, a, b) ::mongo::unittest::ComparisonAssertion( \
- #a, #b , __FILE__ , __LINE__ ).assert##COMPARISON( (a), (b) )
+#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) ::mongo::unittest::assertApproxEqual( \
- #a, #b, a, b, ABSOLUTE_ERR, __FILE__, __LINE__)
+#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.
@@ -110,8 +117,8 @@
* of a subtype of "EXCEPTION_TYPE", the test is considered a failure and further evaluation
* halts.
*/
-#define ASSERT_THROWS(EXPRESSION, EXCEPTION_TYPE) \
- ASSERT_THROWS_PRED(EXPRESSION, \
+#define ASSERT_THROWS(STATEMENT, EXCEPTION_TYPE) \
+ ASSERT_THROWS_PRED(STATEMENT, \
EXCEPTION_TYPE, \
::mongo::stdx::bind(::mongo::unittest::alwaysTrue))
@@ -119,8 +126,8 @@
* 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(EXPRESSION, EXCEPTION_TYPE, EXPECTED_WHAT) \
- ASSERT_THROWS_PRED(EXPRESSION, \
+#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, \
@@ -130,20 +137,19 @@
* Behaves like ASSERT_THROWS, above, but also fails if PREDICATE(ex) for the throw exception, ex,
* is false.
*/
-#define ASSERT_THROWS_PRED(EXPRESSION, EXCEPTION_TYPE, PREDICATE) do { \
- ::mongo::unittest::TestAssertion _testAssertion( __FILE__, __LINE__ ); \
+#define ASSERT_THROWS_PRED(STATEMENT, EXCEPTION_TYPE, PREDICATE) do { \
try { \
- EXPRESSION; \
- _testAssertion.fail("Expected expression " #EXPRESSION \
- " to throw " #EXCEPTION_TYPE \
- " but it threw nothing."); \
+ STATEMENT; \
+ FAIL("Expected statement " #STATEMENT \
+ " to throw " #EXCEPTION_TYPE \
+ " but it threw nothing."); \
} catch (const EXCEPTION_TYPE& ex) { \
if (!(PREDICATE(ex))) { \
- _testAssertion.fail("Expected " #EXPRESSION \
- " to throw an exception of type " \
- #EXCEPTION_TYPE \
- " where " #PREDICATE \
- "(ex) was true, but it was false."); \
+ FAIL("Expected " #STATEMENT \
+ " to throw an exception of type " \
+ #EXCEPTION_TYPE \
+ " where " #PREDICATE \
+ "(ex) was true, but it was false."); \
} \
} \
} while (false)
@@ -403,120 +409,65 @@ namespace mongo {
};
class TestAssertionFailure {
- MONGO_DISALLOW_COPYING(TestAssertionFailure);
public:
TestAssertionFailure(
const std::string& file, unsigned line, const std::string& message);
+ TestAssertionFailure(const TestAssertionFailure& other);
#if __cplusplus < 201103
~TestAssertionFailure();
#else
~TestAssertionFailure() noexcept(false);
#endif
+
+ TestAssertionFailure& operator=(const TestAssertionFailure& other);
+
std::ostream& stream();
private:
TestAssertionFailureException _exception;
std::ostringstream _stream;
+ bool _enabled;
};
- /**
- * Object representing an assertion about some condition.
- */
- class TestAssertion : private boost::noncopyable {
-
- public:
- /**
- * file std::string must stay in scope and remain unchanged for the lifetime
- * of the TestAssertion object.
- */
- TestAssertion( const char* file, unsigned line );
- ~TestAssertion();
-
- MONGO_COMPILER_NORETURN void fail( const std::string& message) const;
- void failIf( bool flag, const std::string &message ) const {
- if ( flag ) fail( message );
- }
-
- private:
- const char* _file;
- const unsigned _line;
- };
-
- /**
- * Specialization of TestAssertion for binary comparisons.
- */
- class ComparisonAssertion : private TestAssertion {
- public:
- /**
- * All char* arguments must stay in scope and remain unchanged for the lifetime
- * of the ComparisonAssertion object.
- */
- ComparisonAssertion( const char* aexp , const char* bexp ,
- const char* file , unsigned line );
-
- template<typename A,typename B>
- void assertEqual( const A& a , const B& b ) {
- if ( a == b )
- return;
- fail(getComparisonFailureMessage("==", a, b));
- }
-
- template<typename A,typename B>
- void assertNotEqual( const A& a , const B& b ) {
- if ( a != b )
- return;
- fail(getComparisonFailureMessage("!=", a, b));
- }
-
- template<typename A,typename B>
- void assertLessThan( const A& a , const B& b ) {
- if ( a < b )
- return;
- fail(getComparisonFailureMessage("<", a, b));
- }
-
- template<typename A,typename B>
- void assertNotLessThan( const A& a , const B& b ) {
- if ( a >= b )
- return;
- fail(getComparisonFailureMessage(">=", a, b));
- }
-
- template<typename A,typename B>
- void assertGreaterThan( const A& a , const B& b ) {
- if ( a > b )
- return;
- fail(getComparisonFailureMessage(">", a, b));
- }
-
- template<typename A,typename B>
- void assertNotGreaterThan( const A& a , const B& b ) {
- if ( a <= b )
- return;
- fail(getComparisonFailureMessage("<=", a, b));
- }
-
- private:
- template< typename A, typename B>
- std::string getComparisonFailureMessage(const std::string &theOperator,
- const A& a, const B& b);
-
- const char* _aexp;
- const char* _bexp;
- };
-
- /**
- * Helper for ASSERT_APPROX_EQUAL to ensure that the arguments are evaluated only once.
- */
- template < typename A, typename B, typename ABSOLUTE_ERR >
- inline void assertApproxEqual(const char* aexp, const char* bexp,
- const A& a, const B& b, const ABSOLUTE_ERR& absoluteErr,
- const char* file , unsigned line) {
- if (std::abs(a - b) <= absoluteErr)
- return;
- TestAssertion(file, line).fail(mongoutils::str::stream()
- << "Expected " << aexp << " and " << bexp << " to be within " << absoluteErr
- << " of each other ((" << a << ") - (" << b << ") = " << (a - b) << ")");
- }
+#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, >=);
+#undef DECLARE_COMPARISON_ASSERTION
/**
* Get the value out of a StatusWith<T>, or throw an exception if it is not OK.
diff --git a/src/mongo/unittest/unittest_test.cpp b/src/mongo/unittest/unittest_test.cpp
index 4e62c47761c..7c557a92b56 100644
--- a/src/mongo/unittest/unittest_test.cpp
+++ b/src/mongo/unittest/unittest_test.cpp
@@ -46,12 +46,12 @@ namespace {
return value.find(pattern) != std::string::npos;
}
-#define ASSERT_TEST_FAILS(TEST_EXPR) \
- ASSERT_THROWS((TEST_EXPR), mongo::unittest::TestAssertionFailureException)
+#define ASSERT_TEST_FAILS(TEST_STMT) \
+ ASSERT_THROWS(TEST_STMT, mongo::unittest::TestAssertionFailureException)
-#define ASSERT_TEST_FAILS_MATCH(TEST_EXPR, PATTERN) \
+#define ASSERT_TEST_FAILS_MATCH(TEST_STMT, PATTERN) \
ASSERT_THROWS_PRED( \
- TEST_EXPR, \
+ TEST_STMT, \
mongo::unittest::TestAssertionFailureException, \
stdx::bind(containsPattern, \
PATTERN, \
@@ -122,6 +122,12 @@ namespace {
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");
+ }
+
+ TEST(UnitTestSelfTest, TestNoDoubleEvaluation) {
+ int i = 0;
+ ASSERT_TEST_FAILS_MATCH(ASSERT_EQ(0, ++i), "(0 == 1)");
}
} // namespace