Codebase list qsslcaudit / 2c6069a tests / test.h
2c6069a

Tree @2c6069a (Download .tar.gz)

test.h @2c6069araw · history · blame

#ifndef QSSLCAUDITTEST_H
#define QSSLCAUDITTEST_H

#include "debug.h"
#include "sslcaudit.h"
#include "ssltest.h"
#include "ssltestresult.h"
#include "sslusersettings.h"

#include <QThread>
#include <QTimer>

class Test : public QObject
{
    Q_OBJECT
public:
    Test(int id, QString testBaseName, QList<SslTest *>sslTests, QObject *parent = nullptr) :
        QObject(parent),
        id(id),
        testBaseName(testBaseName),
        sslTests(sslTests)
    {
        testResults.resize(sslTests.size());
        testResults.fill(-1);
        currentTestNum = 0;
    }

    ~Test() {
        sslCAuditThread.quit();
        sslCAuditThread.wait();
        caudit->deleteLater();
    }

    int getId() { return id; }

    bool isFailed() {
        for (int i = 0; i < sslTests.size(); i++) {
            if (testResults.at(i) != 0)
                return true;
        }
        return false;
    }

    int getResult() { return testResults.at(currentTestNum); }

    SslUserSettings testSettings;

    SslTest * currentSslTest() {
        return sslTests.at(currentTestNum);
    }

    const ClientInfo *currentClient() {
        return caudit->getClientInfo(currentTestNum);
    }

    // required for "recurrentRequests" test
    const ClientInfo *getClient(int testNum) {
        return caudit->getClientInfo(testNum);
    }

    int currentSslTestNum() {
        return currentTestNum;
    }

    QList<SslTest *> allSslTests() {
        return sslTests;
    }

    // used as an entry point by TestsLauncher, can be reimplemented
    virtual void startTests() {
        prepareTests();
        launchSslCAudit();
    }

    // called by Test class in prepareTests() method
    // testSettings member is expected to be filled there
    virtual void setTestsSettings() = 0;

    // this is called when the current test is ready
    // it is expected that subclass will initiate test there, i.e. creating socket and connecting to listener
    virtual void executeNextSslTest() = 0;

    // this called when the current test is finished
    // the subclass is expected to verify current test results
    virtual void verifySslTestResult() = 0;

    // creates SslCAudit instance, configures tests and applies them to this instance
    // this should be the starting call of autotest
    void prepareTests() {
        setTestsSettings();

        for (int i = 0; i < sslTests.size(); i++) {
            if (!sslTests.at(i)->prepare(&testSettings)) {
                RED("failed to prepare test " + sslTests.at(i)->name());
                return;
            }
        }

        caudit = new SslCAudit(&testSettings);

        caudit->moveToThread(&sslCAuditThread);
        sslCAuditThread.start();

        connect(caudit, &SslCAudit::sslTestReady, this, &Test::launchClientTest);
        connect(caudit, &SslCAudit::sslTestFinished, this, &Test::handleTestFinished);
        connect(caudit, &SslCAudit::sslTestsFinished, this, &Test::handleAllTestsFinished);

        caudit->setSslTests(sslTests);
    }

    // asynchronously launches SslCAudit thread. this ends up with the first test becoming ready
    // this is supposed to be called by autotest
    void launchSslCAudit() {
        QTimer::singleShot(0, caudit, &SslCAudit::run);
    }

    // print helpers
    void printTestFailed() {
        RED(QString("autotest #%1 for %2 failed").arg(getId()).arg(testName()));
    }
    void printTestFailed(const QString &details) {
        RED(QString("autotest #%1 for %2 failed: %3").arg(getId()).arg(testName()).arg(details));
    }
    void printTestSucceeded() {
        GREEN(QString("autotest #%1 for %2 succeeded").arg(getId()).arg(testName()));
    }
    QString testName() { return QString("%1_%2").arg(testBaseName).arg(id); }

    // just API wrapper, we don't want to expose caudit member
    bool isSameClient(bool doPrint) {
        return caudit->isSameClient(doPrint);
    }

protected:
    void setResult(int result) {
        testResults[currentTestNum] = result;
    }

signals:
    void autotestFinished();

private slots:
    void launchClientTest() {
        setResult(-1);
        executeNextSslTest();
    }

    void handleAllTestsFinished() {
        sslCAuditThread.quit();
        sslCAuditThread.wait();
    }

    void handleTestFinished() {
        verifySslTestResult();

        if (currentTestNum == sslTests.size()-1) {
            emit autotestFinished();
        } else {
            currentTestNum++;
        }
    }

private:
    int id;
    QString testBaseName;
    QVector<int> testResults;

    QThread sslCAuditThread;
    SslCAudit *caudit;

    int currentTestNum;
    QList<SslTest *> sslTests;

};

class TestsLauncher : public QObject
{
    Q_OBJECT

public:
    TestsLauncher(QList<Test *> sslTests, QObject *parent = nullptr) :
        QObject(parent),
        sslTests(sslTests)
    {
        retCode = 0;

        // re-parenting tests as they will belong to the main()'s thread
        for (int i = 0; i < sslTests.size(); i++) {
            sslTests.at(i)->setParent(this);
        }
    }

    ~TestsLauncher() {}

    void launchNextTest()
    {
        if (sslTests.size() > 0) {
            Test *test = sslTests.takeFirst();
            launchSingleTest(test);
        } else {
            emit autotestsFinished();
        }
    }

    void launchSingleTest(Test *autotest)
    {
        WHITE(QString("launching autotest #%1").arg(autotest->getId()));

        connect(autotest, &Test::autotestFinished, this, &TestsLauncher::handleAutotestFinished);

        autotest->startTests();
    }

    int testsResult()
    {
        return retCode;
    }

signals:
    void autotestsFinished();

private:
    void handleAutotestFinished()
    {
        Test *autotest = qobject_cast<Test *>(sender());
        if (autotest->getResult() != 0) {
            retCode = -1;
        }
        autotest->deleteLater();

        launchNextTest();
    }

    QList<Test *> sslTests;
    int retCode;

};

#endif