Long live QVERIFY_THROWS_EXCEPTION!

Use variable args macros to swallow any extra commas in the
expression. To use this, the type of the exception has to be first.
Use Eddy's suggestion for a new name to avoid breaking the old macro.

[ChangeLog][QtTest] Added QVERIFY_THROWS_EXCEPTION, replacing
QVERIFY_EXCEPTION_THROWN, which has therefore been deprecated.

Change-Id: I16825c35bae0631c5fad5a9a3ace4d6edc067f83
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Marc Mutz 2021-11-23 08:52:22 +01:00
parent b9be035c62
commit 1edf153a6b
5 changed files with 78 additions and 14 deletions

View File

@ -93,11 +93,21 @@ do {\
#ifndef QT_NO_EXCEPTIONS
#if QT_DEPRECATED_SINCE(6, 3)
namespace QTest {
QT_DEPRECATED_VERSION_X_6_3("Don't use QVERIFY_EXCEPTION_THROWN(expr, type) anymore, "
"use QVERIFY_THROWS_EXCEPTION(type, expr...) instead")
inline void useVerifyThrowsException() {}
} // namespace QTest
# define QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) \
QVERIFY_THROWS_EXCEPTION(exceptiontype, QTest::useVerifyThrowsException(); expression)
#endif
# define QVERIFY_THROWS_EXCEPTION(exceptiontype, ...) \
do {\
QT_TRY {\
QT_TRY {\
expression;\
__VA_ARGS__;\
QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \
" but no exception caught", __FILE__, __LINE__);\
return;\
@ -125,7 +135,7 @@ do {\
* So, users must use Qt with exception support enabled if they use exceptions
* in their code.
*/
# define QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) \
# define QVERIFY_THROWS_EXCEPTION(...) \
static_assert(false, "Support of exceptions is disabled")
#endif // !QT_NO_EXCEPTIONS

View File

@ -158,17 +158,23 @@
\since 5.3
\relates QTest
\deprecated [6.3] Use \c{QVERIFY_THROWS_EXCEPTION(exceptiontype, expression)} instead.
*/
The QVERIFY_EXCEPTION_THROWN macro executes \a expression
and tries to catch an exception thrown from \a expression.
/*!
\macro QVERIFY_THROWS_EXCEPTION(exceptiontype, ...)
\since 6.3
The QVERIFY_THROWS_EXCEPTION macro executes the expression given in the variadic
argument and expects to catch an exception thrown from the expression.
There are several possible outcomes:
\list
\li If \a expression throws an exception that is either the same as
\li If the expression throws an exception that is either the same as
\a exceptiontype or derived from \a exceptiontype, then execution will continue.
\li Otherwise, if \a expression throws no exception, or the
\li Otherwise, if the expression throws no exception, or the
exception thrown derives from \c{std::exception}, then a failure
will be recorded in the test log and the macro returns early
(from enclosing function).
@ -178,6 +184,16 @@
re-thrown. This avoids problems with e.g. pthread cancellation exceptions.
\endlist
The macro uses variadic arguments so the expression can contain commas that the
preprocessor considers argument separators, e.g. as in
\code
QVERIFY_THROWS_EXCEPTION(std::bad_alloc,
// macro arguments: ^ exceptiontype
std::vector<std::pair<int, long>>{42'000'000'000, {42, 42L}});
// macro arguments: \---------- 1 ----------/ \-------- 2 --------/ \3/ \ 4 /
// \----------------------- expression -----------------------/
\endcode
\note This macro can only be used in a test function that is invoked
by the test framework.
*/

View File

@ -1584,7 +1584,7 @@ void tst_QThread::create()
const auto &function = [](const ThrowWhenCopying &){};
QScopedPointer<QThread> thread;
ThrowWhenCopying t;
QVERIFY_EXCEPTION_THROWN(thread.reset(QThread::create(function, t)), ThreadException);
QVERIFY_THROWS_EXCEPTION(ThreadException, thread.reset(QThread::create(function, t)));
QVERIFY(!thread);
}
#endif // QT_NO_EXCEPTIONS

View File

@ -2688,7 +2688,7 @@ void tst_QSharedPointer::constructorThrow()
int childDestructorCounter = ThrowData::childDestructorCounter;
QSharedPointer<ThrowData> ptr;
QVERIFY_EXCEPTION_THROWN(ptr = QSharedPointer<ThrowData>::create(), QString);
QVERIFY_THROWS_EXCEPTION(QString, ptr = QSharedPointer<ThrowData>::create());
QVERIFY(ptr.isNull());
QCOMPARE(ThrowData::childGenerationCounter, childGeneration + 1);
// destructor should never be called, if a constructor throws

View File

@ -81,67 +81,105 @@ private slots:
void tst_VerifyExceptionThrown::testCorrectStdTypes() const
{
QVERIFY_THROWS_EXCEPTION(int, throw int(5));
QVERIFY_THROWS_EXCEPTION(float, throw float(9.8));
QVERIFY_THROWS_EXCEPTION(bool, throw bool(true));
QVERIFY_THROWS_EXCEPTION(std::string, throw std::string("some string"));
#if QT_DEPRECATED_SINCE(6, 3)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QVERIFY_EXCEPTION_THROWN(throw int(5), int);
QVERIFY_EXCEPTION_THROWN(throw float(9.8), float);
QVERIFY_EXCEPTION_THROWN(throw bool(true), bool);
QVERIFY_EXCEPTION_THROWN(throw std::string("some string"), std::string);
QT_WARNING_POP
#endif
}
void tst_VerifyExceptionThrown::testCorrectStdExceptions() const
{
// same type
QVERIFY_THROWS_EXCEPTION(std::exception, throw std::exception());
QVERIFY_THROWS_EXCEPTION(std::runtime_error, throw std::runtime_error("runtime error"));
QVERIFY_THROWS_EXCEPTION(std::overflow_error, throw std::overflow_error("overflow error"));
#if QT_DEPRECATED_SINCE(6, 3)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QVERIFY_EXCEPTION_THROWN(throw std::exception(), std::exception);
QVERIFY_EXCEPTION_THROWN(throw std::runtime_error("runtime error"), std::runtime_error);
QVERIFY_EXCEPTION_THROWN(throw std::overflow_error("overflow error"), std::overflow_error);
QT_WARNING_POP
#endif
// inheritance
QVERIFY_THROWS_EXCEPTION(std::runtime_error, throw std::overflow_error("overflow error"));
QVERIFY_THROWS_EXCEPTION(std::exception, throw std::overflow_error("overflow error"));
#if QT_DEPRECATED_SINCE(6, 3)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QVERIFY_EXCEPTION_THROWN(throw std::overflow_error("overflow error"), std::runtime_error);
QVERIFY_EXCEPTION_THROWN(throw std::overflow_error("overflow error"), std::exception);
QT_WARNING_POP
#endif
}
void tst_VerifyExceptionThrown::testCorrectMyExceptions() const
{
// same type
QVERIFY_THROWS_EXCEPTION(MyBaseException, throw MyBaseException());
QVERIFY_THROWS_EXCEPTION(MyDerivedException, throw MyDerivedException());
#if QT_DEPRECATED_SINCE(6, 3)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QVERIFY_EXCEPTION_THROWN(throw MyBaseException(), MyBaseException);
QVERIFY_EXCEPTION_THROWN(throw MyDerivedException(), MyDerivedException);
QT_WARNING_POP
#endif
// inheritance
QVERIFY_THROWS_EXCEPTION(MyBaseException, throw MyDerivedException());
QVERIFY_THROWS_EXCEPTION(std::domain_error, throw MyDerivedException());
#if QT_DEPRECATED_SINCE(6, 3)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QVERIFY_EXCEPTION_THROWN(throw MyDerivedException(), MyBaseException);
QVERIFY_EXCEPTION_THROWN(throw MyDerivedException(), std::domain_error);
QT_WARNING_POP
#endif
}
void tst_VerifyExceptionThrown::testFailInt() const
{
try {
QVERIFY_EXCEPTION_THROWN(throw int(5), double);
QVERIFY_THROWS_EXCEPTION(double, throw int(5));
} catch (int) {}
}
void tst_VerifyExceptionThrown::testFailStdString() const
{
try {
QVERIFY_EXCEPTION_THROWN(throw std::string("some string"), char*);
QVERIFY_THROWS_EXCEPTION(char*, throw std::string("some string"));
} catch (const std::string &) {}
}
void tst_VerifyExceptionThrown::testFailStdRuntimeError() const
{
QVERIFY_EXCEPTION_THROWN(throw std::logic_error("logic error"), std::runtime_error);
QVERIFY_THROWS_EXCEPTION(std::runtime_error, throw std::logic_error("logic error"));
}
void tst_VerifyExceptionThrown::testFailMyException() const
{
QVERIFY_EXCEPTION_THROWN(throw std::logic_error("logic error"), MyBaseException);
QVERIFY_THROWS_EXCEPTION(MyBaseException, throw std::logic_error("logic error"));
}
void tst_VerifyExceptionThrown::testFailMyDerivedException() const
{
QVERIFY_EXCEPTION_THROWN(throw MyDerivedException(), std::runtime_error);
QVERIFY_THROWS_EXCEPTION(std::runtime_error, throw MyDerivedException());
}
void tst_VerifyExceptionThrown::testFailNoException() const
{
QVERIFY_EXCEPTION_THROWN(doSomething(), std::exception);
QVERIFY_THROWS_EXCEPTION(std::exception, doSomething());
}
#endif // !QT_NO_EXCEPTIONS