Merge remote-tracking branch 'origin/11.0'

Change-Id: Ic7bd1fb91f46c5f8fef47b2c442382186aeb7ad3
This commit is contained in:
Eike Ziller
2023-05-23 10:17:31 +02:00
71 changed files with 440 additions and 198 deletions

View File

@@ -16,6 +16,7 @@ headerdirs = . \
../src \
../../../src/libs/aggregation \
../../../src/libs/extensionsystem \
../../../src/libs/solutions/tasking \
../../../src/libs/utils \
../../../src/plugins/coreplugin
@@ -23,6 +24,7 @@ sourcedirs = . \
../src \
../../../src/libs/aggregation \
../../../src/libs/extensionsystem \
../../../src/libs/solutions/tasking \
../../../src/libs/utils \
../../../src/plugins/coreplugin

View File

@@ -34,6 +34,11 @@
for plugins and basic mechanisms for plugin interaction like an
object pool.
\row
\li \l{Tasking}
\li A solution containing a TaskTree and other classes for writing
declarative trees of asynchronous task flows.
\row
\li \l{Utils}
\li Useful classes that are reused in a lot of places in Qt Creator code.

View File

@@ -1034,6 +1034,7 @@ void PluginManagerPrivate::readSettings()
*/
void PluginManagerPrivate::stopAll()
{
m_isShuttingDown = true;
if (delayedInitializeTimer && delayedInitializeTimer->isActive()) {
delayedInitializeTimer->stop();
delete delayedInitializeTimer;
@@ -1839,6 +1840,11 @@ bool PluginManager::isInitializationDone()
return d->m_isInitializationDone;
}
bool PluginManager::isShuttingDown()
{
return d->m_isShuttingDown;
}
/*!
Retrieves one object with \a name from the object pool.
\sa addObject()

View File

@@ -129,6 +129,7 @@ public:
static QString platformName();
static bool isInitializationDone();
static bool isShuttingDown();
static void remoteArguments(const QString &serializedArguments, QObject *socket);
static void shutdown();

View File

@@ -124,6 +124,7 @@ public:
bool m_isInitializationDone = false;
bool enableCrashCheck = true;
bool m_isShuttingDown = false;
QHash<QString, std::function<bool()>> m_scenarios;
QString m_requestedScenario;

View File

@@ -724,7 +724,16 @@ void TaskNode::invokeEndHandler(bool success)
}
/*!
\class TaskTree
\namespace Tasking
\inmodule QtCreator
\brief The Tasking namespace contains a general purpose TaskTree solution.
The Tasking namespace depends on Qt only, and doesn't depend on any \QC
specific code.
*/
/*!
\class Tasking::TaskTree
\inheaderfile solutions/tasking/tasktree.h
\inmodule QtCreator
\ingroup mainclasses
@@ -1180,8 +1189,7 @@ void TaskNode::invokeEndHandler(bool success)
sequential mode).
\li Immediately finishes with an error.
\endlist
If all child tasks finish successfully or the group is empty, the group
finishes with success.
If all child tasks finish successfully, the group finishes with success.
\row
\li continueOnError
\li Similar to stopOnError, but in case any child finishes with
@@ -1194,8 +1202,7 @@ void TaskNode::invokeEndHandler(bool success)
started yet.
\li Finishes with an error when all tasks finish.
\endlist
If all tasks finish successfully or the group is empty, the group
finishes with success.
If all tasks finish successfully, the group finishes with success.
\row
\li stopOnDone
\li If a task finishes with success, the group:
@@ -1203,8 +1210,7 @@ void TaskNode::invokeEndHandler(bool success)
\li Stops running tasks and skips those that it has not started.
\li Immediately finishes with success.
\endlist
If all tasks finish with an error or the group is empty, the group
finishes with an error.
If all tasks finish with an error, the group finishes with an error.
\row
\li continueOnDone
\li Similar to stopOnDone, but in case any child finishes
@@ -1217,22 +1223,22 @@ void TaskNode::invokeEndHandler(bool success)
started yet.
\li Finishes with success when all tasks finish.
\endlist
If all tasks finish with an error or the group is empty, the group
finishes with an error.
If all tasks finish with an error, the group finishes with an error.
\row
\li stopOnFinished
\li The group starts as many tasks as it can. When a task finishes
the group stops and reports the task's result.
When the group is empty, it finishes immediately with success.
Useful only in parallel mode.
In sequential mode, only the first task is started, and when finished,
the group finishes too, so the other tasks are ignored.
\row
\li optional
\li The group executes all tasks and ignores their return state. If all
tasks finish or the group is empty, the group finishes with success.
tasks finish, the group finishes with success.
\endtable
When the group is empty, it finishes immediately with success,
regardless of its workflow policy.
If a child of a group is also a group (in a nested tree), the child group
runs its tasks according to its own workflow policy.

View File

@@ -9,6 +9,7 @@ namespace Utils {
/*!
\class Utils::AnsiEscapeCodeHandler
\inmodule QtCreator
\brief The AnsiEscapeCodeHandler class parses text and extracts ANSI escape codes from it.

View File

@@ -2328,6 +2328,7 @@ void IntegersAspect::setDefaultValue(const QList<int> &value)
/*!
\class Utils::TextDisplay
\inmodule QtCreator
\brief A text display is a phony aspect with the sole purpose of providing
some text display using an Utils::InfoLabel in places where otherwise

View File

@@ -18,6 +18,7 @@
/*!
\class Utils::CheckableMessageBox
\inmodule QtCreator
\brief The CheckableMessageBox class implements a message box suitable for
questions with a

View File

@@ -10,6 +10,7 @@
/*!
\class Utils::ClassNameValidatingLineEdit
\inmodule QtCreator
\brief The ClassNameValidatingLineEdit class implements a line edit that
validates a C++ class name and emits a signal

View File

@@ -41,6 +41,7 @@ namespace Utils {
/*!
\class Utils::ProcessArgs
\inmodule QtCreator
\brief The ProcessArgs class provides functionality for dealing with
shell-quoted process arguments.
@@ -217,7 +218,7 @@ static QStringList doSplitArgsWin(const QString &args, ProcessArgs::SplitError *
If \a err is not NULL, stores a status code at the pointer target. For more
information, see \l SplitError.
If \env is not NULL, performs variable substitution with the
If \a env is not NULL, performs variable substitution with the
given environment.
Returns a list of unquoted words or an empty list if an error occurred.
@@ -253,7 +254,6 @@ static QStringList doSplitArgsWin(const QString &args, ProcessArgs::SplitError *
\c{foo " bar}.
*/
static QStringList splitArgsWin(const QString &_args, bool abortOnMeta,
ProcessArgs::SplitError *err,
const Environment *env, const QString *pwd)
@@ -1398,6 +1398,7 @@ QString ProcessArgs::toString() const
/*!
\class Utils::CommandLine
\inmodule QtCreator
\brief The CommandLine class represents a command line of a QProcess or
similar utility.

View File

@@ -13,7 +13,9 @@ static bool isEndOfWordChar(const QChar &c)
return !c.isLetterOrNumber() && c.category() != QChar::Punctuation_Connector;
}
/*! \class Utils::CompletingTextEdit
/*!
\class Utils::CompletingTextEdit
\inmodule QtCreator
\brief The CompletingTextEdit class is a QTextEdit with auto-completion
support.

View File

@@ -20,6 +20,7 @@
/*!
\class Utils::DetailsWidget
\inmodule QtCreator
\brief The DetailsWidget class implements a button to expand a \e Details
area.

View File

@@ -14,10 +14,10 @@ Q_LOGGING_CATEGORY(deviceShellLog, "qtc.utils.deviceshell", QtWarningMsg)
namespace Utils {
/*!
/*
* The multiplex script waits for input via stdin.
*
* To start a command, a message is send with
* To start a command, a message is sent with
* the format "<cmd-id> "<base64-encoded-stdin-data>" <commandline>\n"
* To stop the script, simply send "exit\n" via stdin
*

View File

@@ -9,6 +9,7 @@
/*!
\class Utils::ElidingLabel
\inmodule QtCreator
\brief The ElidingLabel class is a label suitable for displaying elided
text.

View File

@@ -8,6 +8,7 @@
/*!
\class Utils::FakeToolTip
\inmodule QtCreator
\brief The FakeToolTip class is a widget that pretends to be a tooltip.

View File

@@ -26,6 +26,7 @@
/*!
\class Utils::FancyLineEdit
\inmodule QtCreator
\brief The FancyLineEdit class is an enhanced line edit with several
opt-in features.

View File

@@ -311,7 +311,9 @@ void DockWidget::handleToplevelChanged(bool floating)
/*! \class Utils::FancyMainWindow
/*!
\class Utils::FancyMainWindow
\inmodule QtCreator
\brief The FancyMainWindow class is a MainWindow with dock widgets and
additional "lock" functionality

View File

@@ -40,6 +40,7 @@ static bool checkPath(const FilePath &candidate, int matchLength,
/*!
\class Utils::FileInProjectFinder
\inmodule QtCreator
\brief The FileInProjectFinder class is a helper class to find the \e original
file in the project directory for a given file URL.

View File

@@ -10,6 +10,7 @@
/*!
\class Utils::FileNameValidatingLineEdit
\inmodule QtCreator
\brief The FileNameValidatingLineEdit class is a control that lets the user
choose a (base) file name, based on a QLineEdit.

View File

@@ -37,7 +37,9 @@ static DeviceFileHooks s_deviceHooks;
inline bool isWindowsDriveLetter(QChar ch);
/*! \class Utils::FilePath
/*!
\class Utils::FilePath
\inmodule QtCreator
\brief The FilePath class is an abstraction for handles to objects
in a (possibly remote) file system, similar to a URL or, in the local
@@ -710,20 +712,39 @@ bool FilePath::isSameFile(const FilePath &other) const
return false;
}
static FilePaths appendExeExtensions(const Environment &env, const FilePath &executable)
static FilePaths appendExeExtensions(const FilePath &executable,
FilePath::MatchScope matchScope)
{
FilePaths execs = {executable};
if (executable.osType() == OsTypeWindows) {
FilePaths result = {executable};
const QStringView suffix = executable.suffixView();
if (executable.osType() == OsTypeWindows && suffix.isEmpty()) {
switch (matchScope) {
case FilePath::ExactMatchOnly:
break;
case FilePath::WithExeSuffix:
result.append(executable.stringAppended(".exe"));
break;
case FilePath::WithBatSuffix:
result.append(executable.stringAppended(".bat"));
break;
case FilePath::WithExeOrBatSuffix:
result.append(executable.stringAppended(".exe"));
result.append(executable.stringAppended(".bat"));
break;
case FilePath::WithAnySuffix: {
// Check all the executable extensions on windows:
// PATHEXT is only used if the executable has no extension
if (executable.suffixView().isEmpty()) {
const QStringList extensions = env.expandedValueForKey("PATHEXT").split(';');
static const QStringList extensions = Environment::systemEnvironment()
.expandedValueForKey("PATHEXT").split(';');
for (const QString &ext : extensions)
execs << executable.stringAppended(ext.toLower());
result.append(executable.stringAppended(ext.toLower()));
break;
}
default:
break;
}
}
return execs;
return result;
}
bool FilePath::isSameExecutable(const FilePath &other) const
@@ -734,9 +755,8 @@ bool FilePath::isSameExecutable(const FilePath &other) const
if (!isSameDevice(other))
return false;
const Environment env = other.deviceEnvironment();
const FilePaths exe1List = appendExeExtensions(env, *this);
const FilePaths exe2List = appendExeExtensions(env, other);
const FilePaths exe1List = appendExeExtensions(*this, WithAnySuffix);
const FilePaths exe2List = appendExeExtensions(other, WithAnySuffix);
for (const FilePath &f1 : exe1List) {
for (const FilePath &f2 : exe2List) {
if (f1.isSameFile(f2))
@@ -1478,32 +1498,63 @@ FilePath FilePath::withNewPath(const QString &newPath) const
assert(fullPath == FilePath::fromUrl("docker://123/usr/bin/make"))
\endcode
*/
FilePath FilePath::searchInDirectories(const FilePaths &dirs, const FilePathPredicate &filter) const
FilePath FilePath::searchInDirectories(const FilePaths &dirs,
const FilePathPredicate &filter,
const MatchScope &matchScope) const
{
if (isAbsolutePath())
return *this;
return deviceEnvironment().searchInDirectories(path(), dirs, filter);
if (isEmpty())
return {};
const FilePaths execs = appendExeExtensions(*this, matchScope);
if (isAbsolutePath()) {
for (const FilePath &filePath : execs) {
if (filePath.isExecutableFile() && (!filter || filter(filePath)))
return filePath;
}
return {};
}
QSet<FilePath> alreadyCheckedDirectories;
for (const FilePath &dir : dirs) {
// Compare the initial size of the set with the size after insertion to check
// if the directory was already checked.
const int initialCount = alreadyCheckedDirectories.count();
alreadyCheckedDirectories.insert(dir);
const bool wasAlreadyChecked = alreadyCheckedDirectories.count() == initialCount;
if (dir.isEmpty() || wasAlreadyChecked)
continue;
for (const FilePath &exe : execs) {
const FilePath filePath = dir / exe.path();
if (filePath.isExecutableFile() && (!filter || filter(filePath)))
return filePath;
}
}
return {};
}
FilePath FilePath::searchInPath(const FilePaths &additionalDirs,
PathAmending amending,
const FilePathPredicate &filter) const
const FilePathPredicate &filter,
const MatchScope &matchScope) const
{
if (isAbsolutePath())
return *this;
FilePaths directories = deviceEnvironment().path();
if (needsDevice()) {
directories = Utils::transform(directories, [this](const FilePath &filePath) {
return withNewPath(filePath.path());
});
}
FilePaths directories = devicePathEnvironmentVariable();
if (!additionalDirs.isEmpty()) {
if (amending == AppendToPath)
directories.append(additionalDirs);
else
directories = additionalDirs + directories;
}
return searchInDirectories(directories, filter);
return searchInDirectories(directories, filter, matchScope);
}
Environment FilePath::deviceEnvironment() const
@@ -1515,6 +1566,16 @@ Environment FilePath::deviceEnvironment() const
return Environment::systemEnvironment();
}
FilePaths FilePath::devicePathEnvironmentVariable() const
{
FilePaths result = deviceEnvironment().path();
if (needsDevice()) {
for (FilePath &dir : result)
dir.setParts(this->scheme(), this->host(), dir.path());
}
return result;
}
QString FilePath::formatFilePaths(const FilePaths &files, const QString &separator)
{
const QStringList nativeFiles = transform(files, &FilePath::toUserOutput);

View File

@@ -161,9 +161,8 @@ public:
[[nodiscard]] FilePath withExecutableSuffix() const;
[[nodiscard]] FilePath relativeChildPath(const FilePath &parent) const;
[[nodiscard]] FilePath relativePathFrom(const FilePath &anchor) const;
[[nodiscard]] FilePath searchInDirectories(const FilePaths &dirs,
const FilePathPredicate &filter = {}) const;
[[nodiscard]] Environment deviceEnvironment() const;
[[nodiscard]] FilePaths devicePathEnvironmentVariable() const;
[[nodiscard]] FilePath withNewPath(const QString &newPath) const;
[[nodiscard]] FilePath withNewMappedPath(const FilePath &newPath) const;
@@ -183,12 +182,17 @@ public:
const FileFilter &filter);
enum PathAmending { AppendToPath, PrependToPath };
[[nodiscard]] FilePath searchInPath(const FilePaths &additionalDirs = {},
PathAmending = AppendToPath,
const FilePathPredicate &filter = {}) const;
enum MatchScope { ExactMatchOnly, WithExeSuffix, WithBatSuffix,
WithExeOrBatSuffix, WithAnySuffix };
[[nodiscard]] FilePath searchInDirectories(const FilePaths &dirs,
const FilePathPredicate &filter = {},
const MatchScope &matchScope = {}) const;
[[nodiscard]] FilePath searchInPath(const FilePaths &additionalDirs = {},
PathAmending = AppendToPath,
const FilePathPredicate &filter = {},
const MatchScope &matchScope = {}) const;
std::optional<FilePath> refersToExecutableFile(MatchScope considerScript) const;
[[nodiscard]] expected_str<FilePath> tmpDir() const;

