forked from qt-creator/qt-creator
AutoTest: Allow grouping of test cases
Grouping of test cases can now get enabled for each registered framework. For now grouping happens only folder based. Task-number: QTCREATORBUG-17979 Change-Id: Ic0e5c0ecc76998a1aedea8aa0845f6d9b53fb179 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
#include "gtesttreeitem.h"
|
#include "gtesttreeitem.h"
|
||||||
#include "gtestconfiguration.h"
|
#include "gtestconfiguration.h"
|
||||||
#include "gtestparser.h"
|
#include "gtestparser.h"
|
||||||
|
#include "../testframeworkmanager.h"
|
||||||
|
|
||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
@@ -59,6 +60,7 @@ QVariant GTestTreeItem::data(int column, int role) const
|
|||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
case Root:
|
case Root:
|
||||||
|
case GroupNode:
|
||||||
case TestCase:
|
case TestCase:
|
||||||
case TestFunctionOrSet:
|
case TestFunctionOrSet:
|
||||||
return checked();
|
return checked();
|
||||||
@@ -237,6 +239,22 @@ TestTreeItem *GTestTreeItem::find(const TestParseResult *result)
|
|||||||
states |= GTestTreeItem::Typed;
|
states |= GTestTreeItem::Typed;
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
case Root:
|
case Root:
|
||||||
|
if (TestFrameworkManager::instance()->groupingEnabled(result->frameworkId)) {
|
||||||
|
const QFileInfo fileInfo(parseResult->fileName);
|
||||||
|
const QFileInfo base(fileInfo.absolutePath());
|
||||||
|
for (int row = 0; row < childCount(); ++row) {
|
||||||
|
GTestTreeItem *group = static_cast<GTestTreeItem *>(childAt(row));
|
||||||
|
if (group->filePath() != base.absoluteFilePath())
|
||||||
|
continue;
|
||||||
|
if (auto groupChild = group->findChildByNameStateAndFile(parseResult->name, states,
|
||||||
|
parseResult->proFile)) {
|
||||||
|
return groupChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return findChildByNameStateAndFile(parseResult->name, states, parseResult->proFile);
|
||||||
|
case GroupNode:
|
||||||
return findChildByNameStateAndFile(parseResult->name, states, parseResult->proFile);
|
return findChildByNameStateAndFile(parseResult->name, states, parseResult->proFile);
|
||||||
case TestCase:
|
case TestCase:
|
||||||
return findChildByNameAndFile(result->name, result->fileName);
|
return findChildByNameAndFile(result->name, result->fileName);
|
||||||
@@ -257,6 +275,13 @@ bool GTestTreeItem::modify(const TestParseResult *result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestTreeItem *GTestTreeItem::createParentGroupNode() const
|
||||||
|
{
|
||||||
|
const QFileInfo fileInfo(filePath());
|
||||||
|
const QFileInfo base(fileInfo.absolutePath());
|
||||||
|
return new GTestTreeItem(base.baseName(), fileInfo.absolutePath(), TestTreeItem::GroupNode);
|
||||||
|
}
|
||||||
|
|
||||||
bool GTestTreeItem::modifyTestSetContent(const GTestParseResult *result)
|
bool GTestTreeItem::modifyTestSetContent(const GTestParseResult *result)
|
||||||
{
|
{
|
||||||
bool hasBeenModified = modifyLineAndColumn(result);
|
bool hasBeenModified = modifyLineAndColumn(result);
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public:
|
|||||||
QList<TestConfiguration *> getSelectedTestConfigurations() const override;
|
QList<TestConfiguration *> getSelectedTestConfigurations() const override;
|
||||||
TestTreeItem *find(const TestParseResult *result) override;
|
TestTreeItem *find(const TestParseResult *result) override;
|
||||||
bool modify(const TestParseResult *result) override;
|
bool modify(const TestParseResult *result) override;
|
||||||
|
TestTreeItem *createParentGroupNode() const override;
|
||||||
|
|
||||||
void setStates(TestStates states) { m_state = states; }
|
void setStates(TestStates states) { m_state = states; }
|
||||||
void setState(TestState state) { m_state |= state; }
|
void setState(TestState state) { m_state |= state; }
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ public:
|
|||||||
|
|
||||||
bool active() const { return m_active; }
|
bool active() const { return m_active; }
|
||||||
void setActive(bool active) { m_active = active; }
|
void setActive(bool active) { m_active = active; }
|
||||||
|
bool grouping() const { return m_grouping; }
|
||||||
|
void setGrouping(bool group) { m_grouping = group; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ITestParser *createTestParser() const = 0;
|
virtual ITestParser *createTestParser() const = 0;
|
||||||
@@ -78,6 +80,7 @@ private:
|
|||||||
TestTreeItem *m_rootNode = 0;
|
TestTreeItem *m_rootNode = 0;
|
||||||
ITestParser *m_testParser = 0;
|
ITestParser *m_testParser = 0;
|
||||||
bool m_active = false;
|
bool m_active = false;
|
||||||
|
bool m_grouping = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "qttesttreeitem.h"
|
#include "qttesttreeitem.h"
|
||||||
#include "qttestconfiguration.h"
|
#include "qttestconfiguration.h"
|
||||||
#include "qttestparser.h"
|
#include "qttestparser.h"
|
||||||
|
#include "../testframeworkmanager.h"
|
||||||
|
|
||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
@@ -227,6 +228,19 @@ TestTreeItem *QtTestTreeItem::find(const TestParseResult *result)
|
|||||||
|
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
case Root:
|
case Root:
|
||||||
|
if (TestFrameworkManager::instance()->groupingEnabled(result->frameworkId)) {
|
||||||
|
const QString path = QFileInfo(result->fileName).absolutePath();
|
||||||
|
for (int row = 0; row < childCount(); ++row) {
|
||||||
|
TestTreeItem *group = childItem(row);
|
||||||
|
if (group->filePath() != path)
|
||||||
|
continue;
|
||||||
|
if (auto groupChild = group->findChildByFile(result->fileName))
|
||||||
|
return groupChild;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return findChildByFile(result->fileName);
|
||||||
|
case GroupNode:
|
||||||
return findChildByFile(result->fileName);
|
return findChildByFile(result->fileName);
|
||||||
case TestCase: {
|
case TestCase: {
|
||||||
const QtTestParseResult *qtResult = static_cast<const QtTestParseResult *>(result);
|
const QtTestParseResult *qtResult = static_cast<const QtTestParseResult *>(result);
|
||||||
@@ -259,6 +273,13 @@ bool QtTestTreeItem::modify(const TestParseResult *result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestTreeItem *QtTestTreeItem::createParentGroupNode() const
|
||||||
|
{
|
||||||
|
const QFileInfo fileInfo(filePath());
|
||||||
|
const QFileInfo base(fileInfo.absolutePath());
|
||||||
|
return new QtTestTreeItem(base.baseName(), fileInfo.absolutePath(), TestTreeItem::GroupNode);
|
||||||
|
}
|
||||||
|
|
||||||
TestTreeItem *QtTestTreeItem::findChildByNameAndInheritance(const QString &name, bool inherited) const
|
TestTreeItem *QtTestTreeItem::findChildByNameAndInheritance(const QString &name, bool inherited) const
|
||||||
{
|
{
|
||||||
return findChildBy([name, inherited](const TestTreeItem *other) -> bool {
|
return findChildBy([name, inherited](const TestTreeItem *other) -> bool {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public:
|
|||||||
bool modify(const TestParseResult *result) override;
|
bool modify(const TestParseResult *result) override;
|
||||||
void setInherited(bool inherited) { m_inherited = inherited; }
|
void setInherited(bool inherited) { m_inherited = inherited; }
|
||||||
bool inherited() const { return m_inherited; }
|
bool inherited() const { return m_inherited; }
|
||||||
|
TestTreeItem *createParentGroupNode() const override;
|
||||||
private:
|
private:
|
||||||
TestTreeItem *findChildByNameAndInheritance(const QString &name, bool inherited) const;
|
TestTreeItem *findChildByNameAndInheritance(const QString &name, bool inherited) const;
|
||||||
QString nameSuffix() const;
|
QString nameSuffix() const;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "quicktesttreeitem.h"
|
#include "quicktesttreeitem.h"
|
||||||
#include "quicktestconfiguration.h"
|
#include "quicktestconfiguration.h"
|
||||||
#include "quicktestparser.h"
|
#include "quicktestparser.h"
|
||||||
|
#include "../testframeworkmanager.h"
|
||||||
|
|
||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
@@ -267,7 +268,22 @@ TestTreeItem *QuickTestTreeItem::find(const TestParseResult *result)
|
|||||||
|
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
case Root:
|
case Root:
|
||||||
return result->name.isEmpty() ? unnamedQuickTests() : findChildByFile(result->fileName);
|
if (result->name.isEmpty())
|
||||||
|
return unnamedQuickTests();
|
||||||
|
if (TestFrameworkManager::instance()->groupingEnabled(result->frameworkId)) {
|
||||||
|
const QString path = QFileInfo(result->fileName).absolutePath();
|
||||||
|
for (int row = 0; row < childCount(); ++row) {
|
||||||
|
TestTreeItem *group = childItem(row);
|
||||||
|
if (group->filePath() != path)
|
||||||
|
continue;
|
||||||
|
if (auto groupChild = group->findChildByFile(result->fileName))
|
||||||
|
return groupChild;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return findChildByFile(result->fileName);
|
||||||
|
case GroupNode:
|
||||||
|
return findChildByFile(result->fileName);
|
||||||
case TestCase:
|
case TestCase:
|
||||||
return name().isEmpty() ? findChildByNameAndFile(result->name, result->fileName)
|
return name().isEmpty() ? findChildByNameAndFile(result->name, result->fileName)
|
||||||
: findChildByName(result->name);
|
: findChildByName(result->name);
|
||||||
@@ -303,6 +319,26 @@ bool QuickTestTreeItem::lessThan(const TestTreeItem *other, TestTreeItem::SortMo
|
|||||||
return TestTreeItem::lessThan(other, mode);
|
return TestTreeItem::lessThan(other, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QuickTestTreeItem::isGroupNodeFor(const TestTreeItem *other) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(other, return false);
|
||||||
|
if (other->name().isEmpty()) // unnamed quick tests will not get grouped
|
||||||
|
return false;
|
||||||
|
return TestTreeItem::isGroupNodeFor(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestTreeItem *QuickTestTreeItem::createParentGroupNode() const
|
||||||
|
{
|
||||||
|
if (filePath().isEmpty() || name().isEmpty())
|
||||||
|
return nullptr;
|
||||||
|
if (type() == TestFunctionOrSet)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const QFileInfo fileInfo(filePath());
|
||||||
|
const QFileInfo base(fileInfo.absolutePath());
|
||||||
|
return new QuickTestTreeItem(base.baseName(), fileInfo.absolutePath(), TestTreeItem::GroupNode);
|
||||||
|
}
|
||||||
|
|
||||||
QSet<QString> QuickTestTreeItem::internalTargets() const
|
QSet<QString> QuickTestTreeItem::internalTargets() const
|
||||||
{
|
{
|
||||||
QSet<QString> result;
|
QSet<QString> result;
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ public:
|
|||||||
TestTreeItem *find(const TestParseResult *result) override;
|
TestTreeItem *find(const TestParseResult *result) override;
|
||||||
bool modify(const TestParseResult *result) override;
|
bool modify(const TestParseResult *result) override;
|
||||||
bool lessThan(const TestTreeItem *other, SortMode mode) const override;
|
bool lessThan(const TestTreeItem *other, SortMode mode) const override;
|
||||||
|
bool isGroupNodeFor(const TestTreeItem *other) const override;
|
||||||
|
TestTreeItem *createParentGroupNode() const override;
|
||||||
QSet<QString> internalTargets() const override;
|
QSet<QString> internalTargets() const override;
|
||||||
private:
|
private:
|
||||||
TestTreeItem *unnamedQuickTests() const;
|
TestTreeItem *unnamedQuickTests() const;
|
||||||
|
|||||||
@@ -94,8 +94,10 @@ void TestFrameworkManager::activateFrameworksFromSettings(QSharedPointer<TestSet
|
|||||||
{
|
{
|
||||||
FrameworkIterator it = m_registeredFrameworks.begin();
|
FrameworkIterator it = m_registeredFrameworks.begin();
|
||||||
FrameworkIterator end = m_registeredFrameworks.end();
|
FrameworkIterator end = m_registeredFrameworks.end();
|
||||||
for ( ; it != end; ++it)
|
for ( ; it != end; ++it) {
|
||||||
it.value()->setActive(settings->frameworks.value(it.key(), false));
|
it.value()->setActive(settings->frameworks.value(it.key(), false));
|
||||||
|
it.value()->setGrouping(settings->frameworksGrouping.value(it.key(), false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TestFrameworkManager::frameworkNameForId(const Core::Id &id) const
|
QString TestFrameworkManager::frameworkNameForId(const Core::Id &id) const
|
||||||
@@ -181,6 +183,18 @@ bool TestFrameworkManager::isActive(const Core::Id &frameworkId) const
|
|||||||
return framework ? framework->active() : false;
|
return framework ? framework->active() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TestFrameworkManager::groupingEnabled(const Core::Id &frameworkId) const
|
||||||
|
{
|
||||||
|
ITestFramework *framework = m_registeredFrameworks.value(frameworkId);
|
||||||
|
return framework ? framework->grouping() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFrameworkManager::setGroupingEnabledFor(const Core::Id &frameworkId, bool enabled)
|
||||||
|
{
|
||||||
|
if (ITestFramework *framework = m_registeredFrameworks.value(frameworkId))
|
||||||
|
framework->setGrouping(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
bool TestFrameworkManager::hasActiveFrameworks() const
|
bool TestFrameworkManager::hasActiveFrameworks() const
|
||||||
{
|
{
|
||||||
for (ITestFramework *framework : m_registeredFrameworks.values()) {
|
for (ITestFramework *framework : m_registeredFrameworks.values()) {
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ public:
|
|||||||
QSharedPointer<IFrameworkSettings> settingsForTestFramework(const Core::Id &frameworkId) const;
|
QSharedPointer<IFrameworkSettings> settingsForTestFramework(const Core::Id &frameworkId) const;
|
||||||
void synchronizeSettings(QSettings *s);
|
void synchronizeSettings(QSettings *s);
|
||||||
bool isActive(const Core::Id &frameworkId) const;
|
bool isActive(const Core::Id &frameworkId) const;
|
||||||
|
bool groupingEnabled(const Core::Id &frameworkId) const;
|
||||||
|
void setGroupingEnabledFor(const Core::Id &frameworkId, bool enabled);
|
||||||
bool hasActiveFrameworks() const;
|
bool hasActiveFrameworks() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ static const char autoScrollKey[] = "AutoScrollResults";
|
|||||||
static const char filterScanKey[] = "FilterScan";
|
static const char filterScanKey[] = "FilterScan";
|
||||||
static const char filtersKey[] = "WhiteListFilters";
|
static const char filtersKey[] = "WhiteListFilters";
|
||||||
static const char processArgsKey[] = "ProcessArgs";
|
static const char processArgsKey[] = "ProcessArgs";
|
||||||
|
static const char groupSuffix[] = ".group";
|
||||||
|
|
||||||
static const int defaultTimeout = 60000;
|
static const int defaultTimeout = 60000;
|
||||||
|
|
||||||
@@ -61,9 +62,11 @@ void TestSettings::toSettings(QSettings *s) const
|
|||||||
s->setValue(processArgsKey, processArgs);
|
s->setValue(processArgsKey, processArgs);
|
||||||
s->setValue(filterScanKey, filterScan);
|
s->setValue(filterScanKey, filterScan);
|
||||||
s->setValue(filtersKey, whiteListFilters);
|
s->setValue(filtersKey, whiteListFilters);
|
||||||
// store frameworks and their current active state
|
// store frameworks and their current active and grouping state
|
||||||
for (const Core::Id &id : frameworks.keys())
|
for (const Core::Id &id : frameworks.keys()) {
|
||||||
s->setValue(QLatin1String(id.name()), frameworks.value(id));
|
s->setValue(QLatin1String(id.name()), frameworks.value(id));
|
||||||
|
s->setValue(QLatin1String(id.name().append(groupSuffix)), frameworksGrouping.value(id));
|
||||||
|
}
|
||||||
s->endGroup();
|
s->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +85,14 @@ void TestSettings::fromSettings(QSettings *s)
|
|||||||
TestFrameworkManager *frameworkManager = TestFrameworkManager::instance();
|
TestFrameworkManager *frameworkManager = TestFrameworkManager::instance();
|
||||||
const QList<Core::Id> ®istered = frameworkManager->registeredFrameworkIds();
|
const QList<Core::Id> ®istered = frameworkManager->registeredFrameworkIds();
|
||||||
frameworks.clear();
|
frameworks.clear();
|
||||||
|
frameworksGrouping.clear();
|
||||||
for (const Core::Id &id : registered) {
|
for (const Core::Id &id : registered) {
|
||||||
|
// get their active state
|
||||||
frameworks.insert(id, s->value(QLatin1String(id.name()),
|
frameworks.insert(id, s->value(QLatin1String(id.name()),
|
||||||
frameworkManager->isActive(id)).toBool());
|
frameworkManager->isActive(id)).toBool());
|
||||||
|
// and whether grouping is enabled
|
||||||
|
frameworksGrouping.insert(id, s->value(QLatin1String(id.name().append(groupSuffix)),
|
||||||
|
frameworkManager->groupingEnabled(id)).toBool());
|
||||||
}
|
}
|
||||||
s->endGroup();
|
s->endGroup();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ struct TestSettings
|
|||||||
bool filterScan = false;
|
bool filterScan = false;
|
||||||
bool processArgs = false;
|
bool processArgs = false;
|
||||||
QHash<Core::Id, bool> frameworks;
|
QHash<Core::Id, bool> frameworks;
|
||||||
|
QHash<Core::Id, bool> frameworksGrouping;
|
||||||
QStringList whiteListFilters;
|
QStringList whiteListFilters;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ TestSettings TestSettingsWidget::settings() const
|
|||||||
result.autoScroll = m_ui.autoScrollCB->isChecked();
|
result.autoScroll = m_ui.autoScrollCB->isChecked();
|
||||||
result.processArgs = m_ui.processArgsCB->isChecked();
|
result.processArgs = m_ui.processArgsCB->isChecked();
|
||||||
result.filterScan = m_ui.filterGroupBox->isChecked();
|
result.filterScan = m_ui.filterGroupBox->isChecked();
|
||||||
result.frameworks = frameworks();
|
frameworkSettings(result);
|
||||||
result.whiteListFilters = filters();
|
result.whiteListFilters = filters();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -180,6 +180,11 @@ void TestSettingsWidget::populateFrameworksListWidget(const QHash<Core::Id, bool
|
|||||||
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
|
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
|
||||||
item->setCheckState(0, frameworks.value(id) ? Qt::Checked : Qt::Unchecked);
|
item->setCheckState(0, frameworks.value(id) ? Qt::Checked : Qt::Unchecked);
|
||||||
item->setData(0, Qt::UserRole, id.toSetting());
|
item->setData(0, Qt::UserRole, id.toSetting());
|
||||||
|
item->setData(1, Qt::CheckStateRole, frameworkManager->groupingEnabled(id) ? Qt::Checked
|
||||||
|
: Qt::Unchecked);
|
||||||
|
item->setToolTip(0, tr("Enable or disable test frameworks to be handled by the AutoTest "
|
||||||
|
"plugin."));
|
||||||
|
item->setToolTip(1, tr("Enable or disable grouping of test cases by folder."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,18 +194,18 @@ void TestSettingsWidget::populateFiltersWidget(const QStringList &filters)
|
|||||||
new QTreeWidgetItem(m_ui.filterTreeWidget, {filter} );
|
new QTreeWidgetItem(m_ui.filterTreeWidget, {filter} );
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<Core::Id, bool> TestSettingsWidget::frameworks() const
|
void TestSettingsWidget::frameworkSettings(TestSettings &settings) const
|
||||||
{
|
{
|
||||||
QHash<Core::Id, bool> frameworks;
|
|
||||||
const QAbstractItemModel *model = m_ui.frameworkTreeWidget->model();
|
const QAbstractItemModel *model = m_ui.frameworkTreeWidget->model();
|
||||||
QTC_ASSERT(model, return frameworks);
|
QTC_ASSERT(model, return);
|
||||||
const int itemCount = model->rowCount();
|
const int itemCount = model->rowCount();
|
||||||
for (int row = 0; row < itemCount; ++row) {
|
for (int row = 0; row < itemCount; ++row) {
|
||||||
const QModelIndex index = model->index(row, 0);
|
QModelIndex idx = model->index(row, 0);
|
||||||
frameworks.insert(Core::Id::fromSetting(index.data(Qt::UserRole)),
|
const Core::Id id = Core::Id::fromSetting(idx.data(Qt::UserRole));
|
||||||
index.data(Qt::CheckStateRole) == Qt::Checked);
|
settings.frameworks.insert(id, idx.data(Qt::CheckStateRole) == Qt::Checked);
|
||||||
|
idx = model->index(row, 1);
|
||||||
|
settings.frameworksGrouping.insert(id, idx.data(Qt::CheckStateRole) == Qt::Checked);
|
||||||
}
|
}
|
||||||
return frameworks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList TestSettingsWidget::filters() const
|
QStringList TestSettingsWidget::filters() const
|
||||||
@@ -298,13 +303,20 @@ void TestSettingsPage::apply()
|
|||||||
bool frameworkSyncNecessary = newSettings.frameworks != m_settings->frameworks;
|
bool frameworkSyncNecessary = newSettings.frameworks != m_settings->frameworks;
|
||||||
bool forceReparse = newSettings.filterScan != m_settings->filterScan ||
|
bool forceReparse = newSettings.filterScan != m_settings->filterScan ||
|
||||||
newSettings.whiteListFilters.toSet() != m_settings->whiteListFilters.toSet();
|
newSettings.whiteListFilters.toSet() != m_settings->whiteListFilters.toSet();
|
||||||
|
const QList<Core::Id> changedIds = Utils::filtered(newSettings.frameworksGrouping.keys(),
|
||||||
|
[newSettings, this] (const Core::Id &id) {
|
||||||
|
return newSettings.frameworksGrouping[id] != m_settings->frameworksGrouping[id];
|
||||||
|
});
|
||||||
*m_settings = newSettings;
|
*m_settings = newSettings;
|
||||||
m_settings->toSettings(Core::ICore::settings());
|
m_settings->toSettings(Core::ICore::settings());
|
||||||
TestFrameworkManager::instance()->activateFrameworksFromSettings(m_settings);
|
TestFrameworkManager *frameworkManager = TestFrameworkManager::instance();
|
||||||
|
frameworkManager->activateFrameworksFromSettings(m_settings);
|
||||||
if (frameworkSyncNecessary)
|
if (frameworkSyncNecessary)
|
||||||
TestTreeModel::instance()->syncTestFrameworks();
|
TestTreeModel::instance()->syncTestFrameworks();
|
||||||
else if (forceReparse)
|
else if (forceReparse)
|
||||||
TestTreeModel::instance()->parser()->emitUpdateTestTree();
|
TestTreeModel::instance()->parser()->emitUpdateTestTree();
|
||||||
|
else if (!changedIds.isEmpty())
|
||||||
|
TestTreeModel::instance()->rebuild(changedIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void populateFrameworksListWidget(const QHash<Core::Id, bool> &frameworks);
|
void populateFrameworksListWidget(const QHash<Core::Id, bool> &frameworks);
|
||||||
void populateFiltersWidget(const QStringList &filters);
|
void populateFiltersWidget(const QStringList &filters);
|
||||||
QHash<Core::Id, bool> frameworks() const;
|
void frameworkSettings(TestSettings &settings) const;
|
||||||
QStringList filters() const;
|
QStringList filters() const;
|
||||||
void onFrameworkItemChanged();
|
void onFrameworkItemChanged();
|
||||||
void onAddFilterClicked();
|
void onAddFilterClicked();
|
||||||
|
|||||||
@@ -183,12 +183,29 @@ Warning: this is an experimental feature and might lead to failing to execute th
|
|||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="headerHidden">
|
<property name="headerHidden">
|
||||||
<bool>true</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="columnCount">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<attribute name="headerDefaultSectionSize">
|
||||||
|
<number>150</number>
|
||||||
|
</attribute>
|
||||||
<column>
|
<column>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Framework</string>
|
<string>Framework</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Selects the test frameworks to be handled by the AutoTest plugin.</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Group</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Enables grouping of test cases.</string>
|
||||||
|
</property>
|
||||||
</column>
|
</column>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
#include <cpptools/cpptoolsreuse.h>
|
#include <cpptools/cpptoolsreuse.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
@@ -43,21 +44,31 @@ TestTreeItem::TestTreeItem(const QString &name, const QString &filePath, Type ty
|
|||||||
m_filePath(filePath),
|
m_filePath(filePath),
|
||||||
m_type(type)
|
m_type(type)
|
||||||
{
|
{
|
||||||
m_checked = (m_type == TestCase || m_type == TestFunctionOrSet || m_type == Root)
|
switch (m_type) {
|
||||||
? Qt::Checked : Qt::Unchecked;
|
case Root:
|
||||||
|
case GroupNode:
|
||||||
|
case TestCase:
|
||||||
|
case TestFunctionOrSet:
|
||||||
|
m_checked = Qt::Checked;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_checked = Qt::Unchecked;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QIcon testTreeIcon(TestTreeItem::Type type)
|
static QIcon testTreeIcon(TestTreeItem::Type type)
|
||||||
{
|
{
|
||||||
static QIcon icons[] = {
|
static QIcon icons[] = {
|
||||||
QIcon(),
|
QIcon(),
|
||||||
|
Utils::Icons::OPENFILE.icon(),
|
||||||
CPlusPlus::Icons::iconForType(CPlusPlus::Icons::ClassIconType),
|
CPlusPlus::Icons::iconForType(CPlusPlus::Icons::ClassIconType),
|
||||||
CPlusPlus::Icons::iconForType(CPlusPlus::Icons::SlotPrivateIconType),
|
CPlusPlus::Icons::iconForType(CPlusPlus::Icons::SlotPrivateIconType),
|
||||||
QIcon(":/images/data.png")
|
QIcon(":/images/data.png")
|
||||||
};
|
};
|
||||||
|
|
||||||
if (int(type) >= int(sizeof icons / sizeof *icons))
|
if (int(type) >= int(sizeof icons / sizeof *icons))
|
||||||
return icons[2];
|
return icons[3];
|
||||||
return icons[type];
|
return icons[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +87,8 @@ QVariant TestTreeItem::data(int /*column*/, int role) const
|
|||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
case LinkRole: {
|
case LinkRole: {
|
||||||
|
if (m_type == GroupNode)
|
||||||
|
return QVariant();
|
||||||
QVariant itemLink;
|
QVariant itemLink;
|
||||||
itemLink.setValue(Utils::Link(m_filePath, m_line, m_column));
|
itemLink.setValue(Utils::Link(m_filePath, m_line, m_column));
|
||||||
return itemLink;
|
return itemLink;
|
||||||
@@ -105,6 +118,7 @@ Qt::ItemFlags TestTreeItem::flags(int /*column*/) const
|
|||||||
static const Qt::ItemFlags defaultFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
static const Qt::ItemFlags defaultFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
case Root:
|
case Root:
|
||||||
|
case GroupNode:
|
||||||
return Qt::ItemIsEnabled | Qt::ItemIsAutoTristate | Qt::ItemIsUserCheckable;
|
return Qt::ItemIsEnabled | Qt::ItemIsAutoTristate | Qt::ItemIsUserCheckable;
|
||||||
case TestCase:
|
case TestCase:
|
||||||
return defaultFlags | Qt::ItemIsAutoTristate | Qt::ItemIsUserCheckable;
|
return defaultFlags | Qt::ItemIsAutoTristate | Qt::ItemIsUserCheckable;
|
||||||
@@ -164,6 +178,7 @@ void TestTreeItem::setChecked(const Qt::CheckState checkState)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Root:
|
case Root:
|
||||||
|
case GroupNode:
|
||||||
case TestFunctionOrSet:
|
case TestFunctionOrSet:
|
||||||
case TestCase: {
|
case TestCase: {
|
||||||
Qt::CheckState usedState = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked);
|
Qt::CheckState usedState = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked);
|
||||||
@@ -185,6 +200,7 @@ Qt::CheckState TestTreeItem::checked() const
|
|||||||
{
|
{
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
case Root:
|
case Root:
|
||||||
|
case GroupNode:
|
||||||
case TestCase:
|
case TestCase:
|
||||||
case TestFunctionOrSet:
|
case TestFunctionOrSet:
|
||||||
case TestDataTag:
|
case TestDataTag:
|
||||||
@@ -268,6 +284,9 @@ bool TestTreeItem::lessThan(const TestTreeItem *other, SortMode mode) const
|
|||||||
return index().row() > other->index().row();
|
return index().row() > other->index().row();
|
||||||
return lhs > rhs;
|
return lhs > rhs;
|
||||||
case Naturally: {
|
case Naturally: {
|
||||||
|
if (m_type == GroupNode && other->type() == GroupNode)
|
||||||
|
return m_filePath > other->filePath();
|
||||||
|
|
||||||
const Utils::Link &leftLink = data(0, LinkRole).value<Utils::Link>();
|
const Utils::Link &leftLink = data(0, LinkRole).value<Utils::Link>();
|
||||||
const Utils::Link &rightLink = other->data(0, LinkRole).value<Utils::Link>();
|
const Utils::Link &rightLink = other->data(0, LinkRole).value<Utils::Link>();
|
||||||
if (leftLink.targetFileName == rightLink.targetFileName) {
|
if (leftLink.targetFileName == rightLink.targetFileName) {
|
||||||
@@ -282,6 +301,16 @@ bool TestTreeItem::lessThan(const TestTreeItem *other, SortMode mode) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TestTreeItem::isGroupNodeFor(const TestTreeItem *other) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(other, return false);
|
||||||
|
if (type() != TestTreeItem::GroupNode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// for now there's only the possibility to have 'Folder' nodes
|
||||||
|
return QFileInfo(other->filePath()).absolutePath() == filePath();
|
||||||
|
}
|
||||||
|
|
||||||
QSet<QString> TestTreeItem::internalTargets() const
|
QSet<QString> TestTreeItem::internalTargets() const
|
||||||
{
|
{
|
||||||
auto cppMM = CppTools::CppModelManager::instance();
|
auto cppMM = CppTools::CppModelManager::instance();
|
||||||
@@ -301,7 +330,7 @@ QSet<QString> TestTreeItem::internalTargets() const
|
|||||||
void TestTreeItem::revalidateCheckState()
|
void TestTreeItem::revalidateCheckState()
|
||||||
{
|
{
|
||||||
const Type ttiType = type();
|
const Type ttiType = type();
|
||||||
if (ttiType != TestCase && ttiType != TestFunctionOrSet && ttiType != Root)
|
if (ttiType != TestCase && ttiType != TestFunctionOrSet && ttiType != Root && ttiType != GroupNode)
|
||||||
return;
|
return;
|
||||||
if (childCount() == 0) // can this happen? (we're calling revalidateCS() on parentItem()
|
if (childCount() == 0) // can this happen? (we're calling revalidateCS() on parentItem()
|
||||||
return;
|
return;
|
||||||
@@ -323,13 +352,13 @@ void TestTreeItem::revalidateCheckState()
|
|||||||
foundPartiallyChecked |= (child->checked() == Qt::PartiallyChecked);
|
foundPartiallyChecked |= (child->checked() == Qt::PartiallyChecked);
|
||||||
if (foundPartiallyChecked || (foundChecked && foundUnchecked)) {
|
if (foundPartiallyChecked || (foundChecked && foundUnchecked)) {
|
||||||
m_checked = Qt::PartiallyChecked;
|
m_checked = Qt::PartiallyChecked;
|
||||||
if (ttiType == TestFunctionOrSet || ttiType == TestCase)
|
if (ttiType == TestFunctionOrSet || ttiType == TestCase || ttiType == GroupNode)
|
||||||
parentItem()->revalidateCheckState();
|
parentItem()->revalidateCheckState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_checked = (foundUnchecked ? Qt::Unchecked : Qt::Checked);
|
m_checked = (foundUnchecked ? Qt::Unchecked : Qt::Checked);
|
||||||
if (ttiType == TestFunctionOrSet || ttiType == TestCase)
|
if (ttiType == TestFunctionOrSet || ttiType == TestCase || ttiType == GroupNode)
|
||||||
parentItem()->revalidateCheckState();
|
parentItem()->revalidateCheckState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public:
|
|||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Root,
|
Root,
|
||||||
|
GroupNode,
|
||||||
TestCase,
|
TestCase,
|
||||||
TestFunctionOrSet,
|
TestFunctionOrSet,
|
||||||
TestDataTag,
|
TestDataTag,
|
||||||
@@ -112,7 +113,8 @@ public:
|
|||||||
virtual bool lessThan(const TestTreeItem *other, SortMode mode) const;
|
virtual bool lessThan(const TestTreeItem *other, SortMode mode) const;
|
||||||
virtual TestTreeItem *find(const TestParseResult *result) = 0;
|
virtual TestTreeItem *find(const TestParseResult *result) = 0;
|
||||||
virtual bool modify(const TestParseResult *result) = 0;
|
virtual bool modify(const TestParseResult *result) = 0;
|
||||||
|
virtual bool isGroupNodeFor(const TestTreeItem *other) const;
|
||||||
|
virtual TestTreeItem *createParentGroupNode() const = 0;
|
||||||
virtual QSet<QString> internalTargets() const;
|
virtual QSet<QString> internalTargets() const;
|
||||||
protected:
|
protected:
|
||||||
typedef std::function<bool(const TestTreeItem *)> CompareFunction;
|
typedef std::function<bool(const TestTreeItem *)> CompareFunction;
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ bool TestTreeModel::setData(const QModelIndex &index, const QVariant &value, int
|
|||||||
if (role == Qt::CheckStateRole) {
|
if (role == Qt::CheckStateRole) {
|
||||||
switch (item->type()) {
|
switch (item->type()) {
|
||||||
case TestTreeItem::Root:
|
case TestTreeItem::Root:
|
||||||
|
case TestTreeItem::GroupNode:
|
||||||
case TestTreeItem::TestCase:
|
case TestTreeItem::TestCase:
|
||||||
if (item->childCount() > 0)
|
if (item->childCount() > 0)
|
||||||
emit dataChanged(index.child(0, 0), index.child(item->childCount() - 1, 0));
|
emit dataChanged(index.child(0, 0), index.child(item->childCount() - 1, 0));
|
||||||
@@ -174,6 +175,31 @@ void TestTreeModel::syncTestFrameworks()
|
|||||||
emit updatedActiveFrameworks(sortedIds.size());
|
emit updatedActiveFrameworks(sortedIds.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestTreeModel::rebuild(const QList<Core::Id> &frameworkIds)
|
||||||
|
{
|
||||||
|
TestFrameworkManager *frameworkManager = TestFrameworkManager::instance();
|
||||||
|
for (const Core::Id &id : frameworkIds) {
|
||||||
|
TestTreeItem *frameworkRoot = frameworkManager->rootNodeForTestFramework(id);
|
||||||
|
const bool groupingEnabled = TestFrameworkManager::instance()->groupingEnabled(id);
|
||||||
|
for (int row = frameworkRoot->childCount() - 1; row >= 0; --row) {
|
||||||
|
auto testItem = frameworkRoot->childItem(row);
|
||||||
|
if (!groupingEnabled && testItem->type() == TestTreeItem::GroupNode) {
|
||||||
|
// do not re-insert the GroupNode, but process its children and delete it afterwards
|
||||||
|
for (int childRow = testItem->childCount() - 1; childRow >= 0; --childRow) {
|
||||||
|
// FIXME should this be done recursively until we have a non-GroupNode?
|
||||||
|
TestTreeItem *childTestItem = testItem->childItem(childRow);
|
||||||
|
takeItem(childTestItem);
|
||||||
|
insertItemInParent(childTestItem, frameworkRoot, groupingEnabled);
|
||||||
|
}
|
||||||
|
delete takeItem(testItem);
|
||||||
|
} else {
|
||||||
|
takeItem(testItem);
|
||||||
|
insertItemInParent(testItem, frameworkRoot, groupingEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TestTreeModel::removeFiles(const QStringList &files)
|
void TestTreeModel::removeFiles(const QStringList &files)
|
||||||
{
|
{
|
||||||
for (const QString &file : files)
|
for (const QString &file : files)
|
||||||
@@ -239,6 +265,24 @@ bool TestTreeModel::sweepChildren(TestTreeItem *item)
|
|||||||
return hasChanged;
|
return hasChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestTreeModel::insertItemInParent(TestTreeItem *item, TestTreeItem *root, bool groupingEnabled)
|
||||||
|
{
|
||||||
|
TestTreeItem *parentNode = root;
|
||||||
|
if (groupingEnabled) {
|
||||||
|
parentNode = root->findChildBy([item] (const TestTreeItem *it) {
|
||||||
|
return it->isGroupNodeFor(item);
|
||||||
|
});
|
||||||
|
if (!parentNode) {
|
||||||
|
parentNode = item->createParentGroupNode();
|
||||||
|
if (!parentNode) // we might not get a group node at all
|
||||||
|
parentNode = root;
|
||||||
|
else
|
||||||
|
root->appendChild(parentNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parentNode->appendChild(item);
|
||||||
|
}
|
||||||
|
|
||||||
void TestTreeModel::onParseResultReady(const TestParseResultPtr result)
|
void TestTreeModel::onParseResultReady(const TestParseResultPtr result)
|
||||||
{
|
{
|
||||||
TestTreeItem *rootNode
|
TestTreeItem *rootNode
|
||||||
@@ -249,10 +293,19 @@ void TestTreeModel::onParseResultReady(const TestParseResultPtr result)
|
|||||||
|
|
||||||
void TestTreeModel::handleParseResult(const TestParseResult *result, TestTreeItem *parentNode)
|
void TestTreeModel::handleParseResult(const TestParseResult *result, TestTreeItem *parentNode)
|
||||||
{
|
{
|
||||||
|
const bool groupingEnabled =
|
||||||
|
TestFrameworkManager::instance()->groupingEnabled(result->frameworkId);
|
||||||
// lookup existing items
|
// lookup existing items
|
||||||
if (TestTreeItem *toBeModified = parentNode->find(result)) {
|
if (TestTreeItem *toBeModified = parentNode->find(result)) {
|
||||||
// found existing item... Do not remove
|
// found existing item... Do not remove
|
||||||
toBeModified->markForRemoval(false);
|
toBeModified->markForRemoval(false);
|
||||||
|
// if it's a reparse we need to mark the group node as well to avoid purging it in sweep()
|
||||||
|
if (groupingEnabled) {
|
||||||
|
if (auto directParent = toBeModified->parentItem()) {
|
||||||
|
if (directParent->type() == TestTreeItem::GroupNode)
|
||||||
|
directParent->markForRemoval(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
// modify and when content has changed inform ui
|
// modify and when content has changed inform ui
|
||||||
if (toBeModified->modify(result)) {
|
if (toBeModified->modify(result)) {
|
||||||
const QModelIndex &idx = indexForItem(toBeModified);
|
const QModelIndex &idx = indexForItem(toBeModified);
|
||||||
@@ -266,7 +319,8 @@ void TestTreeModel::handleParseResult(const TestParseResult *result, TestTreeIte
|
|||||||
// if there's no matching item, add the new one
|
// if there's no matching item, add the new one
|
||||||
TestTreeItem *newItem = result->createTestTreeItem();
|
TestTreeItem *newItem = result->createTestTreeItem();
|
||||||
QTC_ASSERT(newItem, return);
|
QTC_ASSERT(newItem, return);
|
||||||
parentNode->appendChild(newItem);
|
|
||||||
|
insertItemInParent(newItem, parentNode, groupingEnabled);
|
||||||
// new items are checked by default - revalidation of parents might be necessary
|
// new items are checked by default - revalidation of parents might be necessary
|
||||||
if (parentNode->checked() != Qt::Checked) {
|
if (parentNode->checked() != Qt::Checked) {
|
||||||
parentNode->revalidateCheckState();
|
parentNode->revalidateCheckState();
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public:
|
|||||||
QList<TestConfiguration *> getSelectedTests() const;
|
QList<TestConfiguration *> getSelectedTests() const;
|
||||||
|
|
||||||
void syncTestFrameworks();
|
void syncTestFrameworks();
|
||||||
|
void rebuild(const QList<Core::Id> &frameworkIds);
|
||||||
|
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
int autoTestsCount() const;
|
int autoTestsCount() const;
|
||||||
@@ -86,6 +87,7 @@ private:
|
|||||||
void removeTestRootNodes();
|
void removeTestRootNodes();
|
||||||
void removeFiles(const QStringList &files);
|
void removeFiles(const QStringList &files);
|
||||||
bool sweepChildren(TestTreeItem *item);
|
bool sweepChildren(TestTreeItem *item);
|
||||||
|
static void insertItemInParent(TestTreeItem *item, TestTreeItem *root, bool groupingEnabled);
|
||||||
|
|
||||||
explicit TestTreeModel(QObject *parent = 0);
|
explicit TestTreeModel(QObject *parent = 0);
|
||||||
void setupParsingConnections();
|
void setupParsingConnections();
|
||||||
|
|||||||
Reference in New Issue
Block a user