diff --git a/src/plugins/autotest/autotestconstants.h b/src/plugins/autotest/autotestconstants.h index 007e81b00ee..0610216bbda 100644 --- a/src/plugins/autotest/autotestconstants.h +++ b/src/plugins/autotest/autotestconstants.h @@ -8,6 +8,7 @@ namespace Autotest { namespace Constants { +const char ACTION_DISABLE_TMP[] = "AutoTest.DisableTemp"; const char ACTION_SCAN_ID[] = "AutoTest.ScanAction"; const char ACTION_RUN_ALL_ID[] = "AutoTest.RunAll"; const char ACTION_RUN_ALL_NODEPLOY_ID[] = "AutoTest.RunAllNoDeploy"; diff --git a/src/plugins/autotest/autotestplugin.cpp b/src/plugins/autotest/autotestplugin.cpp index b3fccb48d20..edfac7559a4 100644 --- a/src/plugins/autotest/autotestplugin.cpp +++ b/src/plugins/autotest/autotestplugin.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -93,6 +94,7 @@ public: void onRunFailedTriggered(); void onRunFileTriggered(); void onRunUnderCursorTriggered(TestRunMode mode); + void onDisableTemporarily(bool disable); TestSettingsPage m_testSettingPage; @@ -254,12 +256,25 @@ void AutotestPluginPrivate::initializeMenuEntries() action->setEnabled(false); menu->addAction(command); + action = new QAction(Tr::tr("Disable Temporarily"), this); + action->setToolTip(Tr::tr("Disable scanning and other actions until explicitly rescanning, " + "re-enabling, or restarting Qt Creator.")); + action->setCheckable(true); + command = ActionManager::registerAction(action, Constants::ACTION_DISABLE_TMP); + connect(action, &QAction::triggered, this, &AutotestPluginPrivate::onDisableTemporarily); + menu->addAction(command); + action = new QAction(Tr::tr("Re&scan Tests"), this); command = ActionManager::registerAction(action, Constants::ACTION_SCAN_ID); command->setDefaultKeySequence( QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+S") : Tr::tr("Alt+Shift+T,Alt+S"))); - connect(action, &QAction::triggered, this, [] { dd->m_testCodeParser.updateTestTree(); }); + connect(action, &QAction::triggered, this, [] { + if (dd->m_testCodeParser.state() == TestCodeParser::DisabledTemporarily) + dd->onDisableTemporarily(false); // Rescan Test should explicitly re-enable + else + dd->m_testCodeParser.updateTestTree(); + }); menu->addAction(command); ActionContainer *toolsMenu = ActionManager::actionContainer(Core::Constants::M_TOOLS); @@ -333,7 +348,7 @@ void AutotestPlugin::extensionsInitialized() ExtensionSystem::IPlugin::ShutdownFlag AutotestPlugin::aboutToShutdown() { - dd->m_testCodeParser.aboutToShutdown(); + dd->m_testCodeParser.aboutToShutdown(true); dd->m_testTreeModel.disconnect(); return SynchronousShutdown; } @@ -467,6 +482,23 @@ void AutotestPluginPrivate::onRunUnderCursorTriggered(TestRunMode mode) m_testRunner.runTests(mode, testsToRun); } +void AutotestPluginPrivate::onDisableTemporarily(bool disable) +{ + if (disable) { + // cancel running parse + m_testCodeParser.aboutToShutdown(false); + // clear model + m_testTreeModel.removeAllTestItems(); + m_testTreeModel.removeAllTestToolItems(); + AutotestPlugin::updateMenuItemsEnabledState(); + } else { + // re-enable + m_testCodeParser.setState(TestCodeParser::Idle); + // trigger scan + m_testCodeParser.updateTestTree(); + } +} + TestFrameworks AutotestPlugin::activeTestFrameworks() { ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); @@ -489,11 +521,12 @@ void AutotestPlugin::updateMenuItemsEnabledState() { const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); const ProjectExplorer::Target *target = project ? project->activeTarget() : nullptr; - const bool canScan = !dd->m_testRunner.isTestRunning() - && dd->m_testCodeParser.state() == TestCodeParser::Idle; + const bool disabled = dd->m_testCodeParser.state() == TestCodeParser::DisabledTemporarily; + const bool canScan = disabled || (!dd->m_testRunner.isTestRunning() + && dd->m_testCodeParser.state() == TestCodeParser::Idle); const bool hasTests = dd->m_testTreeModel.hasTests(); // avoid expensive call to PE::canRunStartupProject() - limit to minimum necessary checks - const bool canRun = hasTests && canScan + const bool canRun = !disabled && hasTests && canScan && project && !project->needsConfiguration() && target && target->activeRunConfiguration() && !ProjectExplorer::BuildManager::isBuilding(); diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index 840ae613dc7..cfcd740f04f 100644 --- a/src/plugins/autotest/testcodeparser.cpp +++ b/src/plugins/autotest/testcodeparser.cpp @@ -64,6 +64,12 @@ void TestCodeParser::setState(State state) if (m_parserState == Shutdown) return; qCDebug(LOG) << "setState(" << state << "), currentState:" << m_parserState; + if (m_parserState == DisabledTemporarily && state == Idle) { + m_parserState = Idle; + qCDebug(LOG) << "Just re-enabling parser."; + return; + } + // avoid triggering parse before code model parsing has finished, but mark as dirty if (isProjectParsing() || m_codeModelParsing) { m_dirty = true; @@ -202,12 +208,15 @@ void TestCodeParser::onProjectPartsUpdated(Project *project) emitUpdateTestTree(); } -void TestCodeParser::aboutToShutdown() +void TestCodeParser::aboutToShutdown(bool isFinal) { - qCDebug(LOG) << "Disabling (immediately) - shutting down"; - m_parserState = Shutdown; + qCDebug(LOG) << "Disabling (immediately) -" + << (isFinal ? "shutting down" : "disabled temporarily"); + m_parserState = isFinal ? Shutdown : DisabledTemporarily; m_taskTree.reset(); m_futureSynchronizer.waitForFinished(); + if (!isFinal) + onFinished(false); } bool TestCodeParser::postponed(const QSet &filePaths) @@ -258,6 +267,7 @@ bool TestCodeParser::postponed(const QSet &filePaths) } return true; case Shutdown: + case DisabledTemporarily: break; } QTC_ASSERT(false, return false); // should not happen at all @@ -277,7 +287,7 @@ static void parseFileForTests(QPromise &promise, void TestCodeParser::scanForTests(const QSet &filePaths, const QList &parsers) { - if (m_parserState == Shutdown || m_testCodeParsers.isEmpty()) + if (m_parserState == Shutdown || m_parserState == DisabledTemporarily || m_testCodeParsers.isEmpty()) return; if (postponed(filePaths)) @@ -419,7 +429,8 @@ void TestCodeParser::onAllTasksFinished(Id type) m_codeModelParsing = false; // avoid illegal parser state if respective widgets became hidden while parsing - setState(Idle); + if (m_parserState != DisabledTemporarily) + setState(Idle); } void TestCodeParser::onFinished(bool success) @@ -451,6 +462,10 @@ void TestCodeParser::onFinished(bool success) case Shutdown: qCDebug(LOG) << "Shutdown complete - not emitting parsingFinished (onFinished)"; break; + case DisabledTemporarily: + qCDebug(LOG) << "Disabling complete - emitting parsingFinished"; + emit parsingFinished(); // ensure hidden progress indicator + break; default: qWarning("I should not be here... State: %d", m_parserState); break; diff --git a/src/plugins/autotest/testcodeparser.h b/src/plugins/autotest/testcodeparser.h index 2dd81d93d60..49e123ba496 100644 --- a/src/plugins/autotest/testcodeparser.h +++ b/src/plugins/autotest/testcodeparser.h @@ -31,7 +31,8 @@ public: Idle, PartialParse, FullParse, - Shutdown + Shutdown, + DisabledTemporarily }; TestCodeParser(); @@ -63,7 +64,7 @@ public: void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document); void onStartupProjectChanged(ProjectExplorer::Project *project); void onProjectPartsUpdated(ProjectExplorer::Project *project); - void aboutToShutdown(); + void aboutToShutdown(bool isFinal); private: bool postponed(const QSet &fileList); diff --git a/src/plugins/autotest/testnavigationwidget.cpp b/src/plugins/autotest/testnavigationwidget.cpp index d7f26e655fa..1908af8203b 100644 --- a/src/plugins/autotest/testnavigationwidget.cpp +++ b/src/plugins/autotest/testnavigationwidget.cpp @@ -190,8 +190,8 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event) QAction *runSelectedNoDeploy = ActionManager::command(Constants::ACTION_RUN_SELECTED_NODEPLOY_ID)->action(); QAction *selectAll = new QAction(Tr::tr("Select All"), &menu); QAction *deselectAll = new QAction(Tr::tr("Deselect All"), &menu); - // TODO remove? QAction *rescan = ActionManager::command(Constants::ACTION_SCAN_ID)->action(); + QAction *disable = ActionManager::command(Constants::ACTION_DISABLE_TMP)->action(); connect(selectAll, &QAction::triggered, m_view, &TestTreeView::selectAll); connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll); @@ -216,6 +216,8 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event) menu.addAction(deselectAll); menu.addSeparator(); menu.addAction(rescan); + menu.addSeparator(); + menu.addAction(disable); menu.exec(mapToGlobal(event->pos())); }