View File

@@ -28,6 +28,7 @@ static inline quint64 getFileLimit()
/*!
\class Utils::FileSystemWatcher
\inmodule QtCreator
\brief The FileSystemWatcher class is a file watcher that internally uses
a centralized QFileSystemWatcher
and enforces limits on Mac OS.

View File

@@ -259,7 +259,9 @@ TempFileSaver::~TempFileSaver()
QFile::remove(m_filePath.toString());
}
/*! \class Utils::FileUtils
/*!
\class Utils::FileUtils
\inmodule QtCreator
\brief The FileUtils class contains file and directory related convenience
functions.

View File

@@ -8,6 +8,7 @@
/*!
\class Utils::FileWizardPage
\inmodule QtCreator
\brief The FileWizardPage class is a standard wizard page for a single file
letting the user choose name

View File

@@ -3,7 +3,9 @@
#include "futuresynchronizer.h"
/*! \class Utils::FutureSynchronizer
/*!
\class Utils::FutureSynchronizer
\inmodule QtCreator
\brief The FutureSynchronizer is an enhanced version of QFutureSynchronizer.
*/

View File

@@ -4,7 +4,9 @@
#include "guard.h"
#include "qtcassert.h"
/*! \class Utils::Guard
/*!
\class Utils::Guard
\inmodule QtCreator
\brief The Guard class implements a recursive guard with locking mechanism.

View File

@@ -10,6 +10,7 @@ using namespace Utils;
/*!
\class Utils::HeaderViewStretcher
\inmodule QtCreator
\brief The HeaderViewStretcher class fixes QHeaderView to resize all
columns to contents, except one

View File

@@ -5,6 +5,7 @@
/*!
\class Utils::TreeView
\inmodule QtCreator
\brief The TreeView adds setActivationMode to QTreeView
to allow for single click/double click behavior on
@@ -15,6 +16,7 @@
/*!
\class Utils::TreeWidget
\inmodule QtCreator
\brief The TreeWidget adds setActivationMode to QTreeWidget
to allow for single click/double click behavior on
@@ -25,6 +27,7 @@
/*!
\class Utils::ListView
\inmodule QtCreator
\brief The ListView adds setActivationMode to QListView
to allow for single click/double click behavior on
@@ -35,6 +38,7 @@
/*!
\class Utils::ListWidget
\inmodule QtCreator
\brief The ListWidget adds setActivationMode to QListWidget
to allow for single click/double click behavior on

View File

@@ -175,6 +175,14 @@ private:
int m_vSpace;
};
/*!
\namespace Layouting
\inmodule QtCreator
\brief The Layouting namespace contains classes for use with layout builders.
*/
/*!
\class Layouting::LayoutItem
\inmodule QtCreator
@@ -469,18 +477,18 @@ void doAddWidget(LayoutBuilder &builder, QWidget *widget)
\class Layouting::Space
\inmodule QtCreator
\brief The Layouting::Space class represents some empty space in a layout.
\brief The Space class represents some empty space in a layout.
*/
/*!
\class Layouting::Stretch
\inmodule QtCreator
\brief The Layouting::Stretch class represents some stretch in a layout.
\brief The Stretch class represents some stretch in a layout.
*/
/*!
\class LayoutBuilder
\class Layouting::LayoutBuilder
\inmodule QtCreator
\brief The LayoutBuilder class provides a convenient way to fill \c QFormLayout

View File

@@ -104,6 +104,7 @@ using namespace Internal;
/*!
\class Utils::MacroExpander
\inmodule QtCreator
\brief The MacroExpander class manages \QC wide variables, that a user
can enter into many string settings. The variables are replaced by an actual value when the string
is used, similar to how environment variables are expanded by a shell.

View File

@@ -9,6 +9,7 @@
/*!
\class Utils::NavigationTreeView
\inmodule QtCreator
\brief The NavigationTreeView class implements a general TreeView for any
sidebar widget.

View File

@@ -11,6 +11,7 @@ namespace Utils {
/*!
\class Utils::OptionPushButton
\inmodule QtCreator
\brief The OptionPushButton class implements a QPushButton for which the menu is only opened
if the user presses the menu indicator.

View File

@@ -5,6 +5,7 @@
/*!
\class Utils::ParameterAction
\inmodule QtCreator
\brief The ParameterAction class is intended for actions that act on a 'current',
string-type parameter (typically a file name), for example 'Save file %1'.

View File

@@ -16,6 +16,7 @@
/*!
\class Utils::PathListEditor
\inmodule QtCreator
\brief The PathListEditor class is a control that lets the user edit a list
of (directory) paths

View File

@@ -50,6 +50,7 @@ static QRect stringToRectangle(const QString &v)
/*!
\class Utils::PersistentSettingsReader
\inmodule QtCreator
\brief The PersistentSettingsReader class reads a QVariantMap of arbitrary,
nested data structures from an XML file.
@@ -349,6 +350,7 @@ FilePath PersistentSettingsReader::filePath()
/*!
\class Utils::PersistentSettingsWriter
\inmodule QtCreator
\brief The PersistentSettingsWriter class serializes a QVariantMap of
arbitrary, nested data structures to an XML file.

View File

@@ -10,7 +10,9 @@
#include <limits>
/*! \class Utils::Port
/*!
\class Utils::Port
\inmodule QtCreator
\brief The Port class implements a wrapper around a 16 bit port number
to be used in conjunction with IP addresses.

View File

@@ -1086,6 +1086,7 @@ ProcessResult ProcessPrivate::interpretExitCode(int exitCode)
/*!
\class Utils::Process
\inmodule QtCreator
\brief The Process class provides functionality for with processes.
@@ -1596,6 +1597,7 @@ QString Process::readAllStandardError()
/*!
\class Utils::SynchronousProcess
\inmodule QtCreator
\brief The SynchronousProcess class runs a synchronous process in its own
event loop that blocks only user input events. Thus, it allows for the GUI to

View File

@@ -7,6 +7,7 @@ namespace Utils {
/*!
\class Utils::ProcessHandle
\inmodule QtCreator
\brief The ProcessHandle class is a helper class to describe a process.
Encapsulates parameters of a running process, local (PID) or remote (to be

View File

@@ -22,6 +22,7 @@
/*!
\class Utils::ProjectIntroPage
\inmodule QtCreator
\brief The ProjectIntroPage class is the standard wizard page for a project,
letting the user choose its name

View File

@@ -7,6 +7,7 @@
/*!
\class Utils::StatusLabel
\inmodule QtCreator
\brief The StatusLabel class displays messages for a while with a timeout.
*/

