QTEST_MAIN 如何自动将所有内容设置为 运行 测试?
How does QTEST_MAIN set everything up automatically to run tests?
我正在处理一个用 C++ 编写的现有项目,该应用程序的入口点是:
QTEST_MAIN(className)
从 the documentation I've read 开始,这将创建一个标准的 C main()
函数,但对于如何调用应用程序测试、调用顺序是什么或其设置方式一无所知-up.
查看我的项目中的class,没有class构造函数; class 本身是从 QObject 派生的。它有 23 个专用插槽:其中一个称为 "initTestCase";其他的是各种测试,都以 "Test".
结尾
插槽 "InitTestCase" 包含一个设置日志过滤规则的调用,仅此而已。当项目编译并运行时,它执行测试,但我看不到命令是如何或从哪里来的。
宏 QTEST_MAIN
在我的程序中实际做了什么,插槽是如何设置的,它如何知道要执行哪些测试?
QTEST_MAIN 转发到 QTEST_MAIN_IMPL:
#define QTEST_MAIN(TestObject) \
int main(int argc, char *argv[]) \
{ \
QTEST_MAIN_IMPL(TestObject) \
}
这个 QTEST_MAIN_IMPL 根据您需要的 QApplication(Widgets、Gui 或 Core)而不同。对于小部件,它看起来像这样:
#define QTEST_MAIN_IMPL(TestObject) \
TESTLIB_SELFCOVERAGE_START(#TestObject) \
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
QApplication app(argc, argv); \
app.setAttribute(Qt::AA_Use96Dpi, true); \
QTEST_DISABLE_KEYPAD_NAVIGATION \
TestObject tc; \
QTEST_SET_MAIN_SOURCE_PATH \
return QTest::qExec(&tc, argc, argv);
QTest::qExec 已定义 qtestcase.cpp:
int QTest::qExec(QObject *testObject, int argc, char **argv)
{
qInit(testObject, argc, argv);
int ret = qRun();
qCleanup();
return ret;
}
在qInit()中,设置了currentTestObject。
在 qRun(), a TestMethods 中创建实例及其构造函数,我们发现这个循环:
const QMetaObject *metaObject = o->metaObject();
const int count = metaObject->methodCount();
m_methods.reserve(count);
for (int i = 0; i < count; ++i) {
const QMetaMethod me = metaObject->method(i);
if (isValidSlot(me))
m_methods.push_back(me);
}
isValidSlot()是这样实现的:
static bool isValidSlot(const QMetaMethod &sl)
{
if (sl.access() != QMetaMethod::Private || sl.parameterCount() != 0
|| sl.returnType() != QMetaType::Void || sl.methodType() != QMetaMethod::Slot)
return false;
const QByteArray name = sl.name();
return !(name.isEmpty() || name.endsWith("_data")
|| name == "initTestCase" || name == "cleanupTestCase"
|| name == "init" || name == "cleanup");
}
最后,调用 TestMethods::invokeMethod(),首先明确检查 initTestCase
并运行它:
QTestResult::setCurrentTestFunction("initTestCase");
if (m_initTestCaseDataMethod.isValid())
m_initTestCaseDataMethod.invoke(testObject, Qt::DirectConnection);
同样,它会在最后检查 cleanupTestCase
。
我正在处理一个用 C++ 编写的现有项目,该应用程序的入口点是:
QTEST_MAIN(className)
从 the documentation I've read 开始,这将创建一个标准的 C main()
函数,但对于如何调用应用程序测试、调用顺序是什么或其设置方式一无所知-up.
查看我的项目中的class,没有class构造函数; class 本身是从 QObject 派生的。它有 23 个专用插槽:其中一个称为 "initTestCase";其他的是各种测试,都以 "Test".
结尾插槽 "InitTestCase" 包含一个设置日志过滤规则的调用,仅此而已。当项目编译并运行时,它执行测试,但我看不到命令是如何或从哪里来的。
宏 QTEST_MAIN
在我的程序中实际做了什么,插槽是如何设置的,它如何知道要执行哪些测试?
QTEST_MAIN 转发到 QTEST_MAIN_IMPL:
#define QTEST_MAIN(TestObject) \
int main(int argc, char *argv[]) \
{ \
QTEST_MAIN_IMPL(TestObject) \
}
这个 QTEST_MAIN_IMPL 根据您需要的 QApplication(Widgets、Gui 或 Core)而不同。对于小部件,它看起来像这样:
#define QTEST_MAIN_IMPL(TestObject) \
TESTLIB_SELFCOVERAGE_START(#TestObject) \
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
QApplication app(argc, argv); \
app.setAttribute(Qt::AA_Use96Dpi, true); \
QTEST_DISABLE_KEYPAD_NAVIGATION \
TestObject tc; \
QTEST_SET_MAIN_SOURCE_PATH \
return QTest::qExec(&tc, argc, argv);
QTest::qExec 已定义 qtestcase.cpp:
int QTest::qExec(QObject *testObject, int argc, char **argv)
{
qInit(testObject, argc, argv);
int ret = qRun();
qCleanup();
return ret;
}
在qInit()中,设置了currentTestObject。
在 qRun(), a TestMethods 中创建实例及其构造函数,我们发现这个循环:
const QMetaObject *metaObject = o->metaObject();
const int count = metaObject->methodCount();
m_methods.reserve(count);
for (int i = 0; i < count; ++i) {
const QMetaMethod me = metaObject->method(i);
if (isValidSlot(me))
m_methods.push_back(me);
}
isValidSlot()是这样实现的:
static bool isValidSlot(const QMetaMethod &sl)
{
if (sl.access() != QMetaMethod::Private || sl.parameterCount() != 0
|| sl.returnType() != QMetaType::Void || sl.methodType() != QMetaMethod::Slot)
return false;
const QByteArray name = sl.name();
return !(name.isEmpty() || name.endsWith("_data")
|| name == "initTestCase" || name == "cleanupTestCase"
|| name == "init" || name == "cleanup");
}
最后,调用 TestMethods::invokeMethod(),首先明确检查 initTestCase
并运行它:
QTestResult::setCurrentTestFunction("initTestCase");
if (m_initTestCaseDataMethod.isValid())
m_initTestCaseDataMethod.invoke(testObject, Qt::DirectConnection);
同样,它会在最后检查 cleanupTestCase
。