forked from qt-creator/qt-creator
AutoTest: Allow limiting scan inside project settings
Enables to limit the scanning for tests and respectively any further action to a list of user defined patterns. If limitation is enabled and any of the filter patterns does match the file will be processed. If no filter pattern matches the file will be ignored. Change-Id: I6a6de8f4137485e83b750997fb3c948dc6e79c68 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
#include "autotestconstants.h"
|
#include "autotestconstants.h"
|
||||||
#include "autotestplugin.h"
|
#include "autotestplugin.h"
|
||||||
#include "autotesttr.h"
|
#include "autotesttr.h"
|
||||||
|
#include "testcodeparser.h"
|
||||||
#include "testprojectsettings.h"
|
#include "testprojectsettings.h"
|
||||||
#include "testtreemodel.h"
|
#include "testtreemodel.h"
|
||||||
|
|
||||||
@@ -13,10 +14,12 @@
|
|||||||
#include <projectexplorer/projectsettingswidget.h>
|
#include <projectexplorer/projectsettingswidget.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/aspects.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QPushButton>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
|
|
||||||
@@ -37,10 +40,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
void populateFrameworks(const QHash<Autotest::ITestFramework *, bool> &frameworks,
|
void populateFrameworks(const QHash<Autotest::ITestFramework *, bool> &frameworks,
|
||||||
const QHash<Autotest::ITestTool *, bool> &testTools);
|
const QHash<Autotest::ITestTool *, bool> &testTools);
|
||||||
|
void populatePathFilters(const QStringList &filters);
|
||||||
void onActiveFrameworkChanged(QTreeWidgetItem *item, int column);
|
void onActiveFrameworkChanged(QTreeWidgetItem *item, int column);
|
||||||
TestProjectSettings *m_projectSettings;
|
TestProjectSettings *m_projectSettings;
|
||||||
QTreeWidget *m_activeFrameworks = nullptr;
|
QTreeWidget *m_activeFrameworks = nullptr;
|
||||||
QComboBox *m_runAfterBuild = nullptr;
|
QComboBox *m_runAfterBuild = nullptr;
|
||||||
|
Utils::BoolAspect m_applyFilter;
|
||||||
|
QTreeWidget *m_pathFilters = nullptr;
|
||||||
QTimer m_syncTimer;
|
QTimer m_syncTimer;
|
||||||
int m_syncType = 0;
|
int m_syncType = 0;
|
||||||
};
|
};
|
||||||
@@ -59,6 +65,14 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
|
|||||||
m_runAfterBuild->addItem(Tr::tr("All"));
|
m_runAfterBuild->addItem(Tr::tr("All"));
|
||||||
m_runAfterBuild->addItem(Tr::tr("Selected"));
|
m_runAfterBuild->addItem(Tr::tr("Selected"));
|
||||||
m_runAfterBuild->setCurrentIndex(int(m_projectSettings->runAfterBuild()));
|
m_runAfterBuild->setCurrentIndex(int(m_projectSettings->runAfterBuild()));
|
||||||
|
m_applyFilter.setToolTip(Tr::tr("Apply path filters before scanning for tests."));
|
||||||
|
m_pathFilters = new QTreeWidget;
|
||||||
|
m_pathFilters->setHeaderHidden(true);
|
||||||
|
m_pathFilters->setRootIsDecorated(false);
|
||||||
|
QLabel *filterLabel = new QLabel(Tr::tr("Wildcard expressions for filtering"), this);
|
||||||
|
QPushButton *addFilter = new QPushButton(Tr::tr("Add"), this);
|
||||||
|
QPushButton *removeFilter = new QPushButton(Tr::tr("Remove"), this);
|
||||||
|
removeFilter->setEnabled(false);
|
||||||
|
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
Column {
|
Column {
|
||||||
@@ -80,6 +94,19 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
|
|||||||
noMargin(),
|
noMargin(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Row { // explicitly outside of the global settings
|
||||||
|
Group {
|
||||||
|
title(Tr::tr("Limit files to path patterns")),
|
||||||
|
m_applyFilter.groupChecker(),
|
||||||
|
Column {
|
||||||
|
filterLabel,
|
||||||
|
Row {
|
||||||
|
Column { m_pathFilters },
|
||||||
|
Column { addFilter, removeFilter, st },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
noMargin(),
|
noMargin(),
|
||||||
}.attachTo(this);
|
}.attachTo(this);
|
||||||
|
|
||||||
@@ -88,7 +115,9 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
|
|||||||
populateFrameworks(m_projectSettings->activeFrameworks(),
|
populateFrameworks(m_projectSettings->activeFrameworks(),
|
||||||
m_projectSettings->activeTestTools());
|
m_projectSettings->activeTestTools());
|
||||||
|
|
||||||
|
populatePathFilters(m_projectSettings->pathFilters());
|
||||||
setUseGlobalSettings(m_projectSettings->useGlobalSettings());
|
setUseGlobalSettings(m_projectSettings->useGlobalSettings());
|
||||||
|
m_applyFilter.setValue(m_projectSettings->limitToFilters());
|
||||||
connect(this, &ProjectSettingsWidget::useGlobalSettingsChanged,
|
connect(this, &ProjectSettingsWidget::useGlobalSettingsChanged,
|
||||||
this, [this, generalWidget](bool useGlobalSettings) {
|
this, [this, generalWidget](bool useGlobalSettings) {
|
||||||
generalWidget->setEnabled(!useGlobalSettings);
|
generalWidget->setEnabled(!useGlobalSettings);
|
||||||
@@ -102,6 +131,56 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
|
|||||||
connect(m_runAfterBuild, &QComboBox::currentIndexChanged, this, [this](int index) {
|
connect(m_runAfterBuild, &QComboBox::currentIndexChanged, this, [this](int index) {
|
||||||
m_projectSettings->setRunAfterBuild(RunAfterBuildMode(index));
|
m_projectSettings->setRunAfterBuild(RunAfterBuildMode(index));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto itemsToStringList = [this] {
|
||||||
|
QStringList items;
|
||||||
|
const QTreeWidgetItem *rootItem = m_pathFilters->invisibleRootItem();
|
||||||
|
for (int i = 0, count = rootItem->childCount(); i < count; ++i) {
|
||||||
|
auto expr = rootItem->child(i)->data(0, Qt::DisplayRole).toString();
|
||||||
|
items.append(expr);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
auto triggerRescan = [this] {
|
||||||
|
TestCodeParser *parser = TestTreeModel::instance()->parser();
|
||||||
|
parser->emitUpdateTestTree();
|
||||||
|
};
|
||||||
|
|
||||||
|
connect(&m_applyFilter, &Utils::BoolAspect::changed,
|
||||||
|
this, [this, triggerRescan] {
|
||||||
|
m_projectSettings->setLimitToFilter(m_applyFilter.value());
|
||||||
|
triggerRescan();
|
||||||
|
});
|
||||||
|
connect(m_pathFilters, &QTreeWidget::itemSelectionChanged,
|
||||||
|
this, [this, removeFilter] {
|
||||||
|
removeFilter->setEnabled(!m_pathFilters->selectedItems().isEmpty());
|
||||||
|
});
|
||||||
|
connect(m_pathFilters->model(), &QAbstractItemModel::dataChanged,
|
||||||
|
this, [this, itemsToStringList, triggerRescan]
|
||||||
|
(const QModelIndex &tl, const QModelIndex &br, const QList<int> &roles) {
|
||||||
|
if (!roles.contains(Qt::DisplayRole))
|
||||||
|
return;
|
||||||
|
if (tl != br)
|
||||||
|
return;
|
||||||
|
m_projectSettings->setPathFilters(itemsToStringList());
|
||||||
|
triggerRescan();
|
||||||
|
});
|
||||||
|
connect(addFilter, &QPushButton::clicked, this, [this] {
|
||||||
|
m_projectSettings->addPathFilter("*");
|
||||||
|
populatePathFilters(m_projectSettings->pathFilters());
|
||||||
|
const QTreeWidgetItem *root = m_pathFilters->invisibleRootItem();
|
||||||
|
QTreeWidgetItem *lastChild = root->child(root->childCount() - 1);
|
||||||
|
const QModelIndex index = m_pathFilters->indexFromItem(lastChild, 0);
|
||||||
|
m_pathFilters->edit(index);
|
||||||
|
});
|
||||||
|
connect(removeFilter, &QPushButton::clicked, this, [this, itemsToStringList, triggerRescan] {
|
||||||
|
const QList<QTreeWidgetItem *> selected = m_pathFilters->selectedItems();
|
||||||
|
QTC_ASSERT(selected.size() == 1, return);
|
||||||
|
m_pathFilters->invisibleRootItem()->removeChild(selected.first());
|
||||||
|
delete selected.first();
|
||||||
|
m_projectSettings->setPathFilters(itemsToStringList());
|
||||||
|
triggerRescan();
|
||||||
|
});
|
||||||
m_syncTimer.setSingleShot(true);
|
m_syncTimer.setSingleShot(true);
|
||||||
connect(&m_syncTimer, &QTimer::timeout, this, [this] {
|
connect(&m_syncTimer, &QTimer::timeout, this, [this] {
|
||||||
auto testTreeModel = TestTreeModel::instance();
|
auto testTreeModel = TestTreeModel::instance();
|
||||||
@@ -136,6 +215,16 @@ void ProjectTestSettingsWidget::populateFrameworks(const QHash<ITestFramework *,
|
|||||||
generateItem(it.key(), it.value());
|
generateItem(it.key(), it.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectTestSettingsWidget::populatePathFilters(const QStringList &filters)
|
||||||
|
{
|
||||||
|
m_pathFilters->clear();
|
||||||
|
for (const QString &filter : filters) {
|
||||||
|
auto item = new QTreeWidgetItem(m_pathFilters, {filter});
|
||||||
|
item->setData(0, Qt::ToolTipRole, filter);
|
||||||
|
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectTestSettingsWidget::onActiveFrameworkChanged(QTreeWidgetItem *item, int column)
|
void ProjectTestSettingsWidget::onActiveFrameworkChanged(QTreeWidgetItem *item, int column)
|
||||||
{
|
{
|
||||||
auto id = Utils::Id::fromSetting(item->data(column, BaseIdRole));
|
auto id = Utils::Id::fromSetting(item->data(column, BaseIdRole));
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
#include "testcodeparser.h"
|
#include "testcodeparser.h"
|
||||||
|
|
||||||
#include "autotestconstants.h"
|
#include "autotestconstants.h"
|
||||||
|
#include "autotestplugin.h"
|
||||||
#include "autotesttr.h"
|
#include "autotesttr.h"
|
||||||
|
#include "testprojectsettings.h"
|
||||||
#include "testsettings.h"
|
#include "testsettings.h"
|
||||||
#include "testtreemodel.h"
|
#include "testtreemodel.h"
|
||||||
|
|
||||||
@@ -336,6 +338,38 @@ void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
|
|||||||
emit requestRemoval(files);
|
emit requestRemoval(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TestProjectSettings *settings = projectSettings(project);
|
||||||
|
if (settings->limitToFilters()) {
|
||||||
|
qCDebug(LOG) << "Applying project path filters - currently" << files.size() << "files";
|
||||||
|
const QStringList filters = settings->pathFilters();
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
// we cannot rely on QRegularExpression::fromWildcard() as we want handle paths
|
||||||
|
const QList<QRegularExpression> regexes
|
||||||
|
= Utils::transform(filters, [] (const QString &filter) {
|
||||||
|
return QRegularExpression(wildcardPatternFromString(filter));
|
||||||
|
});
|
||||||
|
|
||||||
|
files = Utils::filtered(files, [®exes](const FilePath &fn) {
|
||||||
|
for (const QRegularExpression ®ex : regexes) {
|
||||||
|
if (!regex.isValid()) {
|
||||||
|
qCDebug(LOG) << "Skipping invalid pattern? Pattern:" << regex.pattern();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (regex.match(fn.path()).hasMatch())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
qCDebug(LOG) << "After applying filters" << files.size() << "files";
|
||||||
|
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
qCDebug(LOG) << "No filter matched a file - canceling scan immediately";
|
||||||
|
onFinished(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QTC_ASSERT(!(isFullParse && files.isEmpty()), onFinished(true); return);
|
QTC_ASSERT(!(isFullParse && files.isEmpty()), onFinished(true); return);
|
||||||
|
|
||||||
// use only a single parser or all current active?
|
// use only a single parser or all current active?
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace Internal {
|
|||||||
static const char SK_ACTIVE_FRAMEWORKS[] = "AutoTest.ActiveFrameworks";
|
static const char SK_ACTIVE_FRAMEWORKS[] = "AutoTest.ActiveFrameworks";
|
||||||
static const char SK_RUN_AFTER_BUILD[] = "AutoTest.RunAfterBuild";
|
static const char SK_RUN_AFTER_BUILD[] = "AutoTest.RunAfterBuild";
|
||||||
static const char SK_CHECK_STATES[] = "AutoTest.CheckStates";
|
static const char SK_CHECK_STATES[] = "AutoTest.CheckStates";
|
||||||
|
static const char SK_APPLY_FILTER[] = "AutoTest.ApplyFilter";
|
||||||
|
static const char SK_PATH_FILTERS[] = "AutoTest.PathFilters";
|
||||||
|
|
||||||
static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.projectsettings", QtWarningMsg)
|
static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.projectsettings", QtWarningMsg)
|
||||||
|
|
||||||
@@ -100,6 +102,8 @@ void TestProjectSettings::load()
|
|||||||
m_runAfterBuild = runAfterBuild.isValid() ? RunAfterBuildMode(runAfterBuild.toInt())
|
m_runAfterBuild = runAfterBuild.isValid() ? RunAfterBuildMode(runAfterBuild.toInt())
|
||||||
: RunAfterBuildMode::None;
|
: RunAfterBuildMode::None;
|
||||||
m_checkStateCache.fromSettings(m_project->namedSettings(SK_CHECK_STATES).toMap());
|
m_checkStateCache.fromSettings(m_project->namedSettings(SK_CHECK_STATES).toMap());
|
||||||
|
m_limitToFilter = m_project->namedSettings(SK_APPLY_FILTER).toBool();
|
||||||
|
m_pathFilters = m_project->namedSettings(SK_PATH_FILTERS).toStringList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestProjectSettings::save()
|
void TestProjectSettings::save()
|
||||||
@@ -115,6 +119,8 @@ void TestProjectSettings::save()
|
|||||||
m_project->setNamedSettings(SK_ACTIVE_FRAMEWORKS, activeFrameworks);
|
m_project->setNamedSettings(SK_ACTIVE_FRAMEWORKS, activeFrameworks);
|
||||||
m_project->setNamedSettings(SK_RUN_AFTER_BUILD, int(m_runAfterBuild));
|
m_project->setNamedSettings(SK_RUN_AFTER_BUILD, int(m_runAfterBuild));
|
||||||
m_project->setNamedSettings(SK_CHECK_STATES, m_checkStateCache.toSettings(Qt::Checked));
|
m_project->setNamedSettings(SK_CHECK_STATES, m_checkStateCache.toSettings(Qt::Checked));
|
||||||
|
m_project->setNamedSettings(SK_APPLY_FILTER, m_limitToFilter);
|
||||||
|
m_project->setNamedSettings(SK_PATH_FILTERS, m_pathFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -31,15 +31,22 @@ public:
|
|||||||
QHash<ITestTool *, bool> activeTestTools() const { return m_activeTestTools; }
|
QHash<ITestTool *, bool> activeTestTools() const { return m_activeTestTools; }
|
||||||
void activateTestTool(const Utils::Id &id, bool activate);
|
void activateTestTool(const Utils::Id &id, bool activate);
|
||||||
Internal::ItemDataCache<Qt::CheckState> *checkStateCache() { return &m_checkStateCache; }
|
Internal::ItemDataCache<Qt::CheckState> *checkStateCache() { return &m_checkStateCache; }
|
||||||
|
bool limitToFilters() const { return m_limitToFilter; }
|
||||||
|
void setLimitToFilter(bool enable) { m_limitToFilter = enable; }
|
||||||
|
const QStringList pathFilters() const { return m_pathFilters; }
|
||||||
|
void setPathFilters(const QStringList &filters) { m_pathFilters = filters; }
|
||||||
|
void addPathFilter(const QString &filter) { m_pathFilters.append(filter); }
|
||||||
private:
|
private:
|
||||||
void load();
|
void load();
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
ProjectExplorer::Project *m_project;
|
ProjectExplorer::Project *m_project;
|
||||||
bool m_useGlobalSettings = true;
|
bool m_useGlobalSettings = true;
|
||||||
|
bool m_limitToFilter = false;
|
||||||
RunAfterBuildMode m_runAfterBuild = RunAfterBuildMode::None;
|
RunAfterBuildMode m_runAfterBuild = RunAfterBuildMode::None;
|
||||||
QHash<ITestFramework *, bool> m_activeTestFrameworks;
|
QHash<ITestFramework *, bool> m_activeTestFrameworks;
|
||||||
QHash<ITestTool *, bool> m_activeTestTools;
|
QHash<ITestTool *, bool> m_activeTestTools;
|
||||||
|
QStringList m_pathFilters;
|
||||||
Internal::ItemDataCache<Qt::CheckState> m_checkStateCache;
|
Internal::ItemDataCache<Qt::CheckState> m_checkStateCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user