diff --git a/src/libs/utils/tasktree.cpp b/src/libs/utils/tasktree.cpp index a7051cf78e4..528127a97cf 100644 --- a/src/libs/utils/tasktree.cpp +++ b/src/libs/utils/tasktree.cpp @@ -45,21 +45,26 @@ void TaskItem::addChildren(const QList &children) case Type::GroupHandler: QTC_ASSERT(m_type == Type::Group, qWarning("Group Handler may only be a " "child of Group, skipping..."); break); - QTC_ASSERT(!child.m_groupHandler.m_simpleSetupHandler - || !m_groupHandler.m_simpleSetupHandler, + QTC_ASSERT(!child.m_groupHandler.m_setupHandler + || !m_groupHandler.m_setupHandler, qWarning("Group Setup Handler redefinition, overriding...")); - QTC_ASSERT(!child.m_groupHandler.m_simpleDoneHandler - || !m_groupHandler.m_simpleDoneHandler, + QTC_ASSERT(!child.m_groupHandler.m_doneHandler + || !m_groupHandler.m_doneHandler, qWarning("Group Done Handler redefinition, overriding...")); - QTC_ASSERT(!child.m_groupHandler.m_simpleErrorHandler - || !m_groupHandler.m_simpleErrorHandler, + QTC_ASSERT(!child.m_groupHandler.m_errorHandler + || !m_groupHandler.m_errorHandler, qWarning("Group Error Handler redefinition, overriding...")); - if (child.m_groupHandler.m_simpleSetupHandler) - m_groupHandler.m_simpleSetupHandler = child.m_groupHandler.m_simpleSetupHandler; - if (child.m_groupHandler.m_simpleDoneHandler) - m_groupHandler.m_simpleDoneHandler = child.m_groupHandler.m_simpleDoneHandler; - if (child.m_groupHandler.m_simpleErrorHandler) - m_groupHandler.m_simpleErrorHandler = child.m_groupHandler.m_simpleErrorHandler; + QTC_ASSERT(!child.m_groupHandler.m_dynamicSetupHandler + || !m_groupHandler.m_dynamicSetupHandler, + qWarning("Dynamic Setup Handler redefinition, overriding...")); + if (child.m_groupHandler.m_setupHandler) + m_groupHandler.m_setupHandler = child.m_groupHandler.m_setupHandler; + if (child.m_groupHandler.m_doneHandler) + m_groupHandler.m_doneHandler = child.m_groupHandler.m_doneHandler; + if (child.m_groupHandler.m_errorHandler) + m_groupHandler.m_errorHandler = child.m_groupHandler.m_errorHandler; + if (child.m_groupHandler.m_dynamicSetupHandler) + m_groupHandler.m_dynamicSetupHandler = child.m_groupHandler.m_dynamicSetupHandler; break; } } @@ -79,10 +84,11 @@ public: const TaskItem &task); ~TaskContainer(); void start(); + void selectChildren(); void stop(); bool isRunning() const; void childDone(bool success); - void invokeSubTreeHandler(bool success); + void invokeEndHandler(bool success); void resetSuccessBit(); void updateSuccessBit(bool success); @@ -91,7 +97,9 @@ public: const TaskItem::ExecuteMode m_executeMode = TaskItem::ExecuteMode::Parallel; TaskItem::WorkflowPolicy m_workflowPolicy = TaskItem::WorkflowPolicy::StopOnError; const TaskItem::GroupHandler m_groupHandler; + GroupConfig m_groupConfig; QList m_children; + QList m_selectedChildren; int m_currentIndex = -1; bool m_successBit = true; }; @@ -157,13 +165,29 @@ TaskContainer::~TaskContainer() void TaskContainer::start() { - if (m_groupHandler.m_simpleSetupHandler) { + m_groupConfig = {}; + m_selectedChildren.clear(); + + if (m_groupHandler.m_setupHandler) { GuardLocker locker(m_taskTreePrivate->m_guard); - m_groupHandler.m_simpleSetupHandler(); + m_groupHandler.m_setupHandler(); } - if (m_children.isEmpty()) { - invokeSubTreeHandler(true); + if (m_groupHandler.m_dynamicSetupHandler) { + GuardLocker locker(m_taskTreePrivate->m_guard); + m_groupConfig = m_groupHandler.m_dynamicSetupHandler(); + } + + if (m_groupConfig.action == GroupAction::StopWithDone || m_groupConfig.action == GroupAction::StopWithError) { + const bool success = m_groupConfig.action == GroupAction::StopWithDone; + invokeEndHandler(success); + return; + } + + selectChildren(); + + if (m_selectedChildren.isEmpty()) { + invokeEndHandler(true); return; } @@ -171,21 +195,34 @@ void TaskContainer::start() resetSuccessBit(); if (m_executeMode == TaskItem::ExecuteMode::Sequential) { - m_children.at(m_currentIndex)->start(); + m_selectedChildren.at(m_currentIndex)->start(); return; } // Parallel case - for (TaskNode *child : std::as_const(m_children)) { + for (TaskNode *child : std::as_const(m_selectedChildren)) { if (!child->start()) return; } } +void TaskContainer::selectChildren() +{ + if (m_groupConfig.action != GroupAction::ContinueSelected) { + m_selectedChildren = m_children; + return; + } + m_selectedChildren.clear(); + for (int i = 0; i < m_children.size(); ++i) { + if (m_groupConfig.childrenToRun.contains(i)) + m_selectedChildren.append(m_children.at(i)); + } +} + void TaskContainer::stop() { m_currentIndex = -1; - for (TaskNode *child : std::as_const(m_children)) + for (TaskNode *child : std::as_const(m_selectedChildren)) child->stop(); } @@ -199,32 +236,32 @@ void TaskContainer::childDone(bool success) if ((m_workflowPolicy == TaskItem::WorkflowPolicy::StopOnDone && success) || (m_workflowPolicy == TaskItem::WorkflowPolicy::StopOnError && !success)) { stop(); - invokeSubTreeHandler(success); + invokeEndHandler(success); return; } ++m_currentIndex; updateSuccessBit(success); - if (m_currentIndex == m_children.size()) { - invokeSubTreeHandler(m_successBit); + if (m_currentIndex == m_selectedChildren.size()) { + invokeEndHandler(m_successBit); return; } if (m_executeMode == TaskItem::ExecuteMode::Sequential) - m_children.at(m_currentIndex)->start(); + m_selectedChildren.at(m_currentIndex)->start(); } -void TaskContainer::invokeSubTreeHandler(bool success) +void TaskContainer::invokeEndHandler(bool success) { m_currentIndex = -1; m_successBit = success; - if (success && m_groupHandler.m_simpleDoneHandler) { + if (success && m_groupHandler.m_doneHandler) { GuardLocker locker(m_taskTreePrivate->m_guard); - m_groupHandler.m_simpleDoneHandler(); - } else if (!success && m_groupHandler.m_simpleErrorHandler) { + m_groupHandler.m_doneHandler(); + } else if (!success && m_groupHandler.m_errorHandler) { GuardLocker locker(m_taskTreePrivate->m_guard); - m_groupHandler.m_simpleErrorHandler(); + m_groupHandler.m_errorHandler(); } if (m_parentContainer) { m_parentContainer->childDone(success); @@ -238,7 +275,7 @@ void TaskContainer::invokeSubTreeHandler(bool success) void TaskContainer::resetSuccessBit() { - if (m_children.isEmpty()) + if (m_selectedChildren.isEmpty()) m_successBit = true; if (m_workflowPolicy == TaskItem::WorkflowPolicy::StopOnDone @@ -261,7 +298,6 @@ void TaskContainer::updateSuccessBit(bool success) } } - bool TaskNode::start() { if (!m_taskHandler.m_createHandler || !m_taskHandler.m_setupHandler) { diff --git a/src/libs/utils/tasktree.h b/src/libs/utils/tasktree.h index 5ead17ec2da..20bcce0725a 100644 --- a/src/libs/utils/tasktree.h +++ b/src/libs/utils/tasktree.h @@ -6,6 +6,7 @@ #include "utils_global.h" #include +#include namespace Utils { namespace Tasking { @@ -22,6 +23,21 @@ signals: void done(bool success); }; +enum class GroupAction +{ + ContinueAll, + ContinueSelected, + StopWithDone, + StopWithError +}; + +class GroupConfig +{ +public: + GroupAction action = GroupAction::ContinueAll; + QSet childrenToRun = {}; +}; + class QTCREATOR_UTILS_EXPORT TaskItem { public: @@ -31,8 +47,12 @@ public: using TaskSetupHandler = std::function; // Called on task done / error using TaskEndHandler = std::function; - // Called when sub tree entered / after sub three ended with success or failure + // Called when group entered / after group ended with success or failure using GroupSimpleHandler = std::function; + // Called when group entered + using GroupSetupHandler = std::function; + // Called after group ended with success or failure, passed set of successful children +// using GroupEndHandler = std::function &)>; struct TaskHandler { TaskCreateHandler m_createHandler; @@ -42,9 +62,10 @@ public: }; struct GroupHandler { - GroupSimpleHandler m_simpleSetupHandler; - GroupSimpleHandler m_simpleDoneHandler; - GroupSimpleHandler m_simpleErrorHandler; + GroupSimpleHandler m_setupHandler; + GroupSimpleHandler m_doneHandler = {}; + GroupSimpleHandler m_errorHandler = {}; + GroupSetupHandler m_dynamicSetupHandler = {}; }; enum class ExecuteMode { @@ -136,13 +157,13 @@ public: class QTCREATOR_UTILS_EXPORT OnGroupSetup : public TaskItem { public: - OnGroupSetup(const GroupSimpleHandler &handler) : TaskItem({{handler}, {}, {}}) {} + OnGroupSetup(const GroupSimpleHandler &handler) : TaskItem({handler}) {} }; class QTCREATOR_UTILS_EXPORT OnGroupDone : public TaskItem { public: - OnGroupDone(const GroupSimpleHandler &handler) : TaskItem({{}, handler, {}}) {} + OnGroupDone(const GroupSimpleHandler &handler) : TaskItem({{}, handler}) {} }; class QTCREATOR_UTILS_EXPORT OnGroupError : public TaskItem @@ -151,6 +172,13 @@ public: OnGroupError(const GroupSimpleHandler &handler) : TaskItem({{}, {}, handler}) {} }; +class QTCREATOR_UTILS_EXPORT DynamicSetup : public TaskItem +{ +public: + DynamicSetup(const GroupSetupHandler &handler) : TaskItem({{}, {}, {}, handler}) {} +}; + + QTCREATOR_UTILS_EXPORT extern ExecuteInSequence sequential; QTCREATOR_UTILS_EXPORT extern ExecuteInParallel parallel; QTCREATOR_UTILS_EXPORT extern WorkflowPolicy stopOnError; diff --git a/tests/auto/utils/tasktree/tst_tasktree.cpp b/tests/auto/utils/tasktree/tst_tasktree.cpp index 53854ece041..a5c3a30facf 100644 --- a/tests/auto/utils/tasktree/tst_tasktree.cpp +++ b/tests/auto/utils/tasktree/tst_tasktree.cpp @@ -373,6 +373,52 @@ void tst_TaskTree::processTree_data() {2, Handler::Error}, {-1, Handler::GroupDone}}; + 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}}; + const Group dynamicSetupErrorRoot = constructDynamicSetup({stopWithErrorSetup}); + const Log dynamicSetupErrorLog{{1, Handler::Setup}, + {1, Handler::Done}, + {-1, Handler::GroupError}}; + 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}}; + 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}}; + QTest::newRow("Empty") << emptyRoot << emptyLog << false << true; QTest::newRow("Nested") << nestedRoot << nestedLog << true << true; QTest::newRow("Parallel") << parallelRoot << parallelLog << true << true; @@ -385,6 +431,10 @@ void tst_TaskTree::processTree_data() QTest::newRow("StopOnDone") << stopOnDoneRoot << stopOnDoneLog << true << true; QTest::newRow("ContinueOnDone") << continueOnDoneRoot << continueOnDoneLog << true << true; QTest::newRow("Optional") << optionalRoot << optionalLog << true << true; + QTest::newRow("DynamicSetupDone") << dynamicSetupDoneRoot << dynamicSetupDoneLog << true << true; + QTest::newRow("DynamicSetupError") << dynamicSetupErrorRoot << dynamicSetupErrorLog << true << false; + QTest::newRow("DynamicSetupAll") << dynamicSetupAllRoot << dynamicSetupAllLog << true << true; + QTest::newRow("DynamicSetupSelected") << dynamicSetupSelRoot << dynamicSetupSelLog << true << true; } void tst_TaskTree::processTree()