TaskTree: Rename StopWithDone -> StopWithSuccess

Make naming consistent with recent changes.
"Done" is meant to be an event name when the task / group
finishes. "Done" may finish with "Success" or an "Error".

This addresses the 26th point in the task below.

Task-number: QTCREATORBUG-28741
Change-Id: I53ed6905b1c385c398f49e122e8ca60aa3ad0806
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2023-11-04 12:57:23 +01:00
parent 2c0a59384c
commit 4c38f68d0f
29 changed files with 64 additions and 64 deletions

View File

@@ -82,7 +82,7 @@ GroupItem waitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
Barrier *activeSharedBarrier = activeBarrier->barrier();
const std::optional<bool> result = activeSharedBarrier->result();
if (result.has_value())
return result.value() ? SetupResult::StopWithDone : SetupResult::StopWithError;
return result.value() ? SetupResult::StopWithSuccess : SetupResult::StopWithError;
QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult);
return SetupResult::Continue;
});

View File

@@ -417,7 +417,7 @@ private:
const auto onFirstSetup = [input](ConcurrentCall<void> &task) {
if (input == "Skip")
return SetupResult::StopWithDone; // This task won't start, the next one will
return SetupResult::StopWithSuccess; // This task won't start, the next one will
if (input == "Error")
return SetupResult::StopWithError; // This task and the next one won't start
task.setConcurrentCallData(parseAndLog, input);
@@ -439,10 +439,10 @@ private:
the return value of the handler instructs the running tree on how to proceed after
the handler's invocation is finished. The default return value of SetupResult::Continue
instructs the tree to continue running, i.e. to execute the associated \c Task.
The return value of SetupResult::StopWithDone or SetupResult::StopWithError instructs
The return value of SetupResult::StopWithSuccess or SetupResult::StopWithError instructs
the tree to skip the task's execution and finish immediately with success or an error,
respectively.
When the return type is either SetupResult::StopWithDone or SetupResult::StopWithError,
When the return type is either SetupResult::StopWithSuccess or SetupResult::StopWithError,
the task's \a done or \a error handler (even if provided) are not called afterwards.
The \a setup handler may be of a shortened form of std::function<void(Task &)>,
@@ -647,7 +647,7 @@ private:
Default. The group's or task's execution continues normally.
When a group's or task's setup handler returns void, it's assumed that
it returned Continue.
\value StopWithDone
\value StopWithSuccess
The group's or task's execution stops immediately with success.
When returned from the group's setup handler, all child tasks are skipped,
and the group's onGroupDone() handler is invoked (if provided).
@@ -675,15 +675,15 @@ private:
The return value of the handler instructs the running group on how to proceed
after the handler's invocation is finished. The default return value of SetupResult::Continue
instructs the group to continue running, i.e. to start executing its child tasks.
The return value of SetupResult::StopWithDone or SetupResult::StopWithError
The return value of SetupResult::StopWithSuccess or SetupResult::StopWithError
instructs the group to skip the child tasks' execution and finish immediately with
success or an error, respectively.
When the return type is either SetupResult::StopWithDone
When the return type is either SetupResult::StopWithSuccess
of SetupResult::StopWithError, the group's done or error handler (if provided)
is called synchronously immediately afterwards.
\note Even if the group setup handler returns StopWithDone or StopWithError,
\note Even if the group setup handler returns StopWithSuccess or StopWithError,
one of the group's done or error handlers is invoked. This behavior differs
from that of task handlers and might change in the future.
@@ -834,7 +834,7 @@ const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndE
static SetupResult toSetupResult(bool success)
{
return success ? SetupResult::StopWithDone : SetupResult::StopWithError;
return success ? SetupResult::StopWithSuccess : SetupResult::StopWithError;
}
bool TreeStorageBase::isValid() const
@@ -1312,7 +1312,7 @@ SetupResult TaskContainer::start()
if (startAction != SetupResult::Continue) {
m_constData.m_taskTreePrivate->advanceProgress(m_constData.m_taskCount);
// Non-Continue SetupResult takes precedence over the workflow policy.
m_runtimeData->m_successBit = startAction == SetupResult::StopWithDone;
m_runtimeData->m_successBit = startAction == SetupResult::StopWithSuccess;
}
}
if (startAction == SetupResult::Continue) {
@@ -1328,7 +1328,7 @@ SetupResult TaskContainer::continueStart(SetupResult startAction, int nextChild)
: startAction;
QTC_CHECK(isRunning()); // TODO: superfluous
if (groupAction != SetupResult::Continue) {
const bool bit = m_runtimeData->updateSuccessBit(groupAction == SetupResult::StopWithDone);
const bool bit = m_runtimeData->updateSuccessBit(groupAction == SetupResult::StopWithSuccess);
const bool result = invokeDoneHandler(bit ? DoneWith::Success : DoneWith::Error);
if (TaskContainer *parentContainer = m_constData.m_parentContainer) {
QTC_CHECK(parentContainer->isRunning());
@@ -1354,7 +1354,7 @@ SetupResult TaskContainer::startChildren(int nextChild)
if (startAction == SetupResult::Continue)
continue;
const SetupResult finalizeAction = childDone(startAction == SetupResult::StopWithDone);
const SetupResult finalizeAction = childDone(startAction == SetupResult::StopWithSuccess);
if (finalizeAction == SetupResult::Continue)
continue;
@@ -1699,7 +1699,7 @@ bool TaskNode::invokeDoneHandler(DoneWith result)
setup handler doesn't return SetupResult (that is, its return type is
void).
\row
\li StopWithDone
\li StopWithSuccess
\li The task won't be started and it will report success to its parent.
\row
\li StopWithError
@@ -1739,7 +1739,7 @@ bool TaskNode::invokeDoneHandler(DoneWith result)
The error handler is also optional. When used, it must always be the third argument.
You can omit the handlers or substitute the ones that you do not need with curly braces ({}).
\note If the task setup handler returns StopWithDone or StopWithError,
\note If the task setup handler returns StopWithSuccess or StopWithError,
neither the done nor error handler is invoked.
\section1 Group Handlers
@@ -1773,7 +1773,7 @@ bool TaskNode::invokeDoneHandler(DoneWith result)
whole group. If you do not specify a group start handler or its return type
is void, the default group's action is SetupResult::Continue, so that all
tasks are started normally. Otherwise, when the start handler returns
SetupResult::StopWithDone or SetupResult::StopWithError, the tasks are not
SetupResult::StopWithSuccess or SetupResult::StopWithError, the tasks are not
started (they are skipped) and the group itself reports success or failure,
depending on the returned value, respectively.
@@ -1785,7 +1785,7 @@ bool TaskNode::invokeDoneHandler(DoneWith result)
ProcessTask(...) // Process 1
},
Group {
onGroupSetup([] { qDebug() << "Group 2 setup"; return SetupResult::StopWithDone; }),
onGroupSetup([] { qDebug() << "Group 2 setup"; return SetupResult::StopWithSuccess; }),
ProcessTask(...) // Process 2
},
Group {
@@ -1823,7 +1823,7 @@ bool TaskNode::invokeDoneHandler(DoneWith result)
\li
\row
\li Group 2 starts
\li Returns StopWithDone, so Process 2 is skipped and Group 2 reports
\li Returns StopWithSuccess, so Process 2 is skipped and Group 2 reports
success.
\row
\li Group 2 finishes (success)
@@ -1856,7 +1856,7 @@ bool TaskNode::invokeDoneHandler(DoneWith result)
onGroupSetup([] { qDebug() << "Root setup"; }),
ProcessTask(...),
onGroupDone([] { qDebug() << "Root finished with success"; }),
onGroupError([] { qDebug() << "Root finished with error"; })
onGroupError([] { qDebug() << "Root finished with an error"; })
};
\endcode
@@ -1864,7 +1864,7 @@ bool TaskNode::invokeDoneHandler(DoneWith result)
onGroupDone() or onGroupError() each to a group, an assert is triggered at
runtime that includes an error message.
\note Even if the group setup handler returns StopWithDone or StopWithError,
\note Even if the group setup handler returns StopWithSuccess or StopWithError,
one of the group's done or error handlers is invoked. This behavior differs
from that of task handlers and might change in the future.

View File

@@ -127,7 +127,7 @@ Q_ENUM_NS(WorkflowPolicy);
enum class SetupResult
{
Continue,
StopWithDone,
StopWithSuccess,
StopWithError
};
Q_ENUM_NS(SetupResult);
@@ -352,10 +352,10 @@ private:
static_assert(isBool || isVoid,
"Sync element: The synchronous function has to return void or bool.");
if constexpr (isBool) {
return onGroupSetup([function] { return function() ? SetupResult::StopWithDone
return onGroupSetup([function] { return function() ? SetupResult::StopWithSuccess
: SetupResult::StopWithError; });
}
return onGroupSetup([function] { function(); return SetupResult::StopWithDone; });
return onGroupSetup([function] { function(); return SetupResult::StopWithSuccess; });
};
};

View File

@@ -814,12 +814,12 @@ Tasking::GroupItem AndroidBuildApkStep::runRecipe()
if (m_skipBuilding) {
reportWarningOrError(Tr::tr("Android deploy settings file not found, "
"not building an APK."), Task::Error);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
if (AndroidManager::skipInstallationAndPackageSteps(target())) {
reportWarningOrError(Tr::tr("Product type is not an application, not building an APK."),
Task::Warning);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
if (setupHelper())
return SetupResult::Continue;

View File

@@ -128,7 +128,7 @@ Tasking::GroupItem AndroidPackageInstallationStep::runRecipe()
if (AndroidManager::skipInstallationAndPackageSteps(target())) {
reportWarningOrError(Tr::tr("Product type is not an application, not running the "
"Make install step."), Task::Warning);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
for (const QString &dir : std::as_const(m_androidDirsToClean)) {

View File

@@ -355,11 +355,11 @@ void TestRunner::runTestsHelper()
const auto onSetup = [this, config] {
if (!config->project())
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
if (config->testExecutable().isEmpty()) {
reportResult(ResultType::MessageFatal,
Tr::tr("Executable path is empty. (%1)").arg(config->displayName()));
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
return SetupResult::Continue;
};

View File

@@ -90,7 +90,7 @@ Tasking::GroupItem AutogenStep::runRecipe()
if (!m_runAutogen) {
emit addOutput(Tr::tr("Configuration unchanged, skipping autogen step."),
OutputFormat::NormalMessage);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
return SetupResult::Continue;
};

View File

@@ -75,7 +75,7 @@ private:
emit addOutput(::AutotoolsProjectManager::Tr::tr(
"Configuration unchanged, skipping autoreconf step."),
OutputFormat::NormalMessage);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
return SetupResult::Continue;
};

View File

@@ -88,7 +88,7 @@ Tasking::GroupItem ConfigureStep::runRecipe()
if (!m_runConfigure) {
emit addOutput(Tr::tr("Configuration unchanged, skipping configure step."), OutputFormat::NormalMessage);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
ProcessParameters *param = processParameters();

View File

@@ -360,7 +360,7 @@ GroupItem CMakeBuildStep::runRecipe()
else if (bs->isWaitingForParse())
message = Tr::tr("Running CMake in preparation to build...");
else
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
emit addOutput(message, OutputFormat::NormalMessage);
parseTarget = target();
return SetupResult::Continue;

View File

@@ -190,7 +190,7 @@ LocatorMatcherTasks ActionsFilter::matchers()
collectEntriesForCommands();
if (storage->input().simplified().isEmpty()) {
storage->reportOutput(m_entries);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(matches, *storage, m_entries);

View File

@@ -86,7 +86,7 @@ DirectoryFilter::DirectoryFilter(Id id)
if (!m_directories.isEmpty())
return SetupResult::Continue; // Async task will run
m_cache.setFilePaths({});
return SetupResult::StopWithDone; // Group stops, skips async task
return SetupResult::StopWithSuccess; // Group stops, skips async task
};
const auto onSetup = [this](Async<FilePaths> &async) {
async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());

View File

@@ -1488,10 +1488,10 @@ LocatorMatcherTask LocatorFileCache::matcher() const
const auto onSetup = [storage, weak](Async<LocatorFileCachePrivate> &async) {
auto that = weak.lock();
if (!that) // LocatorMatcher is running after *this LocatorFileCache was destructed.
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
if (!that->ensureValidated())
return SetupResult::StopWithDone; // The cache is invalid and
return SetupResult::StopWithSuccess; // The cache is invalid and
// no provider is set or it returned empty generator
that->bumpExecutionId();

View File

@@ -385,7 +385,7 @@ LocatorMatcherTasks JavaScriptFilter::matchers()
return AcceptResult();
};
storage->reportOutput({entry});
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
return SetupResult::Continue;
};

View File

@@ -190,7 +190,7 @@ LocatorMatcherTasks SpotlightLocatorFilter::matchers()
const Link link = Link::fromString(storage->input(), true);
const FilePath input = link.targetFilePath;
if (input.isEmpty())
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
// only pass the file name part to allow searches like "somepath/*foo"
const std::unique_ptr<MacroExpander> expander(createMacroExpander(input.fileName()));

View File

@@ -414,7 +414,7 @@ ShowController::ShowController(IDocument *document, const QString &id)
const auto desciptionDetailsSetup = [storage] {
if (!storage->m_postProcessDescription)
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
return SetupResult::Continue;
};

View File

@@ -69,7 +69,7 @@ LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount,
const auto onFilterSetup = [storage, resultStorage, client, filter](Async<void> &async) {
const QList<SymbolInformation> results = *resultStorage;
if (results.isEmpty())
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
async.setConcurrentCallData(filterResults, *storage, client, results, filter);
return SetupResult::Continue;

View File

@@ -65,7 +65,7 @@ Tasking::GroupItem PySideBuildStep::runRecipe()
const auto onSetup = [this] {
if (!processParameters()->effectiveCommand().isExecutableFile())
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
return SetupResult::Continue;
};

View File

@@ -206,7 +206,7 @@ Tasking::GroupItem QmakeMakeStep::runRecipe()
const auto onSetup = [this] {
if (m_scriptTarget || m_ignoredNonTopLevelBuild)
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
if (!m_makeFileToCheck.exists()) {
const bool success = ignoreReturnValue();
@@ -214,7 +214,7 @@ Tasking::GroupItem QmakeMakeStep::runRecipe()
emit addOutput(Tr::tr("Cannot find Makefile. Check your build settings."),
OutputFormat::NormalMessage);
}
return success ? SetupResult::StopWithDone : SetupResult::StopWithError;
return success ? SetupResult::StopWithSuccess : SetupResult::StopWithError;
}
return SetupResult::Continue;
};

View File

@@ -267,12 +267,12 @@ Tasking::GroupItem QMakeStep::runRecipe()
const auto onSetup = [this] {
if (m_scriptTemplate)
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
if (m_needToRunQMake)
return SetupResult::Continue;
emit addOutput(Tr::tr("Configuration unchanged, skipping qmake step."),
OutputFormat::NormalMessage);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
};
const auto onQMakeSetup = [this](Process &process) {

View File

@@ -149,7 +149,7 @@ GroupItem QnxDeployQtLibrariesDialogPrivate::removeDirTask()
{
const auto onSetup = [this](Process &process) {
if (m_checkResult != CheckResult::RemoveDir)
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
m_deployLogWindow->appendPlainText(Tr::tr("Removing \"%1\"").arg(fullRemoteDirectory()));
process.setCommand({m_device->filePath("rm"), {"-rf", fullRemoteDirectory()}});
return SetupResult::Continue;
@@ -167,7 +167,7 @@ GroupItem QnxDeployQtLibrariesDialogPrivate::uploadTask()
const auto onSetup = [this](FileTransfer &transfer) {
if (m_deployableFiles.isEmpty()) {
emitProgressMessage(Tr::tr("No files need to be uploaded."));
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
emitProgressMessage(Tr::tr("%n file(s) need to be uploaded.", "",
m_deployableFiles.size()));
@@ -183,7 +183,7 @@ GroupItem QnxDeployQtLibrariesDialogPrivate::uploadTask()
}
if (files.isEmpty()) {
emitProgressMessage(Tr::tr("No files need to be uploaded."));
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
transfer.setFilesToTransfer(files);
QObject::connect(&transfer, &FileTransfer::progress,
@@ -250,7 +250,7 @@ Group QnxDeployQtLibrariesDialogPrivate::deployRecipe()
return SetupResult::Continue;
emitProgressMessage(Tr::tr("No deployment action necessary. Skipping."));
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
};
const auto doneHandler = [this] {
emitProgressMessage(Tr::tr("All files successfully deployed."));

View File

@@ -191,7 +191,7 @@ GroupItem GenericDeployStep::deployRecipe()
}
if (files.isEmpty()) {
addSkipDeploymentMessage();
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
return SetupResult::Continue;
};

View File

@@ -169,7 +169,7 @@ GroupItem GenericDirectUploadStep::uploadTask(const TreeStorage<UploadStorage> &
const auto onSetup = [this, storage](FileTransfer &transfer) {
if (storage->filesToUpload.isEmpty()) {
addProgressMessage(Tr::tr("No files need to be uploaded."));
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
addProgressMessage(Tr::tr("%n file(s) need to be uploaded.", "",
storage->filesToUpload.size()));
@@ -190,7 +190,7 @@ GroupItem GenericDirectUploadStep::uploadTask(const TreeStorage<UploadStorage> &
}
if (files.isEmpty()) {
addProgressMessage(Tr::tr("No files need to be uploaded."));
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
transfer.setFilesToTransfer(files);
QObject::connect(&transfer, &FileTransfer::progress,
@@ -254,7 +254,7 @@ GroupItem GenericDirectUploadStep::deployRecipe()
QTC_CHECK(collected.size() >= deployableFiles.size());
if (collected.isEmpty()) {
addSkipDeploymentMessage();
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
storage->deployableFiles = collected;
return SetupResult::Continue;

View File

@@ -48,7 +48,7 @@ GroupItem KillAppStep::deployRecipe()
const auto onSetup = [this](DeviceProcessKiller &killer) {
if (m_remoteExecutable.isEmpty()) {
addSkipDeploymentMessage();
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
killer.setProcessPath(m_remoteExecutable);
addProgressMessage(Tr::tr("Trying to kill \"%1\" on remote device...")

View File

@@ -145,7 +145,7 @@ Tasking::GroupItem TarPackageCreationStep::runRecipe()
if (!m_packagingNeeded) {
emit addOutput(Tr::tr("Tarball up to date, skipping packaging."),
OutputFormat::NormalMessage);
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
}
async.setConcurrentCallData(&TarPackageCreationStep::doPackage, this,

View File

@@ -115,7 +115,7 @@ GroupItem TarPackageDeployStep::deployRecipe()
if (hasLocalFileChanged(DeployableFile(m_packageFilePath, {})))
return SetupResult::Continue;
addSkipDeploymentMessage();
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
};
return Group { onGroupSetup(onSetup), uploadTask(), installTask() };
}

View File

@@ -167,7 +167,7 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
const auto onDescriptionSetup = [this](Process &process) {
if (m_changeNumber == 0)
return SetupResult::StopWithDone;
return SetupResult::StopWithSuccess;
setupCommand(process, {"log", "-r", QString::number(m_changeNumber)});
CommandLine command = process.commandLine();
command << SubversionClient::AddAuthOptions();

View File

@@ -181,7 +181,7 @@ Group ValgrindProcessPrivate::runRecipe() const
};
const auto onParserGroupSetup = [this] {
return m_localServerAddress.isNull() ? SetupResult::StopWithDone : SetupResult::Continue;
return m_localServerAddress.isNull() ? SetupResult::StopWithSuccess : SetupResult::Continue;
};
const auto onParserSetup = [this, storage](Parser &parser) {

View File

@@ -305,7 +305,7 @@ void tst_Tasking::testTree_data()
};
const Group root3 {
Storage(storage),
onGroupSetup([] { return SetupResult::StopWithDone; }),
onGroupSetup([] { return SetupResult::StopWithSuccess; }),
groupDone(0)
};
const Group root4 {
@@ -334,7 +334,7 @@ void tst_Tasking::testTree_data()
};
const auto doneData = [storage, setupGroup](WorkflowPolicy policy) {
return TestData{storage, setupGroup(SetupResult::StopWithDone, policy),
return TestData{storage, setupGroup(SetupResult::StopWithSuccess, policy),
Log{{0, Handler::GroupSuccess}}, 0, OnDone::Success};
};
const auto errorData = [storage, setupGroup](WorkflowPolicy policy) {
@@ -362,8 +362,8 @@ void tst_Tasking::testTree_data()
{
const Group root {
Storage(storage),
createDynamicTask(1, SetupResult::StopWithDone),
createDynamicTask(2, SetupResult::StopWithDone)
createDynamicTask(1, SetupResult::StopWithSuccess),
createDynamicTask(2, SetupResult::StopWithSuccess)
};
const Log log {{1, Handler::Setup}, {2, Handler::Setup}};
QTest::newRow("DynamicTaskDone") << TestData{storage, root, log, 2, OnDone::Success};
@@ -1288,7 +1288,7 @@ void tst_Tasking::testTree_data()
};
};
const Group root1 = createRoot(SetupResult::StopWithDone);
const Group root1 = createRoot(SetupResult::StopWithSuccess);
const Log log1 {
{1, Handler::Setup},
{1, Handler::Success},
@@ -1371,7 +1371,7 @@ void tst_Tasking::testTree_data()
},
Group {
groupSetup(3),
createDynamicTask(3, SetupResult::StopWithDone)
createDynamicTask(3, SetupResult::StopWithSuccess)
},
Group {
groupSetup(4),
@@ -1596,7 +1596,7 @@ void tst_Tasking::testTree_data()
},
Group {
groupSetup(3),
Group { createDynamicTask(3, SetupResult::StopWithDone) }
Group { createDynamicTask(3, SetupResult::StopWithSuccess) }
},
Group {
groupSetup(4),