View File

@@ -7,6 +7,7 @@ namespace Utils {
/*!
\class Utils::TextFieldCheckBox
\inmodule QtCreator
\brief The TextFieldCheckBox class is a aheckbox that plays with
\c QWizard::registerField.

View File

@@ -9,6 +9,7 @@ namespace Utils {
/*!
\class Utils::TextFieldComboBox
\inmodule QtCreator
\brief The TextFieldComboBox class is a non-editable combo box for text
editing purposes that plays with \c QWizard::registerField (providing a
settable 'text' property).

View File

@@ -33,6 +33,7 @@ QDebug operator<<(QDebug d, const TextFileFormat &format)
/*!
\class Utils::TextFileFormat
\inmodule QtCreator
\brief The TextFileFormat class describes the format of a text file and
provides autodetection.

View File

@@ -895,6 +895,7 @@ void TreeItem::propagateModel(BaseTreeModel *m)
/*!
\class Utils::TreeModel
\inmodule QtCreator
\brief The TreeModel class is a convienience base class for models
to use in a QTreeView.

View File

@@ -3,7 +3,8 @@
/*!
\namespace Utils
\inmodule QtCreator
The Utils namespace contains a collection of utility classes and functions for use by all
\brief The Utils namespace contains a collection of utility classes and functions for use by all
plugins.
*/

View File

@@ -23,7 +23,9 @@
#include <QVBoxLayout>
/*! \class Utils::Wizard
/*!
\class Utils::Wizard
\inmodule QtCreator
\brief The Wizard class implements a wizard with a progress bar on the left.

View File

@@ -5,7 +5,9 @@
#include "wizard.h"
/*! \class Utils::WizardPage
/*!
\class Utils::WizardPage
\inmodule QtCreator
\brief QWizardPage with a couple of improvements.

View File

@@ -1268,6 +1268,7 @@ LocatorFilterEntries LocatorFileCachePrivate::generate(const QFuture<void> &futu
/*!
\class Core::LocatorFileCache
\inmodule QtCreator
\brief The LocatorFileCache class encapsulates all the responsibilities needed for
implementing a cache for file filters.

View File

@@ -26,6 +26,8 @@
#include "../settingsdatabase.h"
#include "../statusbarmanager.h"
#include <extensionsystem/pluginmanager.h>
#include <utils/algorithm.h>
#include <utils/async.h>
#include <utils/qtcassert.h>
@@ -147,7 +149,6 @@ bool Locator::delayedInitialize()
void Locator::aboutToShutdown()
{
m_shuttingDown = true;
m_refreshTimer.stop();
m_taskTree.reset();
}
@@ -373,7 +374,7 @@ void Locator::setUseCenteredPopupForShortcut(bool center)
void Locator::refresh(const QList<ILocatorFilter *> &filters)
{
if (m_shuttingDown)
if (ExtensionSystem::PluginManager::isShuttingDown())
return;
m_taskTree.reset(); // Superfluous, just for clarity. The next reset() below is enough.

View File

@@ -67,7 +67,6 @@ private:
bool useCenteredPopup = false;
};
bool m_shuttingDown = false;
bool m_settingsInitialized = false;
Settings m_settings;
QList<ILocatorFilter *> m_filters;

View File

@@ -70,6 +70,7 @@ void ProcessProgressPrivate::parseProgress(const QString &inputText)
/*!
\class Core::ProcessProgress
\inmodule QtCreator
\brief The ProcessProgress class is responsible for showing progress of the running process.

View File

@@ -91,6 +91,7 @@ void TaskProgressPrivate::updateProgress()
/*!
\class Core::TaskProgress
\inmodule QtCreator
\brief The TaskProgress class is responsible for showing progress of the running task tree.

View File

@@ -686,7 +686,6 @@ public:
EngineManager m_engineManager;
QTimer m_shutdownTimer;
bool m_shuttingDown = false;
Console m_console; // ensure Debugger Console is created before settings are taken into account
DebuggerSettings m_debuggerSettings;
@@ -1392,7 +1391,7 @@ static QVariant configValue(const QString &name)
void DebuggerPluginPrivate::updatePresetState()
{
if (m_shuttingDown)
if (PluginManager::isShuttingDown())
return;
Project *startupProject = ProjectManager::startupProject();
@@ -1996,8 +1995,6 @@ void DebuggerPluginPrivate::dumpLog()
void DebuggerPluginPrivate::aboutToShutdown()
{
m_shuttingDown = true;
disconnect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, this, nullptr);
m_shutdownTimer.setInterval(0);
@@ -2081,7 +2078,7 @@ QWidget *addSearch(BaseTreeView *treeView)
void openTextEditor(const QString &titlePattern0, const QString &contents)
{
if (dd->m_shuttingDown)
if (PluginManager::isShuttingDown())
return;
QString titlePattern = titlePattern0;
IEditor *editor = EditorManager::openEditorWithContents(

View File

@@ -135,11 +135,6 @@ void EmacsKeysPlugin::extensionsInitialized()
{
}
ExtensionSystem::IPlugin::ShutdownFlag EmacsKeysPlugin::aboutToShutdown()
{
return SynchronousShutdown;
}
void EmacsKeysPlugin::editorAboutToClose(IEditor *editor)
{
auto w = qobject_cast<QPlainTextEdit*>(editor->widget());

View File

@@ -57,7 +57,6 @@ public:
void initialize() override;
void extensionsInitialized() override;
ShutdownFlag aboutToShutdown() override;
private:
void editorAboutToClose(Core::IEditor *editor);

View File

@@ -107,12 +107,9 @@ class FossilLogConfig : public VcsBaseEditorConfig
Q_OBJECT
public:
FossilLogConfig(FossilClient *client, QToolBar *toolBar) :
VcsBaseEditorConfig(toolBar),
m_client(client)
FossilLogConfig(QToolBar *toolBar)
: VcsBaseEditorConfig(toolBar)
{
QTC_ASSERT(client, return);
addReloadButton();
addLineageComboBox();
addVerboseToggleButton();
@@ -192,9 +189,6 @@ public:
}
return args;
}
private:
FossilClient *m_client;
};
unsigned FossilClient::makeVersionNumber(int major, int minor, int patch)
@@ -1169,7 +1163,7 @@ VcsBaseEditorConfig *FossilClient::createLogCurrentFileEditor(VcsBaseEditorWidge
VcsBaseEditorConfig *FossilClient::createLogEditor(VcsBaseEditorWidget *editor)
{
return new FossilLogConfig(this, editor->toolBar());
return new FossilLogConfig(editor->toolBar());
}
} // namespace Internal

View File

@@ -28,6 +28,8 @@
#include <coreplugin/messagemanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <extensionsystem/pluginmanager.h>
#include <languageserverprotocol/completion.h>
#include <languageserverprotocol/diagnostics.h>
#include <languageserverprotocol/initializemessages.h>
@@ -194,7 +196,7 @@ public:
// temporary container needed since m_resetAssistProvider is changed in resetAssistProviders
for (TextDocument *document : m_resetAssistProvider.keys())
resetAssistProviders(document);
if (!LanguageClientManager::isShuttingDown()) {
if (!ExtensionSystem::PluginManager::isShuttingDown()) {
// prevent accessing deleted editors on Creator shutdown
const QList<Core::IEditor *> &editors = Core::DocumentModel::editorsForOpenedDocuments();
for (Core::IEditor *editor : editors) {

View File

@@ -14,6 +14,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/navigationwidget.h>
#include <extensionsystem/pluginmanager.h>
#include <languageserverprotocol/messages.h>
#include <languageserverprotocol/progresssupport.h>
@@ -32,6 +34,7 @@
#include <QTimer>
using namespace ExtensionSystem;
using namespace LanguageServerProtocol;
namespace LanguageClient {
@@ -39,7 +42,6 @@ namespace LanguageClient {
static Q_LOGGING_CATEGORY(Log, "qtc.languageclient.manager", QtWarningMsg)
static LanguageClientManager *managerInstance = nullptr;
static bool g_shuttingDown = false;
class LanguageClientManagerPrivate
{
@@ -139,7 +141,7 @@ void LanguageClientManager::clientStarted(Client *client)
QTC_ASSERT(client, return);
if (client->state() != Client::Uninitialized) // do not proceed if we already received an error
return;
if (g_shuttingDown) {
if (PluginManager::isShuttingDown()) {
clientFinished(client);
return;
}
@@ -165,7 +167,7 @@ void LanguageClientManager::clientFinished(Client *client)
&& client->state() != Client::ShutdownRequested;
if (unexpectedFinish) {
if (!g_shuttingDown) {
if (!PluginManager::isShuttingDown()) {
const QList<TextEditor::TextDocument *> &clientDocs
= managerInstance->m_clientForDocument.keys(client);
if (client->reset()) {
@@ -187,7 +189,7 @@ void LanguageClientManager::clientFinished(Client *client)
}
}
deleteClient(client);
if (g_shuttingDown && managerInstance->m_clients.isEmpty())
if (PluginManager::isShuttingDown() && managerInstance->m_clients.isEmpty())
emit managerInstance->shutdownFinished();
}
@@ -236,17 +238,14 @@ void LanguageClientManager::deleteClient(Client *client)
for (QList<Client *> &clients : managerInstance->m_clientsForSetting)
clients.removeAll(client);
client->deleteLater();
if (!g_shuttingDown)
if (!PluginManager::isShuttingDown())
emit instance()->clientRemoved(client);
}
void LanguageClientManager::shutdown()
{
QTC_ASSERT(managerInstance, return);
if (g_shuttingDown)
return;
qCDebug(Log) << "shutdown manager";
g_shuttingDown = true;
const auto clients = managerInstance->clients();
for (Client *client : clients)
shutdownClient(client);
@@ -258,11 +257,6 @@ void LanguageClientManager::shutdown()
});
}
bool LanguageClientManager::isShuttingDown()
{
return g_shuttingDown;
}
LanguageClientManager *LanguageClientManager::instance()
{
return managerInstance;

View File

@@ -48,7 +48,6 @@ public:
static void deleteClient(Client *client);
static void shutdown();
static bool isShuttingDown();
static LanguageClientManager *instance();

View File

@@ -22,9 +22,6 @@
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
#include <QCheckBox>
#include <QLabel>
#include <QLineEdit>
#include <QThread>
#include <optional>

View File

@@ -73,7 +73,6 @@ private:
QStringList jobArguments() const;
Utils::MultiSelectionAspect *m_buildTargetsAspect = nullptr;
QStringList m_availableTargets; // FIXME: Unused, remove in 4.15.
Utils::StringAspect *m_makeCommandAspect = nullptr;
Utils::StringAspect *m_userArgumentsAspect = nullptr;
Utils::IntegerAspect *m_userJobCountAspect = nullptr;

View File

@@ -177,6 +177,7 @@
*/
using namespace Core;
using namespace ExtensionSystem;
using namespace ProjectExplorer::Internal;
using namespace Utils;
@@ -600,7 +601,6 @@ public:
BuildPropertiesSettings m_buildPropertiesSettings;
QList<CustomParserSettings> m_customParsers;
bool m_shouldHaveRunConfiguration = false;
bool m_shuttingDown = false;
Id m_runMode = Constants::NO_RUN_MODE;
ToolChainManager *m_toolChainManager = nullptr;
@@ -1632,11 +1632,11 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(ICore::instance(), &ICore::saveSettingsRequested,
dd, &ProjectExplorerPluginPrivate::savePersistentSettings);
connect(EditorManager::instance(), &EditorManager::autoSaved, this, [] {
if (!dd->m_shuttingDown && !SessionManager::loadingSession())
ProjectManager::save();
if (!PluginManager::isShuttingDown() && !SessionManager::loadingSession())
SessionManager::saveSession();
});
connect(qApp, &QApplication::applicationStateChanged, this, [](Qt::ApplicationState state) {
if (!dd->m_shuttingDown && state == Qt::ApplicationActive)
if (!PluginManager::isShuttingDown() && state == Qt::ApplicationActive)
dd->updateWelcomePage();
});
@@ -2173,7 +2173,7 @@ void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu()
m_runWithoutDeployAction->setVisible(m_projectExplorerSettings.deployBeforeRun);
}
ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
{
disconnect(ModeManager::instance(), &ModeManager::currentModeChanged,
dd, &ProjectExplorerPluginPrivate::currentModeChanged);
@@ -2181,8 +2181,6 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
ToolChainManager::aboutToShutdown();
ProjectManager::closeAllProjects();
dd->m_shuttingDown = true;
// Attempt to synchronously shutdown all run controls.
// If that fails, fall back to asynchronous shutdown (Debugger run controls
// might shutdown asynchronously).
@@ -2212,7 +2210,7 @@ void ProjectExplorerPlugin::openNewProjectDialog()
void ProjectExplorerPluginPrivate::showSessionManager()
{
ProjectManager::save();
SessionManager::saveSession();
SessionDialog sessionDialog(ICore::dialogParent());
sessionDialog.setAutoLoadSession(sb_d->isAutoRestoreLastSession());
sessionDialog.exec();
@@ -2251,14 +2249,14 @@ bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project
void ProjectExplorerPluginPrivate::savePersistentSettings()
{
if (dd->m_shuttingDown)
if (PluginManager::isShuttingDown())
return;
if (!SessionManager::loadingSession()) {
for (Project *pro : ProjectManager::projects())
pro->saveSettings();
ProjectManager::save();
SessionManager::saveSession();
}
QtcSettings *s = ICore::settings();
@@ -2560,7 +2558,7 @@ void ProjectExplorerPluginPrivate::checkForShutdown()
{
--m_activeRunControlCount;
QTC_ASSERT(m_activeRunControlCount >= 0, m_activeRunControlCount = 0);
if (m_shuttingDown && m_activeRunControlCount == 0)
if (PluginManager::isShuttingDown() && m_activeRunControlCount == 0)
emit m_instance->asynchronousShutdownFinished();
}

View File

@@ -53,6 +53,7 @@ class ProjectManagerPrivate
{
public:
void loadSession();
void saveSession();
void restoreDependencies();
void restoreStartupProject();
void restoreProjects(const FilePaths &fileList);
@@ -109,6 +110,9 @@ ProjectManager::ProjectManager()
connect(SessionManager::instance(), &SessionManager::aboutToLoadSession, this, [] {
d->loadSession();
});
connect(SessionManager::instance(), &SessionManager::aboutToSaveSession, this, [] {
d->saveSession();
});
}
ProjectManager::~ProjectManager()
@@ -315,53 +319,29 @@ void ProjectManager::removeProject(Project *project)
removeProjects({project});
}
bool ProjectManager::save()
void ProjectManagerPrivate::saveSession()
{
emit SessionManager::instance()->aboutToSaveSession();
const FilePath filePath = SessionManager::sessionNameToFileName(sb_d->m_sessionName);
QVariantMap data;
// See the explanation at loadSession() for how we handle the implicit default session.
if (SessionManager::isDefaultVirgin()) {
if (filePath.exists()) {
PersistentSettingsReader reader;
if (!reader.load(filePath)) {
QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"),
Tr::tr("Could not save session %1").arg(filePath.toUserOutput()));
return false;
}
data = reader.restoreValues();
}
} else {
// save the startup project
if (d->m_startupProject)
data.insert("StartupProject", d->m_startupProject->projectFilePath().toSettings());
SessionManager::setSessionValue("StartupProject",
m_startupProject->projectFilePath().toSettings());
const QColor c = StyleHelper::requestedBaseColor();
if (c.isValid()) {
QString tmp = QString::fromLatin1("#%1%2%3")
.arg(c.red(), 2, 16, QLatin1Char('0'))
.arg(c.green(), 2, 16, QLatin1Char('0'))
.arg(c.blue(), 2, 16, QLatin1Char('0'));
data.insert(QLatin1String("Color"), tmp);
}
FilePaths projectFiles = Utils::transform(projects(), &Project::projectFilePath);
FilePaths projectFiles = Utils::transform(m_projects, &Project::projectFilePath);
// Restore information on projects that failed to load:
// don't read projects to the list, which the user loaded
for (const FilePath &failed : std::as_const(d->m_failedProjects)) {
for (const FilePath &failed : std::as_const(m_failedProjects)) {
if (!projectFiles.contains(failed))
projectFiles << failed;
}
data.insert("ProjectList", Utils::transform<QStringList>(projectFiles,
SessionManager::setSessionValue("ProjectList",
Utils::transform<QStringList>(projectFiles,
&FilePath::toString));
data.insert("CascadeSetActive", d->m_casadeSetActive);
SessionManager::setSessionValue("CascadeSetActive", m_casadeSetActive);
QVariantMap depMap;
auto i = d->m_depMap.constBegin();
while (i != d->m_depMap.constEnd()) {
auto i = m_depMap.constBegin();
while (i != m_depMap.constEnd()) {
QString key = i.key().toString();
QStringList values;
const FilePaths valueList = i.value();
@@ -370,32 +350,7 @@ bool ProjectManager::save()
depMap.insert(key, values);
++i;
}
data.insert(QLatin1String("ProjectDependencies"), QVariant(depMap));
data.insert(QLatin1String("EditorSettings"), EditorManager::saveState().toBase64());
}
const auto end = sb_d->m_values.constEnd();
QStringList keys;
for (auto it = sb_d->m_values.constBegin(); it != end; ++it) {
data.insert(QLatin1String("value-") + it.key(), it.value());
keys << it.key();
}
data.insert(QLatin1String("valueKeys"), keys);
if (!sb_d->m_writer || sb_d->m_writer->fileName() != filePath) {
delete sb_d->m_writer;
sb_d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession");
}
const bool result = sb_d->m_writer->save(data, ICore::dialogParent());
if (result) {
if (!SessionManager::isDefaultVirgin())
sb_d->m_sessionDateTimes.insert(SessionManager::activeSession(), QDateTime::currentDateTime());
} else {
QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error while saving session"),
Tr::tr("Could not save session to file %1").arg(sb_d->m_writer->fileName().toUserOutput()));
}
return result;
SessionManager::setSessionValue(QLatin1String("ProjectDependencies"), QVariant(depMap));
}
/*!

View File

@@ -47,7 +47,6 @@ public:
});
}
static bool save();
static void closeAllProjects();
static void addProject(Project *project);

View File

@@ -7,7 +7,6 @@
#include "projectexplorer.h"
#include "projectexplorertr.h"
#include "projectmanager.h"
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
@@ -514,7 +513,7 @@ bool SessionManager::loadSession(const QString &session, bool initial)
// Allow everyone to set something in the session and before saving
emit SessionManager::instance()->aboutToUnloadSession(sb_d->m_sessionName);
if (!ProjectManager::save()) {
if (!saveSession()) {
sb_d->m_loadingSession = false;
return false;
}
@@ -571,4 +570,67 @@ bool SessionManager::loadSession(const QString &session, bool initial)
return true;
}
bool SessionManager::saveSession()
{
emit SessionManager::instance()->aboutToSaveSession();
const FilePath filePath = SessionManager::sessionNameToFileName(sb_d->m_sessionName);
QVariantMap data;
// See the explanation at loadSession() for how we handle the implicit default session.
if (SessionManager::isDefaultVirgin()) {
if (filePath.exists()) {
PersistentSettingsReader reader;
if (!reader.load(filePath)) {
QMessageBox::warning(ICore::dialogParent(),
Tr::tr("Error while saving session"),
Tr::tr("Could not save session %1")
.arg(filePath.toUserOutput()));
return false;
}
data = reader.restoreValues();
}
} else {
const QColor c = StyleHelper::requestedBaseColor();
if (c.isValid()) {
QString tmp = QString::fromLatin1("#%1%2%3")
.arg(c.red(), 2, 16, QLatin1Char('0'))
.arg(c.green(), 2, 16, QLatin1Char('0'))
.arg(c.blue(), 2, 16, QLatin1Char('0'));
setSessionValue("Color", tmp);
}
setSessionValue("EditorSettings", EditorManager::saveState().toBase64());
const auto end = sb_d->m_sessionValues.constEnd();
for (auto it = sb_d->m_sessionValues.constBegin(); it != end; ++it)
data.insert(it.key(), it.value());
}
const auto end = sb_d->m_values.constEnd();
QStringList keys;
for (auto it = sb_d->m_values.constBegin(); it != end; ++it) {
data.insert("value-" + it.key(), it.value());
keys << it.key();
}
data.insert("valueKeys", keys);
if (!sb_d->m_writer || sb_d->m_writer->fileName() != filePath) {
delete sb_d->m_writer;
sb_d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession");
}
const bool result = sb_d->m_writer->save(data, ICore::dialogParent());
if (result) {
if (!SessionManager::isDefaultVirgin())
sb_d->m_sessionDateTimes.insert(SessionManager::activeSession(),
QDateTime::currentDateTime());
} else {
QMessageBox::warning(ICore::dialogParent(),
Tr::tr("Error while saving session"),
Tr::tr("Could not save session to file %1")
.arg(sb_d->m_writer->fileName().toUserOutput()));
}
return result;
}
} // namespace ProjectExplorer

View File

@@ -64,6 +64,7 @@ public:
static void addSessionLoadingSteps(int steps);
static bool loadSession(const QString &session, bool initial = false);
static bool saveSession();
signals:
void startupSessionRestored();

View File

@@ -210,11 +210,6 @@ void QmlJSEditorPlugin::extensionsInitialized()
QmllsSettingsManager::instance()->setupAutoupdate();
}
ExtensionSystem::IPlugin::ShutdownFlag QmlJSEditorPlugin::aboutToShutdown()
{
return IPlugin::aboutToShutdown();
}
Utils::JsonSchemaManager *QmlJSEditorPlugin::jsonManager()
{
return &m_instance->d->m_jsonManager;

View File

@@ -29,7 +29,6 @@ public:
private:
void initialize() final;
void extensionsInitialized() final;
ShutdownFlag aboutToShutdown() final;
class QmlJSEditorPluginPrivate *d = nullptr;
};

View File

@@ -631,6 +631,115 @@ void tst_Tasking::testTree_data()
QTest::newRow("SequentialError") << TestData{storage, root, log, 5, OnDone::Failure};
}
{
const auto constructEmptyWorkflow = [=](WorkflowPolicy policy) {
return Group {
Storage(storage),
workflowPolicy(policy),
onGroupDone(groupDone(0)),
onGroupError(groupError(0))
};
};
const Log log = {{0, Handler::GroupDone}};
const Group root1 = constructEmptyWorkflow(WorkflowPolicy::StopOnError);
QTest::newRow("EmptyStopOnError") << TestData{storage, root1, log, 0, OnDone::Success};
const Group root2 = constructEmptyWorkflow(WorkflowPolicy::ContinueOnError);
QTest::newRow("EmptyContinueOnError") << TestData{storage, root2, log, 0, OnDone::Success};
const Group root3 = constructEmptyWorkflow(WorkflowPolicy::StopOnDone);
QTest::newRow("EmptyStopOnDone") << TestData{storage, root3, log, 0, OnDone::Success};
const Group root4 = constructEmptyWorkflow(WorkflowPolicy::ContinueOnDone);
QTest::newRow("EmptyContinueOnDone") << TestData{storage, root4, log, 0, OnDone::Success};
const Group root5 = constructEmptyWorkflow(WorkflowPolicy::StopOnFinished);
QTest::newRow("EmptyStopOnFinished") << TestData{storage, root5, log, 0, OnDone::Success};
const Group root6 = constructEmptyWorkflow(WorkflowPolicy::Optional);
QTest::newRow("EmptyOptional") << TestData{storage, root6, log, 0, OnDone::Success};
}
{
const auto constructDoneWorkflow = [=](WorkflowPolicy policy) {
return Group {
Storage(storage),
workflowPolicy(policy),
Test(setupTask(1), logDone, logError),
onGroupDone(groupDone(0)),
onGroupError(groupError(0))
};
};
const Log log = {
{1, Handler::Setup},
{1, Handler::Done},
{0, Handler::GroupDone}
};
const Group root1 = constructDoneWorkflow(WorkflowPolicy::StopOnError);
QTest::newRow("DoneStopOnError") << TestData{storage, root1, log, 1, OnDone::Success};
const Group root2 = constructDoneWorkflow(WorkflowPolicy::ContinueOnError);
QTest::newRow("DoneContinueOnError") << TestData{storage, root2, log, 1, OnDone::Success};
const Group root3 = constructDoneWorkflow(WorkflowPolicy::StopOnDone);
QTest::newRow("DoneStopOnDone") << TestData{storage, root3, log, 1, OnDone::Success};
const Group root4 = constructDoneWorkflow(WorkflowPolicy::ContinueOnDone);
QTest::newRow("DoneContinueOnDone") << TestData{storage, root4, log, 1, OnDone::Success};
const Group root5 = constructDoneWorkflow(WorkflowPolicy::StopOnFinished);
QTest::newRow("DoneStopOnFinished") << TestData{storage, root5, log, 1, OnDone::Success};
const Group root6 = constructDoneWorkflow(WorkflowPolicy::Optional);
QTest::newRow("DoneOptional") << TestData{storage, root6, log, 1, OnDone::Success};
}
{
const auto constructErrorWorkflow = [=](WorkflowPolicy policy) {
return Group {
Storage(storage),
workflowPolicy(policy),
Test(setupFailingTask(1), logDone, logError),
onGroupDone(groupDone(0)),
onGroupError(groupError(0))
};
};
const Log log = {
{1, Handler::Setup},
{1, Handler::Error},
{0, Handler::GroupError}
};
const Log optionalLog = {
{1, Handler::Setup},
{1, Handler::Error},
{0, Handler::GroupDone}
};
const Group root1 = constructErrorWorkflow(WorkflowPolicy::StopOnError);
QTest::newRow("ErrorStopOnError") << TestData{storage, root1, log, 1, OnDone::Failure};
const Group root2 = constructErrorWorkflow(WorkflowPolicy::ContinueOnError);
QTest::newRow("ErrorContinueOnError") << TestData{storage, root2, log, 1, OnDone::Failure};
const Group root3 = constructErrorWorkflow(WorkflowPolicy::StopOnDone);
QTest::newRow("ErrorStopOnDone") << TestData{storage, root3, log, 1, OnDone::Failure};
const Group root4 = constructErrorWorkflow(WorkflowPolicy::ContinueOnDone);
QTest::newRow("ErrorContinueOnDone") << TestData{storage, root4, log, 1, OnDone::Failure};
const Group root5 = constructErrorWorkflow(WorkflowPolicy::StopOnFinished);
QTest::newRow("ErrorStopOnFinished") << TestData{storage, root5, log, 1, OnDone::Failure};
const Group root6 = constructErrorWorkflow(WorkflowPolicy::Optional);
QTest::newRow("ErrorOptional") << TestData{storage, root6, optionalLog, 1, OnDone::Success};
}
{
const Group root = constructSimpleSequence(WorkflowPolicy::StopOnError);
const Log log {