AutoTest: Allow temporarily disabling of functionality

Especially when touching headers included centrally while
refactoring bigger projects the retriggered parsing of
large (depending) parts of the project can become rather
annoying.
Adds an action for immediate disabling parsing and
respectively other test related functions.

Change-Id: I553615cce90bc88d636a4519718887306ee5215b
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Stenger
2023-08-25 14:10:10 +02:00
parent 6b312ad54c
commit e26fee092b
5 changed files with 65 additions and 13 deletions

View File

@@ -8,6 +8,7 @@
namespace Autotest { namespace Autotest {
namespace Constants { namespace Constants {
const char ACTION_DISABLE_TMP[] = "AutoTest.DisableTemp";
const char ACTION_SCAN_ID[] = "AutoTest.ScanAction"; const char ACTION_SCAN_ID[] = "AutoTest.ScanAction";
const char ACTION_RUN_ALL_ID[] = "AutoTest.RunAll"; const char ACTION_RUN_ALL_ID[] = "AutoTest.RunAll";
const char ACTION_RUN_ALL_NODEPLOY_ID[] = "AutoTest.RunAllNoDeploy"; const char ACTION_RUN_ALL_NODEPLOY_ID[] = "AutoTest.RunAllNoDeploy";

View File

@@ -30,6 +30,7 @@
#include <coreplugin/coreconstants.h> #include <coreplugin/coreconstants.h>
#include <coreplugin/icontext.h> #include <coreplugin/icontext.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cplusplus/CppDocument.h> #include <cplusplus/CppDocument.h>
#include <cplusplus/LookupContext.h> #include <cplusplus/LookupContext.h>
@@ -93,6 +94,7 @@ public:
void onRunFailedTriggered(); void onRunFailedTriggered();
void onRunFileTriggered(); void onRunFileTriggered();
void onRunUnderCursorTriggered(TestRunMode mode); void onRunUnderCursorTriggered(TestRunMode mode);
void onDisableTemporarily(bool disable);
TestSettingsPage m_testSettingPage; TestSettingsPage m_testSettingPage;
@@ -254,12 +256,25 @@ void AutotestPluginPrivate::initializeMenuEntries()
action->setEnabled(false); action->setEnabled(false);
menu->addAction(command); 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); action = new QAction(Tr::tr("Re&scan Tests"), this);
command = ActionManager::registerAction(action, Constants::ACTION_SCAN_ID); command = ActionManager::registerAction(action, Constants::ACTION_SCAN_ID);
command->setDefaultKeySequence( command->setDefaultKeySequence(
QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+S") : Tr::tr("Alt+Shift+T,Alt+S"))); 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); menu->addAction(command);
ActionContainer *toolsMenu = ActionManager::actionContainer(Core::Constants::M_TOOLS); ActionContainer *toolsMenu = ActionManager::actionContainer(Core::Constants::M_TOOLS);
@@ -333,7 +348,7 @@ void AutotestPlugin::extensionsInitialized()
ExtensionSystem::IPlugin::ShutdownFlag AutotestPlugin::aboutToShutdown() ExtensionSystem::IPlugin::ShutdownFlag AutotestPlugin::aboutToShutdown()
{ {
dd->m_testCodeParser.aboutToShutdown(); dd->m_testCodeParser.aboutToShutdown(true);
dd->m_testTreeModel.disconnect(); dd->m_testTreeModel.disconnect();
return SynchronousShutdown; return SynchronousShutdown;
} }
@@ -467,6 +482,23 @@ void AutotestPluginPrivate::onRunUnderCursorTriggered(TestRunMode mode)
m_testRunner.runTests(mode, testsToRun); 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() TestFrameworks AutotestPlugin::activeTestFrameworks()
{ {
ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
@@ -489,11 +521,12 @@ void AutotestPlugin::updateMenuItemsEnabledState()
{ {
const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
const ProjectExplorer::Target *target = project ? project->activeTarget() : nullptr; const ProjectExplorer::Target *target = project ? project->activeTarget() : nullptr;
const bool canScan = !dd->m_testRunner.isTestRunning() const bool disabled = dd->m_testCodeParser.state() == TestCodeParser::DisabledTemporarily;
&& dd->m_testCodeParser.state() == TestCodeParser::Idle; const bool canScan = disabled || (!dd->m_testRunner.isTestRunning()
&& dd->m_testCodeParser.state() == TestCodeParser::Idle);
const bool hasTests = dd->m_testTreeModel.hasTests(); const bool hasTests = dd->m_testTreeModel.hasTests();
// avoid expensive call to PE::canRunStartupProject() - limit to minimum necessary checks // 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() && project && !project->needsConfiguration()
&& target && target->activeRunConfiguration() && target && target->activeRunConfiguration()
&& !ProjectExplorer::BuildManager::isBuilding(); && !ProjectExplorer::BuildManager::isBuilding();

View File

@@ -64,6 +64,12 @@ void TestCodeParser::setState(State state)
if (m_parserState == Shutdown) if (m_parserState == Shutdown)
return; return;
qCDebug(LOG) << "setState(" << state << "), currentState:" << m_parserState; 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 // avoid triggering parse before code model parsing has finished, but mark as dirty
if (isProjectParsing() || m_codeModelParsing) { if (isProjectParsing() || m_codeModelParsing) {
m_dirty = true; m_dirty = true;
@@ -202,12 +208,15 @@ void TestCodeParser::onProjectPartsUpdated(Project *project)
emitUpdateTestTree(); emitUpdateTestTree();
} }
void TestCodeParser::aboutToShutdown() void TestCodeParser::aboutToShutdown(bool isFinal)
{ {
qCDebug(LOG) << "Disabling (immediately) - shutting down"; qCDebug(LOG) << "Disabling (immediately) -"
m_parserState = Shutdown; << (isFinal ? "shutting down" : "disabled temporarily");
m_parserState = isFinal ? Shutdown : DisabledTemporarily;
m_taskTree.reset(); m_taskTree.reset();
m_futureSynchronizer.waitForFinished(); m_futureSynchronizer.waitForFinished();
if (!isFinal)
onFinished(false);
} }
bool TestCodeParser::postponed(const QSet<FilePath> &filePaths) bool TestCodeParser::postponed(const QSet<FilePath> &filePaths)
@@ -258,6 +267,7 @@ bool TestCodeParser::postponed(const QSet<FilePath> &filePaths)
} }
return true; return true;
case Shutdown: case Shutdown:
case DisabledTemporarily:
break; break;
} }
QTC_ASSERT(false, return false); // should not happen at all QTC_ASSERT(false, return false); // should not happen at all
@@ -277,7 +287,7 @@ static void parseFileForTests(QPromise<TestParseResultPtr> &promise,
void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths, void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
const QList<ITestParser *> &parsers) const QList<ITestParser *> &parsers)
{ {
if (m_parserState == Shutdown || m_testCodeParsers.isEmpty()) if (m_parserState == Shutdown || m_parserState == DisabledTemporarily || m_testCodeParsers.isEmpty())
return; return;
if (postponed(filePaths)) if (postponed(filePaths))
@@ -419,6 +429,7 @@ void TestCodeParser::onAllTasksFinished(Id type)
m_codeModelParsing = false; m_codeModelParsing = false;
// avoid illegal parser state if respective widgets became hidden while parsing // avoid illegal parser state if respective widgets became hidden while parsing
if (m_parserState != DisabledTemporarily)
setState(Idle); setState(Idle);
} }
@@ -451,6 +462,10 @@ void TestCodeParser::onFinished(bool success)
case Shutdown: case Shutdown:
qCDebug(LOG) << "Shutdown complete - not emitting parsingFinished (onFinished)"; qCDebug(LOG) << "Shutdown complete - not emitting parsingFinished (onFinished)";
break; break;
case DisabledTemporarily:
qCDebug(LOG) << "Disabling complete - emitting parsingFinished";
emit parsingFinished(); // ensure hidden progress indicator
break;
default: default:
qWarning("I should not be here... State: %d", m_parserState); qWarning("I should not be here... State: %d", m_parserState);
break; break;

View File

@@ -31,7 +31,8 @@ public:
Idle, Idle,
PartialParse, PartialParse,
FullParse, FullParse,
Shutdown Shutdown,
DisabledTemporarily
}; };
TestCodeParser(); TestCodeParser();
@@ -63,7 +64,7 @@ public:
void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document); void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document);
void onStartupProjectChanged(ProjectExplorer::Project *project); void onStartupProjectChanged(ProjectExplorer::Project *project);
void onProjectPartsUpdated(ProjectExplorer::Project *project); void onProjectPartsUpdated(ProjectExplorer::Project *project);
void aboutToShutdown(); void aboutToShutdown(bool isFinal);
private: private:
bool postponed(const QSet<Utils::FilePath> &fileList); bool postponed(const QSet<Utils::FilePath> &fileList);

View File

@@ -190,8 +190,8 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event)
QAction *runSelectedNoDeploy = ActionManager::command(Constants::ACTION_RUN_SELECTED_NODEPLOY_ID)->action(); QAction *runSelectedNoDeploy = ActionManager::command(Constants::ACTION_RUN_SELECTED_NODEPLOY_ID)->action();
QAction *selectAll = new QAction(Tr::tr("Select All"), &menu); QAction *selectAll = new QAction(Tr::tr("Select All"), &menu);
QAction *deselectAll = new QAction(Tr::tr("Deselect All"), &menu); QAction *deselectAll = new QAction(Tr::tr("Deselect All"), &menu);
// TODO remove?
QAction *rescan = ActionManager::command(Constants::ACTION_SCAN_ID)->action(); 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(selectAll, &QAction::triggered, m_view, &TestTreeView::selectAll);
connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll); connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll);
@@ -216,6 +216,8 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event)
menu.addAction(deselectAll); menu.addAction(deselectAll);
menu.addSeparator(); menu.addSeparator();
menu.addAction(rescan); menu.addAction(rescan);
menu.addSeparator();
menu.addAction(disable);
menu.exec(mapToGlobal(event->pos())); menu.exec(mapToGlobal(event->pos()));
} }