2022-10-12 14:30:24 +02:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
|
|
#include <app/app_version.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/launcherinterface.h>
|
|
|
|
|
#include <utils/qtcprocess.h>
|
|
|
|
|
#include <utils/singleton.h>
|
|
|
|
|
#include <utils/temporarydirectory.h>
|
|
|
|
|
|
|
|
|
|
#include <QtTest>
|
|
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
|
|
using namespace Utils;
|
2022-12-04 08:31:14 +01:00
|
|
|
using namespace Utils::Tasking;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
enum class Handler {
|
|
|
|
|
Setup,
|
|
|
|
|
Done,
|
|
|
|
|
Error,
|
|
|
|
|
GroupSetup,
|
|
|
|
|
GroupDone,
|
|
|
|
|
GroupError
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using Log = QList<QPair<int, Handler>>;
|
|
|
|
|
|
|
|
|
|
class tst_TaskTree : public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
|
void initTestCase();
|
|
|
|
|
|
|
|
|
|
void validConstructs(); // compile test
|
|
|
|
|
void processTree_data();
|
|
|
|
|
void processTree();
|
|
|
|
|
void storage_data();
|
|
|
|
|
void storage();
|
2022-12-04 08:31:14 +01:00
|
|
|
void storageOperators();
|
2022-11-22 15:21:45 +01:00
|
|
|
void storageDestructor();
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
void cleanupTestCase();
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Log m_log;
|
|
|
|
|
FilePath m_testAppPath;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void tst_TaskTree::initTestCase()
|
|
|
|
|
{
|
|
|
|
|
Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/"
|
|
|
|
|
+ Core::Constants::IDE_CASED_ID + "-XXXXXX");
|
|
|
|
|
const QString libExecPath(qApp->applicationDirPath() + '/'
|
|
|
|
|
+ QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
|
|
|
|
|
LauncherInterface::setPathToLauncher(libExecPath);
|
|
|
|
|
m_testAppPath = FilePath::fromString(QLatin1String(TESTAPP_PATH)
|
|
|
|
|
+ QLatin1String("/testapp")).withExecutableSuffix();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void tst_TaskTree::cleanupTestCase()
|
|
|
|
|
{
|
|
|
|
|
Utils::Singleton::deleteAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void tst_TaskTree::validConstructs()
|
|
|
|
|
{
|
|
|
|
|
const Group process {
|
|
|
|
|
parallel,
|
|
|
|
|
Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
|
|
|
|
|
Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
|
|
|
|
|
Process([](QtcProcess &) {}, [](const QtcProcess &) {})
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Group group1 {
|
|
|
|
|
process
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Group group2 {
|
|
|
|
|
parallel,
|
|
|
|
|
Group {
|
|
|
|
|
parallel,
|
|
|
|
|
Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
|
|
|
|
|
Group {
|
|
|
|
|
parallel,
|
|
|
|
|
Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
|
|
|
|
|
Group {
|
|
|
|
|
parallel,
|
|
|
|
|
Process([](QtcProcess &) {}, [](const QtcProcess &) {})
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Group {
|
|
|
|
|
parallel,
|
|
|
|
|
Process([](QtcProcess &) {}, [](const QtcProcess &) {}),
|
|
|
|
|
OnGroupDone([] {}),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
process,
|
|
|
|
|
OnGroupDone([] {}),
|
|
|
|
|
OnGroupError([] {})
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char s_processIdProperty[] = "__processId";
|
|
|
|
|
|
|
|
|
|
void tst_TaskTree::processTree_data()
|
|
|
|
|
{
|
|
|
|
|
using namespace std::placeholders;
|
|
|
|
|
|
|
|
|
|
QTest::addColumn<Group>("root");
|
|
|
|
|
QTest::addColumn<Log>("expectedLog");
|
|
|
|
|
QTest::addColumn<bool>("runningAfterStart");
|
|
|
|
|
QTest::addColumn<bool>("success");
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::addColumn<int>("taskCount");
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const auto setupProcessHelper = [this](QtcProcess &process, const QStringList &args, int processId) {
|
|
|
|
|
process.setCommand(CommandLine(m_testAppPath, args));
|
|
|
|
|
process.setProperty(s_processIdProperty, processId);
|
|
|
|
|
m_log.append({processId, Handler::Setup});
|
|
|
|
|
};
|
|
|
|
|
const auto setupProcess = [setupProcessHelper](QtcProcess &process, int processId) {
|
|
|
|
|
setupProcessHelper(process, {"-return", "0"}, processId);
|
|
|
|
|
};
|
|
|
|
|
// const auto setupErrorProcess = [setupProcessHelper](QtcProcess &process, int processId) {
|
|
|
|
|
// setupProcessHelper(process, {"-return", "1"}, processId);
|
|
|
|
|
// };
|
|
|
|
|
const auto setupCrashProcess = [setupProcessHelper](QtcProcess &process, int processId) {
|
|
|
|
|
setupProcessHelper(process, {"-crash"}, processId);
|
|
|
|
|
};
|
|
|
|
|
const auto readResultAnonymous = [this](const QtcProcess &) {
|
|
|
|
|
m_log.append({-1, Handler::Done});
|
|
|
|
|
};
|
|
|
|
|
const auto readResult = [this](const QtcProcess &process) {
|
|
|
|
|
const int processId = process.property(s_processIdProperty).toInt();
|
|
|
|
|
m_log.append({processId, Handler::Done});
|
|
|
|
|
};
|
|
|
|
|
const auto readError = [this](const QtcProcess &process) {
|
|
|
|
|
const int processId = process.property(s_processIdProperty).toInt();
|
|
|
|
|
m_log.append({processId, Handler::Error});
|
|
|
|
|
};
|
|
|
|
|
const auto groupSetup = [this](int processId) {
|
|
|
|
|
m_log.append({processId, Handler::GroupSetup});
|
|
|
|
|
};
|
|
|
|
|
const auto groupDone = [this](int processId) {
|
|
|
|
|
m_log.append({processId, Handler::GroupDone});
|
|
|
|
|
};
|
|
|
|
|
const auto rootDone = [this] {
|
|
|
|
|
m_log.append({-1, Handler::GroupDone});
|
|
|
|
|
};
|
|
|
|
|
const auto rootError = [this] {
|
|
|
|
|
m_log.append({-1, Handler::GroupError});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Group emptyRoot {
|
|
|
|
|
OnGroupDone(rootDone)
|
|
|
|
|
};
|
|
|
|
|
const Log emptyLog{{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("Empty") << emptyRoot << emptyLog << false << true << 0;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group nestedRoot {
|
|
|
|
|
Group {
|
|
|
|
|
Group {
|
|
|
|
|
Group {
|
|
|
|
|
Group {
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 5), readResult),
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 5)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 5))
|
|
|
|
|
},
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 4)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 4))
|
|
|
|
|
},
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 3)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 3))
|
|
|
|
|
},
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 2)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 2))
|
|
|
|
|
},
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 1)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 1))
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(rootDone)
|
|
|
|
|
};
|
|
|
|
|
const Log nestedLog{{1, Handler::GroupSetup},
|
|
|
|
|
{2, Handler::GroupSetup},
|
|
|
|
|
{3, Handler::GroupSetup},
|
|
|
|
|
{4, Handler::GroupSetup},
|
|
|
|
|
{5, Handler::GroupSetup},
|
|
|
|
|
{5, Handler::Setup},
|
|
|
|
|
{5, Handler::Done},
|
|
|
|
|
{5, Handler::GroupDone},
|
|
|
|
|
{4, Handler::GroupDone},
|
|
|
|
|
{3, Handler::GroupDone},
|
|
|
|
|
{2, Handler::GroupDone},
|
|
|
|
|
{1, Handler::GroupDone},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("Nested") << nestedRoot << nestedLog << true << true << 1;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group parallelRoot {
|
|
|
|
|
parallel,
|
|
|
|
|
Process(std::bind(setupProcess, _1, 1), readResultAnonymous),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 2), readResultAnonymous),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 3), readResultAnonymous),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 4), readResultAnonymous),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 5), readResultAnonymous),
|
|
|
|
|
OnGroupDone(rootDone)
|
|
|
|
|
};
|
|
|
|
|
const Log parallelLog{{1, Handler::Setup}, // Setup order is determined in parallel mode
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{3, Handler::Setup},
|
|
|
|
|
{4, Handler::Setup},
|
|
|
|
|
{5, Handler::Setup},
|
|
|
|
|
{-1, Handler::Done}, // Done order isn't determined in parallel mode
|
|
|
|
|
{-1, Handler::Done},
|
|
|
|
|
{-1, Handler::Done},
|
|
|
|
|
{-1, Handler::Done},
|
|
|
|
|
{-1, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupDone}}; // Done handlers may come in different order
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("Parallel") << parallelRoot << parallelLog << true << true << 5;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group sequentialRoot {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 1), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 2), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 3), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 4), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 5), readResult),
|
|
|
|
|
OnGroupDone(rootDone)
|
|
|
|
|
};
|
|
|
|
|
const Group sequentialEncapsulatedRoot {
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 1), readResult)
|
|
|
|
|
},
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 2), readResult)
|
|
|
|
|
},
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 3), readResult)
|
|
|
|
|
},
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 4), readResult)
|
|
|
|
|
},
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 5), readResult)
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(rootDone)
|
|
|
|
|
};
|
2022-11-16 09:06:32 +01:00
|
|
|
auto setupSubTree = [=](TaskTree &taskTree) {
|
|
|
|
|
const Group nestedRoot {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 2), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 3), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 4), readResult),
|
|
|
|
|
};
|
|
|
|
|
taskTree.setupRoot(nestedRoot);
|
|
|
|
|
};
|
|
|
|
|
const Group sequentialSubTreeRoot {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 1), readResult),
|
|
|
|
|
Tree(setupSubTree),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 5), readResult),
|
|
|
|
|
OnGroupDone(rootDone)
|
|
|
|
|
};
|
2022-10-12 14:30:24 +02:00
|
|
|
const Log sequentialLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Done},
|
|
|
|
|
{3, Handler::Setup},
|
|
|
|
|
{3, Handler::Done},
|
|
|
|
|
{4, Handler::Setup},
|
|
|
|
|
{4, Handler::Done},
|
|
|
|
|
{5, Handler::Setup},
|
|
|
|
|
{5, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("Sequential") << sequentialRoot << sequentialLog << true << true << 5;
|
|
|
|
|
QTest::newRow("SequentialEncapsulated") << sequentialEncapsulatedRoot << sequentialLog
|
|
|
|
|
<< true << true << 5;
|
2022-11-16 09:06:32 +01:00
|
|
|
QTest::newRow("SequentialSubTree") << sequentialSubTreeRoot << sequentialLog
|
|
|
|
|
<< true << true << 3; // We don't inspect subtrees
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group sequentialNestedRoot {
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 1), readResult),
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 2), readResult),
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 3), readResult),
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 4), readResult),
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 5), readResult),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 5))
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 4))
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 3))
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 2))
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 1))
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(rootDone)
|
|
|
|
|
};
|
|
|
|
|
const Log sequentialNestedLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Done},
|
|
|
|
|
{3, Handler::Setup},
|
|
|
|
|
{3, Handler::Done},
|
|
|
|
|
{4, Handler::Setup},
|
|
|
|
|
{4, Handler::Done},
|
|
|
|
|
{5, Handler::Setup},
|
|
|
|
|
{5, Handler::Done},
|
|
|
|
|
{5, Handler::GroupDone},
|
|
|
|
|
{4, Handler::GroupDone},
|
|
|
|
|
{3, Handler::GroupDone},
|
|
|
|
|
{2, Handler::GroupDone},
|
|
|
|
|
{1, Handler::GroupDone},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("SequentialNested") << sequentialNestedRoot << sequentialNestedLog
|
|
|
|
|
<< true << true << 5;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group sequentialErrorRoot {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 1), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 2), readResult),
|
|
|
|
|
Process(std::bind(setupCrashProcess, _1, 3), readResult, readError),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 4), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 5), readResult),
|
|
|
|
|
OnGroupDone(rootDone),
|
|
|
|
|
OnGroupError(rootError)
|
|
|
|
|
};
|
|
|
|
|
const Log sequentialErrorLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Done},
|
|
|
|
|
{3, Handler::Setup},
|
|
|
|
|
{3, Handler::Error},
|
|
|
|
|
{-1, Handler::GroupError}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("SequentialError") << sequentialErrorRoot << sequentialErrorLog
|
|
|
|
|
<< true << false << 5;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2022-11-11 10:37:02 +01:00
|
|
|
const auto constructSimpleSequence = [=](const Workflow &policy) {
|
2022-10-12 14:30:24 +02:00
|
|
|
return Group {
|
|
|
|
|
policy,
|
|
|
|
|
Process(std::bind(setupProcess, _1, 1), readResult),
|
|
|
|
|
Process(std::bind(setupCrashProcess, _1, 2), readResult, readError),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 3), readResult),
|
|
|
|
|
OnGroupDone(rootDone),
|
|
|
|
|
OnGroupError(rootError)
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Group stopOnErrorRoot = constructSimpleSequence(stopOnError);
|
|
|
|
|
const Log stopOnErrorLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Error},
|
|
|
|
|
{-1, Handler::GroupError}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("StopOnError") << stopOnErrorRoot << stopOnErrorLog << true << false << 3;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group continueOnErrorRoot = constructSimpleSequence(continueOnError);
|
|
|
|
|
const Log continueOnErrorLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Error},
|
|
|
|
|
{3, Handler::Setup},
|
|
|
|
|
{3, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupError}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("ContinueOnError") << continueOnErrorRoot << continueOnErrorLog
|
|
|
|
|
<< true << false << 3;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group stopOnDoneRoot = constructSimpleSequence(stopOnDone);
|
|
|
|
|
const Log stopOnDoneLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("StopOnDone") << stopOnDoneRoot << stopOnDoneLog
|
|
|
|
|
<< true << true << 3;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group continueOnDoneRoot = constructSimpleSequence(continueOnDone);
|
|
|
|
|
const Log continueOnDoneLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Error},
|
|
|
|
|
{3, Handler::Setup},
|
|
|
|
|
{3, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("ContinueOnDone") << continueOnDoneRoot << continueOnDoneLog << true << true << 3;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const Group optionalRoot {
|
|
|
|
|
optional,
|
|
|
|
|
Process(std::bind(setupCrashProcess, _1, 1), readResult, readError),
|
|
|
|
|
Process(std::bind(setupCrashProcess, _1, 2), readResult, readError),
|
|
|
|
|
OnGroupDone(rootDone),
|
|
|
|
|
OnGroupError(rootError)
|
|
|
|
|
};
|
|
|
|
|
const Log optionalLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Error},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Error},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("Optional") << optionalRoot << optionalLog << true << true << 2;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2022-11-04 14:08:32 +01:00
|
|
|
const auto stopWithDoneSetup = [] { return GroupConfig{GroupAction::StopWithDone}; };
|
|
|
|
|
const auto stopWithErrorSetup = [] { return GroupConfig{GroupAction::StopWithError}; };
|
|
|
|
|
const auto continueAllSetup = [] { return GroupConfig{GroupAction::ContinueAll}; };
|
|
|
|
|
const auto continueSelSetup = [] { return GroupConfig{GroupAction::ContinueSelected, {0, 2}}; };
|
|
|
|
|
const auto constructDynamicSetup = [=](const DynamicSetup &dynamicSetup) {
|
|
|
|
|
return Group {
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 1), readResult)
|
|
|
|
|
},
|
|
|
|
|
Group {
|
|
|
|
|
dynamicSetup,
|
|
|
|
|
Process(std::bind(setupProcess, _1, 2), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 3), readResult),
|
|
|
|
|
Process(std::bind(setupProcess, _1, 4), readResult)
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(rootDone),
|
|
|
|
|
OnGroupError(rootError)
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
const Group dynamicSetupDoneRoot = constructDynamicSetup({stopWithDoneSetup});
|
|
|
|
|
const Log dynamicSetupDoneLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("DynamicSetupDone") << dynamicSetupDoneRoot << dynamicSetupDoneLog
|
|
|
|
|
<< true << true << 4;
|
|
|
|
|
|
2022-11-04 14:08:32 +01:00
|
|
|
const Group dynamicSetupErrorRoot = constructDynamicSetup({stopWithErrorSetup});
|
|
|
|
|
const Log dynamicSetupErrorLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupError}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("DynamicSetupError") << dynamicSetupErrorRoot << dynamicSetupErrorLog
|
|
|
|
|
<< true << false << 4;
|
|
|
|
|
|
2022-11-04 14:08:32 +01:00
|
|
|
const Group dynamicSetupAllRoot = constructDynamicSetup({continueAllSetup});
|
|
|
|
|
const Log dynamicSetupAllLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Done},
|
|
|
|
|
{3, Handler::Setup},
|
|
|
|
|
{3, Handler::Done},
|
|
|
|
|
{4, Handler::Setup},
|
|
|
|
|
{4, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("DynamicSetupAll") << dynamicSetupAllRoot << dynamicSetupAllLog
|
|
|
|
|
<< true << true << 4;
|
|
|
|
|
|
2022-11-04 14:08:32 +01:00
|
|
|
const Group dynamicSetupSelRoot = constructDynamicSetup({continueSelSetup});
|
|
|
|
|
const Log dynamicSetupSelLog{{1, Handler::Setup},
|
|
|
|
|
{1, Handler::Done},
|
|
|
|
|
{2, Handler::Setup},
|
|
|
|
|
{2, Handler::Done},
|
|
|
|
|
{4, Handler::Setup},
|
|
|
|
|
{4, Handler::Done},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
2022-11-10 14:31:25 +01:00
|
|
|
QTest::newRow("DynamicSetupSelected") << dynamicSetupSelRoot << dynamicSetupSelLog
|
|
|
|
|
<< true << true << 4;
|
2022-11-16 09:06:32 +01:00
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void tst_TaskTree::processTree()
|
|
|
|
|
{
|
|
|
|
|
m_log = {};
|
|
|
|
|
|
|
|
|
|
QFETCH(Group, root);
|
|
|
|
|
QFETCH(Log, expectedLog);
|
|
|
|
|
QFETCH(bool, runningAfterStart);
|
|
|
|
|
QFETCH(bool, success);
|
2022-11-10 14:31:25 +01:00
|
|
|
QFETCH(int, taskCount);
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
QEventLoop eventLoop;
|
|
|
|
|
TaskTree processTree(root);
|
2022-11-10 14:31:25 +01:00
|
|
|
QCOMPARE(processTree.taskCount(), taskCount);
|
2022-10-12 14:30:24 +02:00
|
|
|
int doneCount = 0;
|
|
|
|
|
int errorCount = 0;
|
|
|
|
|
connect(&processTree, &TaskTree::done, this, [&doneCount, &eventLoop] { ++doneCount; eventLoop.quit(); });
|
|
|
|
|
connect(&processTree, &TaskTree::errorOccurred, this, [&errorCount, &eventLoop] { ++errorCount; eventLoop.quit(); });
|
|
|
|
|
processTree.start();
|
|
|
|
|
QCOMPARE(processTree.isRunning(), runningAfterStart);
|
|
|
|
|
|
2022-11-25 12:35:07 +01:00
|
|
|
if (runningAfterStart) {
|
|
|
|
|
QTimer timer;
|
|
|
|
|
bool timedOut = false;
|
|
|
|
|
connect(&timer, &QTimer::timeout, &eventLoop, [&eventLoop, &timedOut] {
|
|
|
|
|
timedOut = true;
|
|
|
|
|
eventLoop.quit();
|
|
|
|
|
});
|
2022-12-01 15:50:03 +01:00
|
|
|
timer.setInterval(2000);
|
2022-11-25 12:35:07 +01:00
|
|
|
timer.setSingleShot(true);
|
|
|
|
|
timer.start();
|
|
|
|
|
eventLoop.exec();
|
|
|
|
|
QCOMPARE(timedOut, false);
|
|
|
|
|
QCOMPARE(processTree.isRunning(), false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-10 15:59:54 +01:00
|
|
|
QCOMPARE(processTree.progressValue(), taskCount);
|
2022-10-12 14:30:24 +02:00
|
|
|
QCOMPARE(m_log, expectedLog);
|
|
|
|
|
|
|
|
|
|
const int expectedDoneCount = success ? 1 : 0;
|
|
|
|
|
const int expectedErrorCount = success ? 0 : 1;
|
|
|
|
|
QCOMPARE(doneCount, expectedDoneCount);
|
|
|
|
|
QCOMPARE(errorCount, expectedErrorCount);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
struct CustomStorage
|
|
|
|
|
{
|
|
|
|
|
CustomStorage() { ++s_count; }
|
|
|
|
|
~CustomStorage() { --s_count; }
|
|
|
|
|
Log m_log;
|
|
|
|
|
static int instanceCount() { return s_count; }
|
|
|
|
|
private:
|
|
|
|
|
static int s_count;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int CustomStorage::s_count = 0;
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
void tst_TaskTree::storage_data()
|
|
|
|
|
{
|
|
|
|
|
using namespace std::placeholders;
|
|
|
|
|
|
|
|
|
|
QTest::addColumn<Group>("root");
|
2022-12-05 11:20:57 +01:00
|
|
|
QTest::addColumn<TreeStorage<CustomStorage>>("storage");
|
2022-10-12 14:30:24 +02:00
|
|
|
QTest::addColumn<Log>("expectedLog");
|
|
|
|
|
QTest::addColumn<bool>("runningAfterStart");
|
|
|
|
|
QTest::addColumn<bool>("success");
|
|
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
TreeStorage<CustomStorage> storageLog;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
const auto setupProcessHelper = [storageLog, testAppPath = m_testAppPath]
|
|
|
|
|
(QtcProcess &process, const QStringList &args, int processId) {
|
|
|
|
|
process.setCommand(CommandLine(testAppPath, args));
|
2022-10-12 14:30:24 +02:00
|
|
|
process.setProperty(s_processIdProperty, processId);
|
2022-11-21 15:05:39 +01:00
|
|
|
storageLog->m_log.append({processId, Handler::Setup});
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
const auto setupProcess = [setupProcessHelper](QtcProcess &process, int processId) {
|
|
|
|
|
setupProcessHelper(process, {"-return", "0"}, processId);
|
|
|
|
|
};
|
2022-11-21 15:05:39 +01:00
|
|
|
const auto readResult = [storageLog](const QtcProcess &process) {
|
2022-10-12 14:30:24 +02:00
|
|
|
const int processId = process.property(s_processIdProperty).toInt();
|
2022-11-21 15:05:39 +01:00
|
|
|
storageLog->m_log.append({processId, Handler::Done});
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
2022-11-21 15:05:39 +01:00
|
|
|
const auto groupSetup = [storageLog](int processId) {
|
|
|
|
|
storageLog->m_log.append({processId, Handler::GroupSetup});
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
2022-11-21 15:05:39 +01:00
|
|
|
const auto groupDone = [storageLog](int processId) {
|
|
|
|
|
storageLog->m_log.append({processId, Handler::GroupDone});
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
2022-11-21 15:05:39 +01:00
|
|
|
const auto rootDone = [storageLog] {
|
|
|
|
|
storageLog->m_log.append({-1, Handler::GroupDone});
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
const Log expectedLog{{1, Handler::GroupSetup},
|
|
|
|
|
{2, Handler::GroupSetup},
|
|
|
|
|
{3, Handler::GroupSetup},
|
|
|
|
|
{4, Handler::GroupSetup},
|
|
|
|
|
{5, Handler::GroupSetup},
|
|
|
|
|
{5, Handler::Setup},
|
|
|
|
|
{5, Handler::Done},
|
|
|
|
|
{5, Handler::GroupDone},
|
|
|
|
|
{4, Handler::GroupDone},
|
|
|
|
|
{3, Handler::GroupDone},
|
|
|
|
|
{2, Handler::GroupDone},
|
|
|
|
|
{1, Handler::GroupDone},
|
|
|
|
|
{-1, Handler::GroupDone}};
|
|
|
|
|
|
|
|
|
|
const Group root {
|
|
|
|
|
Storage(storageLog),
|
2022-10-12 14:30:24 +02:00
|
|
|
Group {
|
|
|
|
|
Group {
|
|
|
|
|
Group {
|
|
|
|
|
Group {
|
|
|
|
|
Group {
|
|
|
|
|
Process(std::bind(setupProcess, _1, 5), readResult),
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 5)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 5))
|
|
|
|
|
},
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 4)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 4))
|
|
|
|
|
},
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 3)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 3))
|
|
|
|
|
},
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 2)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 2))
|
|
|
|
|
},
|
|
|
|
|
OnGroupSetup(std::bind(groupSetup, 1)),
|
|
|
|
|
OnGroupDone(std::bind(groupDone, 1))
|
|
|
|
|
},
|
|
|
|
|
OnGroupDone(rootDone)
|
|
|
|
|
};
|
|
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
QTest::newRow("Storage") << root << storageLog << expectedLog << true << true;
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void tst_TaskTree::storage()
|
|
|
|
|
{
|
|
|
|
|
QFETCH(Group, root);
|
2022-12-05 11:20:57 +01:00
|
|
|
QFETCH(TreeStorage<CustomStorage>, storage);
|
2022-10-12 14:30:24 +02:00
|
|
|
QFETCH(Log, expectedLog);
|
|
|
|
|
QFETCH(bool, runningAfterStart);
|
|
|
|
|
QFETCH(bool, success);
|
|
|
|
|
|
2022-12-05 11:20:57 +01:00
|
|
|
QVERIFY(storage.isValid());
|
2022-11-21 15:05:39 +01:00
|
|
|
QCOMPARE(CustomStorage::instanceCount(), 0);
|
|
|
|
|
|
2022-12-05 11:20:57 +01:00
|
|
|
Log actualLog;
|
2022-10-12 14:30:24 +02:00
|
|
|
QEventLoop eventLoop;
|
|
|
|
|
TaskTree processTree(root);
|
2022-12-05 11:20:57 +01:00
|
|
|
auto collectLog = [&actualLog](CustomStorage *storage){
|
|
|
|
|
actualLog = storage->m_log;
|
|
|
|
|
};
|
|
|
|
|
processTree.onStorageDone(storage, collectLog);
|
2022-10-12 14:30:24 +02:00
|
|
|
int doneCount = 0;
|
|
|
|
|
int errorCount = 0;
|
|
|
|
|
connect(&processTree, &TaskTree::done, this, [&doneCount, &eventLoop] { ++doneCount; eventLoop.quit(); });
|
|
|
|
|
connect(&processTree, &TaskTree::errorOccurred, this, [&errorCount, &eventLoop] { ++errorCount; eventLoop.quit(); });
|
|
|
|
|
processTree.start();
|
2022-11-21 15:05:39 +01:00
|
|
|
QCOMPARE(CustomStorage::instanceCount(), 1);
|
2022-10-12 14:30:24 +02:00
|
|
|
QCOMPARE(processTree.isRunning(), runningAfterStart);
|
|
|
|
|
|
|
|
|
|
QTimer timer;
|
|
|
|
|
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
|
2022-12-01 15:50:03 +01:00
|
|
|
timer.setInterval(2000);
|
2022-10-12 14:30:24 +02:00
|
|
|
timer.setSingleShot(true);
|
|
|
|
|
timer.start();
|
|
|
|
|
eventLoop.exec();
|
|
|
|
|
|
|
|
|
|
QVERIFY(!processTree.isRunning());
|
2022-12-05 11:20:57 +01:00
|
|
|
QCOMPARE(actualLog, expectedLog);
|
2022-11-21 15:05:39 +01:00
|
|
|
QCOMPARE(CustomStorage::instanceCount(), 0);
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
const int expectedDoneCount = success ? 1 : 0;
|
|
|
|
|
const int expectedErrorCount = success ? 0 : 1;
|
|
|
|
|
QCOMPARE(doneCount, expectedDoneCount);
|
|
|
|
|
QCOMPARE(errorCount, expectedErrorCount);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 08:31:14 +01:00
|
|
|
void tst_TaskTree::storageOperators()
|
2022-11-22 15:21:45 +01:00
|
|
|
{
|
2022-12-04 08:31:14 +01:00
|
|
|
TreeStorageBase storage1 = TreeStorage<CustomStorage>();
|
|
|
|
|
TreeStorageBase storage2 = TreeStorage<CustomStorage>();
|
|
|
|
|
TreeStorageBase storage3 = storage1;
|
2022-11-22 15:21:45 +01:00
|
|
|
|
2022-12-04 08:31:14 +01:00
|
|
|
QVERIFY(storage1 == storage3);
|
|
|
|
|
QVERIFY(storage1 != storage2);
|
|
|
|
|
QVERIFY(storage2 != storage3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void tst_TaskTree::storageDestructor()
|
|
|
|
|
{
|
2022-11-22 15:21:45 +01:00
|
|
|
QCOMPARE(CustomStorage::instanceCount(), 0);
|
|
|
|
|
{
|
|
|
|
|
const auto setupProcess = [this](QtcProcess &process) {
|
|
|
|
|
process.setCommand(CommandLine(m_testAppPath, {"-sleep", "1"}));
|
|
|
|
|
};
|
|
|
|
|
const Group root {
|
|
|
|
|
Storage(TreeStorage<CustomStorage>()),
|
|
|
|
|
Process(setupProcess)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TaskTree processTree(root);
|
|
|
|
|
QCOMPARE(CustomStorage::instanceCount(), 0);
|
|
|
|
|
processTree.start();
|
|
|
|
|
QCOMPARE(CustomStorage::instanceCount(), 1);
|
|
|
|
|
}
|
|
|
|
|
QCOMPARE(CustomStorage::instanceCount(), 0);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
QTEST_GUILESS_MAIN(tst_TaskTree)
|
|
|
|
|
|
|
|
|
|
#include "tst_tasktree.moc"
|