diff --git a/src/plugins/autotest/gtest/gtesttreeitem.cpp b/src/plugins/autotest/gtest/gtesttreeitem.cpp index efd336e1efe..fb71f695e44 100644 --- a/src/plugins/autotest/gtest/gtesttreeitem.cpp +++ b/src/plugins/autotest/gtest/gtesttreeitem.cpp @@ -26,6 +26,7 @@ #include "gtesttreeitem.h" #include "gtestconfiguration.h" #include "gtestparser.h" +#include "../testframeworkmanager.h" #include #include @@ -59,6 +60,7 @@ QVariant GTestTreeItem::data(int column, int role) const case Qt::CheckStateRole: switch (type()) { case Root: + case GroupNode: case TestCase: case TestFunctionOrSet: return checked(); @@ -237,6 +239,22 @@ TestTreeItem *GTestTreeItem::find(const TestParseResult *result) states |= GTestTreeItem::Typed; switch (type()) { 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(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); case TestCase: 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 hasBeenModified = modifyLineAndColumn(result); diff --git a/src/plugins/autotest/gtest/gtesttreeitem.h b/src/plugins/autotest/gtest/gtesttreeitem.h index b732840ce81..7aad3563dd8 100644 --- a/src/plugins/autotest/gtest/gtesttreeitem.h +++ b/src/plugins/autotest/gtest/gtesttreeitem.h @@ -58,6 +58,7 @@ public: QList getSelectedTestConfigurations() const override; TestTreeItem *find(const TestParseResult *result) override; bool modify(const TestParseResult *result) override; + TestTreeItem *createParentGroupNode() const override; void setStates(TestStates states) { m_state = states; } void setState(TestState state) { m_state |= state; } diff --git a/src/plugins/autotest/itestframework.h b/src/plugins/autotest/itestframework.h index ae6e0ec437c..67778f13753 100644 --- a/src/plugins/autotest/itestframework.h +++ b/src/plugins/autotest/itestframework.h @@ -69,6 +69,8 @@ public: bool active() const { return m_active; } void setActive(bool active) { m_active = active; } + bool grouping() const { return m_grouping; } + void setGrouping(bool group) { m_grouping = group; } protected: virtual ITestParser *createTestParser() const = 0; @@ -78,6 +80,7 @@ private: TestTreeItem *m_rootNode = 0; ITestParser *m_testParser = 0; bool m_active = false; + bool m_grouping = false; }; } // namespace Internal diff --git a/src/plugins/autotest/qtest/qttesttreeitem.cpp b/src/plugins/autotest/qtest/qttesttreeitem.cpp index 850ba6dc542..a06598db74e 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.cpp +++ b/src/plugins/autotest/qtest/qttesttreeitem.cpp @@ -26,6 +26,7 @@ #include "qttesttreeitem.h" #include "qttestconfiguration.h" #include "qttestparser.h" +#include "../testframeworkmanager.h" #include #include @@ -227,6 +228,19 @@ TestTreeItem *QtTestTreeItem::find(const TestParseResult *result) switch (type()) { 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); case TestCase: { const QtTestParseResult *qtResult = static_cast(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 { return findChildBy([name, inherited](const TestTreeItem *other) -> bool { diff --git a/src/plugins/autotest/qtest/qttesttreeitem.h b/src/plugins/autotest/qtest/qttesttreeitem.h index 1f25bfb8398..a1ea02ace4a 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.h +++ b/src/plugins/autotest/qtest/qttesttreeitem.h @@ -48,6 +48,7 @@ public: bool modify(const TestParseResult *result) override; void setInherited(bool inherited) { m_inherited = inherited; } bool inherited() const { return m_inherited; } + TestTreeItem *createParentGroupNode() const override; private: TestTreeItem *findChildByNameAndInheritance(const QString &name, bool inherited) const; QString nameSuffix() const; diff --git a/src/plugins/autotest/quick/quicktesttreeitem.cpp b/src/plugins/autotest/quick/quicktesttreeitem.cpp index 29f6aa4a7cc..098e7c37689 100644 --- a/src/plugins/autotest/quick/quicktesttreeitem.cpp +++ b/src/plugins/autotest/quick/quicktesttreeitem.cpp @@ -26,6 +26,7 @@ #include "quicktesttreeitem.h" #include "quicktestconfiguration.h" #include "quicktestparser.h" +#include "../testframeworkmanager.h" #include #include @@ -267,7 +268,22 @@ TestTreeItem *QuickTestTreeItem::find(const TestParseResult *result) switch (type()) { 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: return name().isEmpty() ? findChildByNameAndFile(result->name, result->fileName) : findChildByName(result->name); @@ -303,6 +319,26 @@ bool QuickTestTreeItem::lessThan(const TestTreeItem *other, TestTreeItem::SortMo 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 QuickTestTreeItem::internalTargets() const { QSet result; diff --git a/src/plugins/autotest/quick/quicktesttreeitem.h b/src/plugins/autotest/quick/quicktesttreeitem.h index be036a13fb3..b7116125e30 100644 --- a/src/plugins/autotest/quick/quicktesttreeitem.h +++ b/src/plugins/autotest/quick/quicktesttreeitem.h @@ -47,6 +47,8 @@ public: TestTreeItem *find(const TestParseResult *result) override; bool modify(const TestParseResult *result) override; bool lessThan(const TestTreeItem *other, SortMode mode) const override; + bool isGroupNodeFor(const TestTreeItem *other) const override; + TestTreeItem *createParentGroupNode() const override; QSet internalTargets() const override; private: TestTreeItem *unnamedQuickTests() const; diff --git a/src/plugins/autotest/testframeworkmanager.cpp b/src/plugins/autotest/testframeworkmanager.cpp index 4726d2e02e1..8b6bfe22707 100644 --- a/src/plugins/autotest/testframeworkmanager.cpp +++ b/src/plugins/autotest/testframeworkmanager.cpp @@ -94,8 +94,10 @@ void TestFrameworkManager::activateFrameworksFromSettings(QSharedPointersetActive(settings->frameworks.value(it.key(), false)); + it.value()->setGrouping(settings->frameworksGrouping.value(it.key(), false)); + } } 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; } +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 { for (ITestFramework *framework : m_registeredFrameworks.values()) { diff --git a/src/plugins/autotest/testframeworkmanager.h b/src/plugins/autotest/testframeworkmanager.h index 5b92d710d19..6f37adc84cb 100644 --- a/src/plugins/autotest/testframeworkmanager.h +++ b/src/plugins/autotest/testframeworkmanager.h @@ -63,6 +63,8 @@ public: QSharedPointer settingsForTestFramework(const Core::Id &frameworkId) const; void synchronizeSettings(QSettings *s); 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; private: diff --git a/src/plugins/autotest/testsettings.cpp b/src/plugins/autotest/testsettings.cpp index 3756ef3cc23..55cbe311146 100644 --- a/src/plugins/autotest/testsettings.cpp +++ b/src/plugins/autotest/testsettings.cpp @@ -42,6 +42,7 @@ static const char autoScrollKey[] = "AutoScrollResults"; static const char filterScanKey[] = "FilterScan"; static const char filtersKey[] = "WhiteListFilters"; static const char processArgsKey[] = "ProcessArgs"; +static const char groupSuffix[] = ".group"; static const int defaultTimeout = 60000; @@ -61,9 +62,11 @@ void TestSettings::toSettings(QSettings *s) const s->setValue(processArgsKey, processArgs); s->setValue(filterScanKey, filterScan); s->setValue(filtersKey, whiteListFilters); - // store frameworks and their current active state - for (const Core::Id &id : frameworks.keys()) + // store frameworks and their current active and grouping state + for (const Core::Id &id : frameworks.keys()) { s->setValue(QLatin1String(id.name()), frameworks.value(id)); + s->setValue(QLatin1String(id.name().append(groupSuffix)), frameworksGrouping.value(id)); + } s->endGroup(); } @@ -82,9 +85,14 @@ void TestSettings::fromSettings(QSettings *s) TestFrameworkManager *frameworkManager = TestFrameworkManager::instance(); const QList ®istered = frameworkManager->registeredFrameworkIds(); frameworks.clear(); + frameworksGrouping.clear(); for (const Core::Id &id : registered) { + // get their active state frameworks.insert(id, s->value(QLatin1String(id.name()), 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(); } diff --git a/src/plugins/autotest/testsettings.h b/src/plugins/autotest/testsettings.h index 449a56f322f..657ec4c0e27 100644 --- a/src/plugins/autotest/testsettings.h +++ b/src/plugins/autotest/testsettings.h @@ -50,6 +50,7 @@ struct TestSettings bool filterScan = false; bool processArgs = false; QHash frameworks; + QHash frameworksGrouping; QStringList whiteListFilters; }; diff --git a/src/plugins/autotest/testsettingspage.cpp b/src/plugins/autotest/testsettingspage.cpp index c7717364d68..852db0bdee4 100644 --- a/src/plugins/autotest/testsettingspage.cpp +++ b/src/plugins/autotest/testsettingspage.cpp @@ -164,7 +164,7 @@ TestSettings TestSettingsWidget::settings() const result.autoScroll = m_ui.autoScrollCB->isChecked(); result.processArgs = m_ui.processArgsCB->isChecked(); result.filterScan = m_ui.filterGroupBox->isChecked(); - result.frameworks = frameworks(); + frameworkSettings(result); result.whiteListFilters = filters(); return result; } @@ -180,6 +180,11 @@ void TestSettingsWidget::populateFrameworksListWidget(const QHashsetFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); item->setCheckState(0, frameworks.value(id) ? Qt::Checked : Qt::Unchecked); 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} ); } -QHash TestSettingsWidget::frameworks() const +void TestSettingsWidget::frameworkSettings(TestSettings &settings) const { - QHash frameworks; const QAbstractItemModel *model = m_ui.frameworkTreeWidget->model(); - QTC_ASSERT(model, return frameworks); + QTC_ASSERT(model, return); const int itemCount = model->rowCount(); for (int row = 0; row < itemCount; ++row) { - const QModelIndex index = model->index(row, 0); - frameworks.insert(Core::Id::fromSetting(index.data(Qt::UserRole)), - index.data(Qt::CheckStateRole) == Qt::Checked); + QModelIndex idx = model->index(row, 0); + const Core::Id id = Core::Id::fromSetting(idx.data(Qt::UserRole)); + 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 @@ -298,13 +303,20 @@ void TestSettingsPage::apply() bool frameworkSyncNecessary = newSettings.frameworks != m_settings->frameworks; bool forceReparse = newSettings.filterScan != m_settings->filterScan || newSettings.whiteListFilters.toSet() != m_settings->whiteListFilters.toSet(); + const QList 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->toSettings(Core::ICore::settings()); - TestFrameworkManager::instance()->activateFrameworksFromSettings(m_settings); + TestFrameworkManager *frameworkManager = TestFrameworkManager::instance(); + frameworkManager->activateFrameworksFromSettings(m_settings); if (frameworkSyncNecessary) TestTreeModel::instance()->syncTestFrameworks(); else if (forceReparse) TestTreeModel::instance()->parser()->emitUpdateTestTree(); + else if (!changedIds.isEmpty()) + TestTreeModel::instance()->rebuild(changedIds); } } // namespace Internal diff --git a/src/plugins/autotest/testsettingspage.h b/src/plugins/autotest/testsettingspage.h index 6ee67154c65..91c16d6705f 100644 --- a/src/plugins/autotest/testsettingspage.h +++ b/src/plugins/autotest/testsettingspage.h @@ -48,7 +48,7 @@ public: private: void populateFrameworksListWidget(const QHash &frameworks); void populateFiltersWidget(const QStringList &filters); - QHash frameworks() const; + void frameworkSettings(TestSettings &settings) const; QStringList filters() const; void onFrameworkItemChanged(); void onAddFilterClicked(); diff --git a/src/plugins/autotest/testsettingspage.ui b/src/plugins/autotest/testsettingspage.ui index 25949744740..5f7f867f3f4 100644 --- a/src/plugins/autotest/testsettingspage.ui +++ b/src/plugins/autotest/testsettingspage.ui @@ -183,12 +183,29 @@ Warning: this is an experimental feature and might lead to failing to execute th false - true + false + + 2 + + + 150 + Framework + + Selects the test frameworks to be handled by the AutoTest plugin. + + + + + Group + + + Enables grouping of test cases. + diff --git a/src/plugins/autotest/testtreeitem.cpp b/src/plugins/autotest/testtreeitem.cpp index de91ccd90bf..5c3b03724db 100644 --- a/src/plugins/autotest/testtreeitem.cpp +++ b/src/plugins/autotest/testtreeitem.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -43,21 +44,31 @@ TestTreeItem::TestTreeItem(const QString &name, const QString &filePath, Type ty m_filePath(filePath), m_type(type) { - m_checked = (m_type == TestCase || m_type == TestFunctionOrSet || m_type == Root) - ? Qt::Checked : Qt::Unchecked; + switch (m_type) { + 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 icons[] = { QIcon(), + Utils::Icons::OPENFILE.icon(), CPlusPlus::Icons::iconForType(CPlusPlus::Icons::ClassIconType), CPlusPlus::Icons::iconForType(CPlusPlus::Icons::SlotPrivateIconType), QIcon(":/images/data.png") }; if (int(type) >= int(sizeof icons / sizeof *icons)) - return icons[2]; + return icons[3]; return icons[type]; } @@ -76,6 +87,8 @@ QVariant TestTreeItem::data(int /*column*/, int role) const case Qt::CheckStateRole: return QVariant(); case LinkRole: { + if (m_type == GroupNode) + return QVariant(); QVariant itemLink; itemLink.setValue(Utils::Link(m_filePath, m_line, m_column)); return itemLink; @@ -105,6 +118,7 @@ Qt::ItemFlags TestTreeItem::flags(int /*column*/) const static const Qt::ItemFlags defaultFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; switch (m_type) { case Root: + case GroupNode: return Qt::ItemIsEnabled | Qt::ItemIsAutoTristate | Qt::ItemIsUserCheckable; case TestCase: return defaultFlags | Qt::ItemIsAutoTristate | Qt::ItemIsUserCheckable; @@ -164,6 +178,7 @@ void TestTreeItem::setChecked(const Qt::CheckState checkState) break; } case Root: + case GroupNode: case TestFunctionOrSet: case TestCase: { Qt::CheckState usedState = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked); @@ -185,6 +200,7 @@ Qt::CheckState TestTreeItem::checked() const { switch (m_type) { case Root: + case GroupNode: case TestCase: case TestFunctionOrSet: case TestDataTag: @@ -268,6 +284,9 @@ bool TestTreeItem::lessThan(const TestTreeItem *other, SortMode mode) const return index().row() > other->index().row(); return lhs > rhs; case Naturally: { + if (m_type == GroupNode && other->type() == GroupNode) + return m_filePath > other->filePath(); + const Utils::Link &leftLink = data(0, LinkRole).value(); const Utils::Link &rightLink = other->data(0, LinkRole).value(); 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 TestTreeItem::internalTargets() const { auto cppMM = CppTools::CppModelManager::instance(); @@ -301,7 +330,7 @@ QSet TestTreeItem::internalTargets() const void TestTreeItem::revalidateCheckState() { const Type ttiType = type(); - if (ttiType != TestCase && ttiType != TestFunctionOrSet && ttiType != Root) + if (ttiType != TestCase && ttiType != TestFunctionOrSet && ttiType != Root && ttiType != GroupNode) return; if (childCount() == 0) // can this happen? (we're calling revalidateCS() on parentItem() return; @@ -323,13 +352,13 @@ void TestTreeItem::revalidateCheckState() foundPartiallyChecked |= (child->checked() == Qt::PartiallyChecked); if (foundPartiallyChecked || (foundChecked && foundUnchecked)) { m_checked = Qt::PartiallyChecked; - if (ttiType == TestFunctionOrSet || ttiType == TestCase) + if (ttiType == TestFunctionOrSet || ttiType == TestCase || ttiType == GroupNode) parentItem()->revalidateCheckState(); return; } } m_checked = (foundUnchecked ? Qt::Unchecked : Qt::Checked); - if (ttiType == TestFunctionOrSet || ttiType == TestCase) + if (ttiType == TestFunctionOrSet || ttiType == TestCase || ttiType == GroupNode) parentItem()->revalidateCheckState(); } diff --git a/src/plugins/autotest/testtreeitem.h b/src/plugins/autotest/testtreeitem.h index f4a7d735db5..33d7117e4fe 100644 --- a/src/plugins/autotest/testtreeitem.h +++ b/src/plugins/autotest/testtreeitem.h @@ -55,6 +55,7 @@ public: enum Type { Root, + GroupNode, TestCase, TestFunctionOrSet, TestDataTag, @@ -112,7 +113,8 @@ public: virtual bool lessThan(const TestTreeItem *other, SortMode mode) const; virtual TestTreeItem *find(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 internalTargets() const; protected: typedef std::function CompareFunction; diff --git a/src/plugins/autotest/testtreemodel.cpp b/src/plugins/autotest/testtreemodel.cpp index 738ac5bbf00..63d83e54e6a 100644 --- a/src/plugins/autotest/testtreemodel.cpp +++ b/src/plugins/autotest/testtreemodel.cpp @@ -110,6 +110,7 @@ bool TestTreeModel::setData(const QModelIndex &index, const QVariant &value, int if (role == Qt::CheckStateRole) { switch (item->type()) { case TestTreeItem::Root: + case TestTreeItem::GroupNode: case TestTreeItem::TestCase: if (item->childCount() > 0) emit dataChanged(index.child(0, 0), index.child(item->childCount() - 1, 0)); @@ -174,6 +175,31 @@ void TestTreeModel::syncTestFrameworks() emit updatedActiveFrameworks(sortedIds.size()); } +void TestTreeModel::rebuild(const QList &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) { for (const QString &file : files) @@ -239,6 +265,24 @@ bool TestTreeModel::sweepChildren(TestTreeItem *item) 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) { TestTreeItem *rootNode @@ -249,10 +293,19 @@ void TestTreeModel::onParseResultReady(const TestParseResultPtr result) void TestTreeModel::handleParseResult(const TestParseResult *result, TestTreeItem *parentNode) { + const bool groupingEnabled = + TestFrameworkManager::instance()->groupingEnabled(result->frameworkId); // lookup existing items if (TestTreeItem *toBeModified = parentNode->find(result)) { // found existing item... Do not remove 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 if (toBeModified->modify(result)) { 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 TestTreeItem *newItem = result->createTestTreeItem(); QTC_ASSERT(newItem, return); - parentNode->appendChild(newItem); + + insertItemInParent(newItem, parentNode, groupingEnabled); // new items are checked by default - revalidation of parents might be necessary if (parentNode->checked() != Qt::Checked) { parentNode->revalidateCheckState(); diff --git a/src/plugins/autotest/testtreemodel.h b/src/plugins/autotest/testtreemodel.h index 2259cd64d31..a47fc9bea77 100644 --- a/src/plugins/autotest/testtreemodel.h +++ b/src/plugins/autotest/testtreemodel.h @@ -56,6 +56,7 @@ public: QList getSelectedTests() const; void syncTestFrameworks(); + void rebuild(const QList &frameworkIds); #ifdef WITH_TESTS int autoTestsCount() const; @@ -86,6 +87,7 @@ private: void removeTestRootNodes(); void removeFiles(const QStringList &files); bool sweepChildren(TestTreeItem *item); + static void insertItemInParent(TestTreeItem *item, TestTreeItem *root, bool groupingEnabled); explicit TestTreeModel(QObject *parent = 0); void setupParsingConnections();