Je travaille sur un projet existant écrit en C++, le point d'entrée de l'application est :

QTEST_MAIN(className)

À partir de la documentation que j'ai lue, cela créera un fonction C standard main(), mais on ne sait pas du tout comment les tests d'application sont appelés ou quel est l'ordre d'appel ou comment sa configuration.

En regardant la classe dans le projet que j'ai, il n'y a pas de constructeur de classe ; la classe elle-même est dérivée de QObject. Il dispose de 23 slots privés : l'un d'entre eux s'appelle « initTestCase » ; les autres sont des tests variés se terminant tous par "Test".

Le slot "InitTestCase" contient un seul appel pour configurer les règles de filtrage de journalisation et c'est tout. Lorsque le projet est compilé et exécuté, il exécute des tests, mais je ne vois pas comment ni d'où vient la commande.

Que fait réellement la macro QTEST_MAIN dans mon programme, comment les slots sont-ils configurés et comment sait-elle quels tests exécuter ?

-1
SPlatten 30 janv. 2020 à 11:44

1 réponse

Meilleure réponse

QTEST_MAIN transmet à un QTEST_MAIN_IMPL :

#define QTEST_MAIN(TestObject) \
int main(int argc, char *argv[]) \
{ \
    QTEST_MAIN_IMPL(TestObject) \
}

Ce QTEST_MAIN_IMPL est différent selon la QApplication dont vous avez besoin (Widgets, Gui ou Core). Pour les widgets, cela ressemble à ceci :

#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 est défini qtestcase.cpp :

int QTest::qExec(QObject *testObject, int argc, char **argv)
{
    qInit(testObject, argc, argv);
    int ret = qRun();
    qCleanup();
    return ret;
}

Dans qInit(), currentTestObject est défini .

Dans qRun(), un TestMethods instance est créée, et elle son constructeur, on trouve cette boucle :

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() est implémenté ainsi :

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");
}

Enfin, TestMethods::invokeMethod() est appelé, ce qui vérifie explicitement un initTestCase en premier et l'exécute :

QTestResult::setCurrentTestFunction("initTestCase");
if (m_initTestCaseDataMethod.isValid())
    m_initTestCaseDataMethod.invoke(testObject, Qt::DirectConnection);

De même, il vérifie cleanupTestCase à la fin.

0
E_net4 says don't copy that 30 mars 2020 à 18:33