syncevolution/test/ClientTestAssert.h

158 lines
7.2 KiB
C++

/*
* Copyright (C) 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef INCL_CLIENTTESTASSERT
#define INCL_CLIENTTESTASSERT
#include <cppunit/Exception.h>
#include <syncevo/util.h>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
/**
* All of the macros below include the behavior of the corresponding
* CPUNIT assertion. In addition they catch exceptions and turn them
* into the following, extended CPPUnit exception by preserving the
* information from the original exception and adding the current
* source code line.
*
* Source information is listed as inner first (as part of the error
* preamble), followed by error at that location and all locations
* that the error passed through until caught at the top level of a
* test.
*/
class CTException : public CppUnit::Exception
{
public:
CTException(const CppUnit::Message &message = CppUnit::Message(),
const std::string &currentMessage = "",
const CppUnit::SourceLine &currentSourceLine = CppUnit::SourceLine(),
const CppUnit::SourceLine &previousSourceLine = CppUnit::SourceLine()) :
CppUnit::Exception(message, previousSourceLine)
{
CppUnit::Message extendedMessage = message;
if (!currentMessage.empty()) {
extendedMessage.addDetail(currentMessage);
}
if (currentSourceLine.isValid()) {
extendedMessage.addDetail(StringPrintf("%s:%d",
getBasename(currentSourceLine.fileName()).c_str(),
currentSourceLine.lineNumber()));
}
setMessage(extendedMessage);
}
};
// Avoid clang 'error: attributes are not allowed on a function-definition' by putting the SE_NORETURN
// into a separate declaration.
static void ClientTestExceptionHandle(const char *file, int line, const std::string &message = "") SE_NORETURN;
static void ClientTestExceptionHandle(const char *file, int line, const std::string &message)
{
CppUnit::SourceLine here(file, line);
try {
throw;
} catch (const CTException &ex) {
throw(CTException(ex.message(),
message,
here,
ex.sourceLine()));
} catch (const CppUnit::Exception &ex) {
if (ex.sourceLine() == CPPUNIT_SOURCELINE()) {
/* failure in condition expression itself, already includes source info, pass through */
throw;
} else {
throw(CTException(ex.message(),
message,
here,
ex.sourceLine()));
}
} catch (const Exception &ex) {
throw(CTException(CppUnit::Message(ex.what()),
message,
here,
CppUnit::SourceLine(ex.m_file, ex.m_line)));
} catch (const std::exception &ex) {
CppUnit::Message msg(CPPUNIT_EXTRACT_EXCEPTION_TYPE_(ex,
"std::exception or derived"));
msg.addDetail(std::string("What(): ") + ex.what());
throw(CTException(msg,
message,
here));
}
}
// The only purpose of this here is to teach clang's scan-build that certain
// code paths are not taken. Because the program just exists on failed
// asserts, the resulting binary should not be used for real testing.
// (http://clang-analyzer.llvm.org/annotations.html#custom_assertions).
#ifdef CT_ASSERT_SIMPLE
// Disable CPPUNIT, use simple assert.
# define CT_ASSERT_NO_CLANG_BUILD(_assert)
# define CT_ASSERT_TRUE_CLANG_BUILD(_expression) if (!(_expression)) { exit(1); }
# define CT_ASSERT_EXE_CLANG_BUILD(_statement) _statement
#else
// Use CPPUNIT assert, disable simple assert.
# define CT_ASSERT_NO_CLANG_BUILD(_assert) _assert
# define CT_ASSERT_TRUE_CLANG_BUILD(_expression)
# define CT_ASSERT_EXE_CLANG_BUILD(_statement)
#endif
#define CT_WRAP_ASSERT(_file, _line, _assert, _expression) \
do { \
try { \
SE_LOG_DEBUG(NULL, "%s:%d: starting %s", getBasename(_file).c_str(), _line, #_assert); \
CT_ASSERT_NO_CLANG_BUILD(_assert); \
_expression; \
SE_LOG_DEBUG(NULL, "%s:%d: ending %s", getBasename(_file).c_str(), _line, #_assert); \
} catch (...) { \
ClientTestExceptionHandle(_file, _line); \
} \
} while (false)
#define CT_WRAP_ASSERT_MESSAGE(_file, _line, _message, _assert, _expression) \
do { \
try { \
SE_LOG_DEBUG(NULL, "%s:%d: starting %s %s", \
getBasename(_file).c_str(), _line, \
std::string(_message).c_str(), \
#_assert); \
CT_ASSERT_NO_CLANG_BUILD(_assert); \
_expression; \
SE_LOG_DEBUG(NULL, "%s:%d: ending %s", getBasename(_file).c_str(), _line, #_assert); \
} catch (...) { \
ClientTestExceptionHandle(_file, _line, _message); \
} \
} while (false)
#define CT_ASSERT(condition) CT_WRAP_ASSERT(__FILE__, __LINE__, CPPUNIT_ASSERT(condition), CT_ASSERT_TRUE_CLANG_BUILD(condition))
#define CT_ASSERT_NO_THROW(expression) CT_WRAP_ASSERT(__FILE__, __LINE__, expression, CT_ASSERT_EXE_CLANG_BUILD(expression))
#define CT_ASSERT_NO_THROW_MESSAGE(message, expression) CT_WRAP_ASSERT_MESSAGE(__FILE__, __LINE__, message, expression, CT_ASSERT_EXE_CLANG_BUILD(expression))
#define CT_ASSERT_MESSAGE(message,condition) CT_WRAP_ASSERT(__FILE__, __LINE__, CPPUNIT_ASSERT_MESSAGE(message, (condition)), CT_ASSERT_TRUE_CLANG_BUILD(condition))
#define CT_FAIL(message) CPPUNIT_FAIL(message)
#define CT_ASSERT_EQUAL(expected,actual) CT_WRAP_ASSERT(__FILE__, __LINE__, CPPUNIT_ASSERT_EQUAL((expected),(actual)), CT_ASSERT_TRUE_CLANG_BUILD((expected) == (actual)))
#define CT_ASSERT_EQUAL_MESSAGE(message,expected,actual) CT_WRAP_ASSERT(__FILE__, __LINE__, CPPUNIT_ASSERT_EQUAL_MESSAGE(message,(expected),(actual)), CT_ASSERT_TRUE_CLANG_BUILD((expected) == (actual)))
#define CT_ASSERT_DOUBLES_EQUAL(expected,actual,delta) CT_WRAP_ASSERT(__FILE__, __LINE__, CPPUNIT_ASSERT_DOUBLES_EQUAL((expected),(actual),(delta)), CT_ASSERT_TRUE_CLANG_BUILD(fabs((expected) - (actual)) < (delta)))
#define CT_ASSERT_DOUBLES_EQUAL_MESSAGE(message,expected,actual,delta) CT_WRAP_ASSERT(__FILE__, __LINE__, CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(__FILE__, __LINE__, message,(expected),(actual),(delta)), CT_ASSERT_TRUE_CLANG_BUILD(fabs((expected) - (actual)) < (delta)))
SE_END_CXX
#endif // INCL_CLIENTTESTASSERT