TaskTree: Implement simple progress reporting

The progressMaximum is the number of tasks in tree.
The progressValueChanged signal is emitted whenever task is done
(advance by one) or when some number of tasks were skipped or stopped
due to workflow policy or dynamic setup group handler.

Change-Id: I403059a6466ae0534ef647c3c1c61c0318f10325
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2022-11-10 15:59:54 +01:00
parent e38db06203
commit a4b7e10861
3 changed files with 93 additions and 9 deletions

View File

@@ -84,12 +84,12 @@ public:
const TaskItem &task); const TaskItem &task);
~TaskContainer(); ~TaskContainer();
void start(); void start();
void selectChildren(); int selectChildren(); // returns the skipped child count
void stop(); void stop();
bool isRunning() const; bool isRunning() const;
int taskCount() const; int taskCount() const;
void childDone(bool success); void childDone(bool success);
void invokeEndHandler(bool success); void invokeEndHandler(bool success, bool propagateToParent = true);
void resetSuccessBit(); void resetSuccessBit();
void updateSuccessBit(bool success); void updateSuccessBit(bool success);
@@ -135,11 +135,46 @@ public:
: q(taskTree) : q(taskTree)
, m_root(this, nullptr, root) {} , m_root(this, nullptr, root) {}
void start() {
m_progressValue = 0;
emitStartedAndProgress();
m_root.start();
}
void stop() {
if (!m_root.isRunning())
return;
// TODO: should we have canceled flag (passed to handler)?
// Just one done handler with result flag:
// FinishedWithSuccess, FinishedWithError, Canceled, TimedOut.
// Canceled either directly by user, or by workflow policy - doesn't matter, in both
// cases canceled from outside.
m_root.stop();
emitError();
}
void advanceProgress(int byValue) {
if (byValue == 0)
return;
QTC_CHECK(byValue > 0);
QTC_CHECK(m_progressValue + byValue <= m_root.taskCount());
m_progressValue += byValue;
emitProgress();
}
void emitStartedAndProgress() {
GuardLocker locker(m_guard);
emit q->started();
emit q->progressValueChanged(m_progressValue);
}
void emitProgress() {
GuardLocker locker(m_guard);
emit q->progressValueChanged(m_progressValue);
}
void emitDone() { void emitDone() {
QTC_CHECK(m_progressValue == m_root.taskCount());
GuardLocker locker(m_guard); GuardLocker locker(m_guard);
emit q->done(); emit q->done();
} }
void emitError() { void emitError() {
QTC_CHECK(m_progressValue == m_root.taskCount());
GuardLocker locker(m_guard); GuardLocker locker(m_guard);
emit q->errorOccurred(); emit q->errorOccurred();
} }
@@ -147,6 +182,7 @@ public:
TaskTree *q = nullptr; TaskTree *q = nullptr;
TaskNode m_root; TaskNode m_root;
Guard m_guard; Guard m_guard;
int m_progressValue = 0;
}; };
TaskContainer::TaskContainer(TaskTreePrivate *taskTreePrivate, TaskContainer *parentContainer, TaskContainer::TaskContainer(TaskTreePrivate *taskTreePrivate, TaskContainer *parentContainer,
@@ -172,6 +208,7 @@ TaskContainer::~TaskContainer()
void TaskContainer::start() void TaskContainer::start()
{ {
m_currentIndex = 0;
m_groupConfig = {}; m_groupConfig = {};
m_selectedChildren.clear(); m_selectedChildren.clear();
@@ -187,11 +224,14 @@ void TaskContainer::start()
if (m_groupConfig.action == GroupAction::StopWithDone || m_groupConfig.action == GroupAction::StopWithError) { if (m_groupConfig.action == GroupAction::StopWithDone || m_groupConfig.action == GroupAction::StopWithError) {
const bool success = m_groupConfig.action == GroupAction::StopWithDone; const bool success = m_groupConfig.action == GroupAction::StopWithDone;
const int skippedTaskCount = taskCount();
m_taskTreePrivate->advanceProgress(skippedTaskCount);
invokeEndHandler(success); invokeEndHandler(success);
return; return;
} }
selectChildren(); const int skippedTaskCount = selectChildren();
m_taskTreePrivate->advanceProgress(skippedTaskCount);
if (m_selectedChildren.isEmpty()) { if (m_selectedChildren.isEmpty()) {
invokeEndHandler(true); invokeEndHandler(true);
@@ -213,21 +253,35 @@ void TaskContainer::start()
} }
} }
void TaskContainer::selectChildren() int TaskContainer::selectChildren()
{ {
if (m_groupConfig.action != GroupAction::ContinueSelected) { if (m_groupConfig.action != GroupAction::ContinueSelected) {
m_selectedChildren = m_children; m_selectedChildren = m_children;
return; return 0;
} }
m_selectedChildren.clear(); m_selectedChildren.clear();
int skippedTaskCount = 0;
for (int i = 0; i < m_children.size(); ++i) { for (int i = 0; i < m_children.size(); ++i) {
if (m_groupConfig.childrenToRun.contains(i)) if (m_groupConfig.childrenToRun.contains(i))
m_selectedChildren.append(m_children.at(i)); m_selectedChildren.append(m_children.at(i));
else
skippedTaskCount += m_children.at(i)->taskCount();
} }
return skippedTaskCount;
} }
void TaskContainer::stop() void TaskContainer::stop()
{ {
if (!isRunning())
return;
if (m_executeMode == TaskItem::ExecuteMode::Sequential) {
int skippedTaskCount = 0;
for (int i = m_currentIndex + 1; i < m_selectedChildren.size(); ++i)
skippedTaskCount += m_selectedChildren.at(i)->taskCount();
m_taskTreePrivate->advanceProgress(skippedTaskCount);
}
m_currentIndex = -1; m_currentIndex = -1;
for (TaskNode *child : std::as_const(m_selectedChildren)) for (TaskNode *child : std::as_const(m_selectedChildren))
child->stop(); child->stop();
@@ -264,7 +318,7 @@ void TaskContainer::childDone(bool success)
m_selectedChildren.at(m_currentIndex)->start(); m_selectedChildren.at(m_currentIndex)->start();
} }
void TaskContainer::invokeEndHandler(bool success) void TaskContainer::invokeEndHandler(bool success, bool propagateToParent)
{ {
m_currentIndex = -1; m_currentIndex = -1;
m_successBit = success; m_successBit = success;
@@ -275,6 +329,10 @@ void TaskContainer::invokeEndHandler(bool success)
GuardLocker locker(m_taskTreePrivate->m_guard); GuardLocker locker(m_taskTreePrivate->m_guard);
m_groupHandler.m_errorHandler(); m_groupHandler.m_errorHandler();
} }
if (!propagateToParent)
return;
if (m_parentContainer) { if (m_parentContainer) {
m_parentContainer->childDone(success); m_parentContainer->childDone(success);
return; return;
@@ -329,6 +387,7 @@ bool TaskNode::start()
GuardLocker locker(m_container.m_taskTreePrivate->m_guard); GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
m_taskHandler.m_errorHandler(*m_task.get()); m_taskHandler.m_errorHandler(*m_task.get());
} }
m_container.m_taskTreePrivate->advanceProgress(1);
m_task.release()->deleteLater(); m_task.release()->deleteLater();
@@ -342,8 +401,23 @@ bool TaskNode::start()
void TaskNode::stop() void TaskNode::stop()
{ {
m_task.reset(); if (!isRunning())
return;
if (!m_task) {
m_container.stop(); m_container.stop();
m_container.invokeEndHandler(false, false);
return;
}
// TODO: cancelHandler?
// TODO: call TaskInterface::stop() ?
if (m_taskHandler.m_errorHandler) {
GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
m_taskHandler.m_errorHandler(*m_task.get());
}
m_container.m_taskTreePrivate->advanceProgress(1);
m_task.reset();
} }
bool TaskNode::isRunning() const bool TaskNode::isRunning() const
@@ -422,14 +496,14 @@ void TaskTree::start()
QTC_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return); QTC_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return);
QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The start() is called from one of the" QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The start() is called from one of the"
"TaskTree handlers, ingoring..."); return); "TaskTree handlers, ingoring..."); return);
d->m_root.start(); d->start();
} }
void TaskTree::stop() void TaskTree::stop()
{ {
QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The stop() is called from one of the" QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The stop() is called from one of the"
"TaskTree handlers, ingoring..."); return); "TaskTree handlers, ingoring..."); return);
d->m_root.stop(); d->stop();
} }
bool TaskTree::isRunning() const bool TaskTree::isRunning() const
@@ -442,4 +516,9 @@ int TaskTree::taskCount() const
return d->m_root.taskCount(); return d->m_root.taskCount();
} }
int TaskTree::progressValue() const
{
return d->m_progressValue;
}
} // namespace Utils } // namespace Utils

View File

@@ -246,10 +246,14 @@ public:
void stop(); void stop();
bool isRunning() const; bool isRunning() const;
int taskCount() const; int taskCount() const;
int progressMaximum() const { return taskCount(); }
int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded
signals: signals:
void started();
void done(); void done();
void errorOccurred(); void errorOccurred();
void progressValueChanged(int value); // updated whenever task finished / skipped / stopped
private: private:
TaskTreePrivate *d; TaskTreePrivate *d;

View File

@@ -470,6 +470,7 @@ void tst_TaskTree::processTree()
eventLoop.exec(); eventLoop.exec();
QVERIFY(!processTree.isRunning()); QVERIFY(!processTree.isRunning());
QCOMPARE(processTree.progressValue(), taskCount);
QCOMPARE(m_log, expectedLog); QCOMPARE(m_log, expectedLog);
const int expectedDoneCount = success ? 1 : 0; const int expectedDoneCount = success ? 1 : 0;