forked from qt-creator/qt-creator
AutoTest: Allow basic filtering of scanned folders
This allows to specify folders to be used as search folders
while scanning for tests.
Current approach allows simple folder names or folder structures
without wildcards.
Examples:
Value What will be (recursively) scanned
tests if the current project has any (not necessarily
a direct) subfolder 'tests' this folder will be
scanned
tests/auto if the current project has any (not necessarily
a direct) subfolder 'tests' and this folder has
a direct subfolder 'auto' the 'auto' folder will
be scanned
If there are more folders which apply to the rules then all of them
will be scanned.
This filtering will not keep the parser inside these folders as it
might be necessary to step into different folders because of
dependencies if the found tests, but the search for entry points to
tests will be limited to these folders.
Task-number: QTCREATORBUG-16705
Change-Id: Ib93465540cd20656d033e16205807aba6830d738
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -24,18 +24,97 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestconstants.h"
|
||||
#include "testcodeparser.h"
|
||||
#include "testframeworkmanager.h"
|
||||
#include "testsettingspage.h"
|
||||
#include "testsettings.h"
|
||||
#include "testtreemodel.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QRegExp>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestFilterDialog : public QDialog
|
||||
{
|
||||
public:
|
||||
explicit TestFilterDialog(QWidget *parent = 0, Qt::WindowFlags f = 0);
|
||||
QString filterPath() const;
|
||||
void setDetailsText(const QString &details) { m_details->setText(details); }
|
||||
void setDefaultFilterPath(const QString &defaultPath);
|
||||
private:
|
||||
static bool validate(Utils::FancyLineEdit *edit, QString * /*errormessage*/);
|
||||
QLabel *m_details;
|
||||
Utils::FancyLineEdit *m_lineEdit;
|
||||
QString m_defaultPath;
|
||||
};
|
||||
|
||||
TestFilterDialog::TestFilterDialog(QWidget *parent, Qt::WindowFlags f)
|
||||
: QDialog(parent, f),
|
||||
m_details(new QLabel),
|
||||
m_lineEdit(new Utils::FancyLineEdit)
|
||||
{
|
||||
setModal(true);
|
||||
auto layout = new QVBoxLayout(this);
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
layout->addWidget(m_details);
|
||||
m_lineEdit->setValidationFunction(&validate);
|
||||
layout->addWidget(m_lineEdit);
|
||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
auto okButton = buttonBox->button(QDialogButtonBox::Ok);
|
||||
auto cancelButton = buttonBox->button(QDialogButtonBox::Cancel);
|
||||
okButton->setEnabled(false);
|
||||
layout->addWidget(buttonBox);
|
||||
setLayout(layout);
|
||||
connect(m_lineEdit, &Utils::FancyLineEdit::validChanged, okButton, &QPushButton::setEnabled);
|
||||
connect(okButton, &QPushButton::clicked, this, &QDialog::accept);
|
||||
connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
QString TestFilterDialog::filterPath() const
|
||||
{
|
||||
static const QRegExp repetition("//+");
|
||||
QString path = m_lineEdit->isValid() ? m_lineEdit->text() : m_defaultPath;
|
||||
path.replace('\\', '/'); // turn windows separators into forward slashes
|
||||
path.replace(repetition, "/"); // avoid duplicated separators
|
||||
if (!path.startsWith('/'))
|
||||
path.prepend('/');
|
||||
if (!path.endsWith('/'))
|
||||
path.append('/');
|
||||
if (path.length() <= 2) // after surrounding with '/' this should be > 2 if valid
|
||||
return QString(); // empty string marks invalid filter
|
||||
return path;
|
||||
}
|
||||
|
||||
void TestFilterDialog::setDefaultFilterPath(const QString &defaultPath)
|
||||
{
|
||||
m_lineEdit->setText(defaultPath);
|
||||
if (m_lineEdit->isValid())
|
||||
m_defaultPath = defaultPath;
|
||||
else
|
||||
m_lineEdit->setText(m_defaultPath);
|
||||
}
|
||||
|
||||
bool TestFilterDialog::validate(Utils::FancyLineEdit *edit, QString *)
|
||||
{
|
||||
if (!edit)
|
||||
return false;
|
||||
const QString &value = edit->text();
|
||||
if (value.isEmpty())
|
||||
return false;
|
||||
// should we distinguish between Windows and UNIX?
|
||||
static const QRegExp valid("(\\|/)?([^?*:;\"\'|<>\t\b]+(\\|/)?)+");
|
||||
return valid.exactMatch(value);
|
||||
}
|
||||
/**************************************************************************************************/
|
||||
|
||||
TestSettingsWidget::TestSettingsWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
@@ -49,6 +128,16 @@ TestSettingsWidget::TestSettingsWidget(QWidget *parent)
|
||||
"having at least one active test framework."));
|
||||
connect(m_ui.frameworkListWidget, &QListWidget::itemChanged,
|
||||
this, &TestSettingsWidget::onFrameworkItemChanged);
|
||||
connect(m_ui.addFilter, &QPushButton::clicked, this, &TestSettingsWidget::onAddFilterClicked);
|
||||
connect(m_ui.editFilter, &QPushButton::clicked, this, &TestSettingsWidget::onEditFilterClicked);
|
||||
connect(m_ui.filterTreeWidget, &QTreeWidget::activated,
|
||||
this, &TestSettingsWidget::onEditFilterClicked);
|
||||
connect(m_ui.removeFilter, &QPushButton::clicked,
|
||||
this, &TestSettingsWidget::onRemoveFilterClicked);
|
||||
connect(m_ui.filterTreeWidget, &QTreeWidget::itemSelectionChanged, [this] () {
|
||||
m_ui.editFilter->setEnabled(true);
|
||||
m_ui.removeFilter->setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
void TestSettingsWidget::setSettings(const TestSettings &settings)
|
||||
@@ -58,7 +147,9 @@ void TestSettingsWidget::setSettings(const TestSettings &settings)
|
||||
m_ui.omitRunConfigWarnCB->setChecked(settings.omitRunConfigWarn);
|
||||
m_ui.limitResultOutputCB->setChecked(settings.limitResultOutput);
|
||||
m_ui.autoScrollCB->setChecked(settings.autoScroll);
|
||||
m_ui.filterGroupBox->setChecked(settings.filterScan);
|
||||
populateFrameworksListWidget(settings.frameworks);
|
||||
populateFiltersWidget(settings.whiteListFilters);
|
||||
}
|
||||
|
||||
TestSettings TestSettingsWidget::settings() const
|
||||
@@ -69,7 +160,9 @@ TestSettings TestSettingsWidget::settings() const
|
||||
result.omitRunConfigWarn = m_ui.omitRunConfigWarnCB->isChecked();
|
||||
result.limitResultOutput = m_ui.limitResultOutputCB->isChecked();
|
||||
result.autoScroll = m_ui.autoScrollCB->isChecked();
|
||||
result.filterScan = m_ui.filterGroupBox->isChecked();
|
||||
result.frameworks = frameworks();
|
||||
result.whiteListFilters = filters();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -87,6 +180,12 @@ void TestSettingsWidget::populateFrameworksListWidget(const QHash<Core::Id, bool
|
||||
}
|
||||
}
|
||||
|
||||
void TestSettingsWidget::populateFiltersWidget(const QStringList &filters)
|
||||
{
|
||||
for (const QString &filter : filters)
|
||||
new QTreeWidgetItem(m_ui.filterTreeWidget, {filter} );
|
||||
}
|
||||
|
||||
QHash<Core::Id, bool> TestSettingsWidget::frameworks() const
|
||||
{
|
||||
const int itemCount = m_ui.frameworkListWidget->count();
|
||||
@@ -100,6 +199,16 @@ QHash<Core::Id, bool> TestSettingsWidget::frameworks() const
|
||||
return frameworks;
|
||||
}
|
||||
|
||||
QStringList TestSettingsWidget::filters() const
|
||||
{
|
||||
QStringList result;
|
||||
if (QAbstractItemModel *model = m_ui.filterTreeWidget->model()) {
|
||||
for (int row = 0, count = model->rowCount(); row < count; ++row)
|
||||
result.append(model->index(row, 0).data().toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TestSettingsWidget::onFrameworkItemChanged()
|
||||
{
|
||||
for (int row = 0, count = m_ui.frameworkListWidget->count(); row < count; ++row) {
|
||||
@@ -113,6 +222,45 @@ void TestSettingsWidget::onFrameworkItemChanged()
|
||||
m_ui.frameworksWarnIcon->setVisible(true);
|
||||
}
|
||||
|
||||
void TestSettingsWidget::onAddFilterClicked()
|
||||
{
|
||||
TestFilterDialog dialog;
|
||||
dialog.setWindowTitle(tr("Add Filter"));
|
||||
dialog.setDetailsText(tr("<p>Specify a filter expression to be added to the list of filters."
|
||||
"<br/>Wildcards are not supported.</p>"));
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
const QString &filter = dialog.filterPath();
|
||||
if (!filter.isEmpty())
|
||||
new QTreeWidgetItem(m_ui.filterTreeWidget, {filter} );
|
||||
}
|
||||
}
|
||||
|
||||
void TestSettingsWidget::onEditFilterClicked()
|
||||
{
|
||||
const QList<QTreeWidgetItem *> &selected = m_ui.filterTreeWidget->selectedItems();
|
||||
QTC_ASSERT(selected.size() == 1, return);
|
||||
const QString &oldFilter = selected.first()->data(0, Qt::DisplayRole).toString();
|
||||
|
||||
TestFilterDialog dialog;
|
||||
dialog.setWindowTitle(tr("Edit Filter"));
|
||||
dialog.setDetailsText(tr("<p>Specify a filter expression that will replace \"%1\"."
|
||||
"<br/>Wildcards are not supported.</p>").arg(oldFilter));
|
||||
dialog.setDefaultFilterPath(oldFilter);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
const QString &edited = dialog.filterPath();
|
||||
if (!edited.isEmpty() && edited != oldFilter)
|
||||
selected.first()->setData(0, Qt::DisplayRole, edited);
|
||||
}
|
||||
}
|
||||
|
||||
void TestSettingsWidget::onRemoveFilterClicked()
|
||||
{
|
||||
const QList<QTreeWidgetItem *> &selected = m_ui.filterTreeWidget->selectedItems();
|
||||
QTC_ASSERT(selected.size() == 1, return);
|
||||
m_ui.filterTreeWidget->removeItemWidget(selected.first(), 0);
|
||||
delete selected.first();
|
||||
}
|
||||
|
||||
TestSettingsPage::TestSettingsPage(const QSharedPointer<TestSettings> &settings)
|
||||
: m_settings(settings), m_widget(0)
|
||||
{
|
||||
@@ -142,11 +290,14 @@ void TestSettingsPage::apply()
|
||||
return;
|
||||
const TestSettings newSettings = m_widget->settings();
|
||||
bool frameworkSyncNecessary = newSettings.frameworks != m_settings->frameworks;
|
||||
bool forceReparse = newSettings.whiteListFilters.toSet() != m_settings->whiteListFilters.toSet();
|
||||
*m_settings = newSettings;
|
||||
m_settings->toSettings(Core::ICore::settings());
|
||||
TestFrameworkManager::instance()->activateFrameworksFromSettings(m_settings);
|
||||
if (frameworkSyncNecessary)
|
||||
TestTreeModel::instance()->syncTestFrameworks();
|
||||
else if (forceReparse)
|
||||
TestTreeModel::instance()->parser()->emitUpdateTestTree();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
Reference in New Issue
Block a user