AutoTest: Free TestTreeItem from CppTools dependency

Makes TestTreeItem programming language agnostic.

By moving the "query" methods to CppTools, the cohesion within these
methods is improved, i.e. information crosses the AutoTest <-> CppTools
border fewer times. Furthermore, it allows the CppTools plugin to see
how its data is being used, allowing it to optimize its queries
behind the scenes.

Change-Id: I0a60140abaca1193d500605dfa2812b4d937d94c
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Bernhard Beschow
2021-01-01 19:19:43 +01:00
parent 3f0f289b28
commit ac61bfdc90
11 changed files with 96 additions and 76 deletions

View File

@@ -30,6 +30,7 @@
#include "boosttestparser.h"
#include "../testframeworkmanager.h"
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/session.h>
#include <utils/qtcassert.h>
@@ -197,8 +198,10 @@ QList<ITestConfiguration *> BoostTestTreeItem::getAllTestConfigurations() const
++funcChildren;
});
if (funcChildren) {
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return);
testsPerProjectfile[item->proFile()].testCases += funcChildren;
testsPerProjectfile[item->proFile()].internalTargets.unite(item->internalTargets());
testsPerProjectfile[item->proFile()].internalTargets.unite(cppMM->internalTargets(item->filePath()));
}
});
@@ -236,6 +239,8 @@ QList<ITestConfiguration *> BoostTestTreeItem::getTestConfigurations(
if (!item->enabled()) // ignore child tests known to be disabled when using run selected
return;
if (predicate(item)) {
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return);
QString tcName = item->name();
if (item->state().testFlag(BoostTestTreeItem::Templated))
tcName.append("<*");
@@ -244,7 +249,7 @@ QList<ITestConfiguration *> BoostTestTreeItem::getTestConfigurations(
tcName = handleSpecialFunctionNames(tcName);
testCasesForProjectFile[item->proFile()].testCases.append(
item->prependWithParentsSuitePaths(tcName));
testCasesForProjectFile[item->proFile()].internalTargets.unite(item->internalTargets());
testCasesForProjectFile[item->proFile()].internalTargets.unite(cppMM->internalTargets(item->filePath()));
}
});
@@ -280,6 +285,8 @@ ITestConfiguration *BoostTestTreeItem::testConfiguration() const
{
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
const Type itemType = type();
if (itemType == TestSuite || itemType == TestCase) {
@@ -313,7 +320,7 @@ ITestConfiguration *BoostTestTreeItem::testConfiguration() const
config->setProjectFile(proFile());
config->setProject(project);
config->setTestCases(testCases);
config->setInternalTargets(internalTargets());
config->setInternalTargets(cppMM->internalTargets(filePath()));
return config;
}
return nullptr;

View File

@@ -27,6 +27,7 @@
#include "catchconfiguration.h"
#include "catchframework.h"
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <utils/qtcassert.h>
@@ -157,6 +158,8 @@ ITestConfiguration *CatchTreeItem::testConfiguration() const
{
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
if (type() != TestCase)
return nullptr;
@@ -167,7 +170,7 @@ ITestConfiguration *CatchTreeItem::testConfiguration() const
config->setProjectFile(proFile());
config->setProject(project);
config->setTestCases(QStringList(testCasesString()));
config->setInternalTargets(internalTargets());
config->setInternalTargets(cppMM->internalTargets(filePath()));
return config;
}
@@ -190,6 +193,8 @@ static void collectTestInfo(const TestTreeItem *item,
bool ignoreCheckState)
{
QTC_ASSERT(item, return);
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return);
const int childCount = item->childCount();
if (item->type() == TestTreeItem::GroupNode) {
item->forFirstLevelChildItems([&testCasesForProfile, ignoreCheckState](TestTreeItem *it) {
@@ -206,15 +211,15 @@ static void collectTestInfo(const TestTreeItem *item,
CatchTreeItem *current = static_cast<CatchTreeItem *>(it);
testCasesForProfile[projectFile].names.append(current->testCasesString());
});
testCasesForProfile[projectFile].internalTargets.unite(item->internalTargets());
testCasesForProfile[projectFile].internalTargets.unite(cppMM->internalTargets(item->filePath()));
} else if (item->checked() == Qt::PartiallyChecked) {
item->forFirstLevelChildItems([&testCasesForProfile](TestTreeItem *child) {
item->forFirstLevelChildItems([&testCasesForProfile, cppMM](TestTreeItem *child) {
QTC_ASSERT(child->type() == TestTreeItem::TestCase, return);
if (child->checked() == Qt::Checked) {
CatchTreeItem *current = static_cast<CatchTreeItem *>(child);
testCasesForProfile[child->proFile()].names.append(current->testCasesString());
testCasesForProfile[child->proFile()].internalTargets.unite(
child->internalTargets());
cppMM->internalTargets(child->filePath()));
}
});
@@ -230,11 +235,13 @@ static void collectFailedTestInfo(const CatchTreeItem *item,
item->forAllChildItems([&testCasesForProfile](TestTreeItem *it) {
QTC_ASSERT(it, return);
QTC_ASSERT(it->parentItem(), return);
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return);
if (it->type() == TestTreeItem::TestCase && it->data(0, FailedRole).toBool()) {
CatchTreeItem *current = static_cast<CatchTreeItem *>(it);
testCasesForProfile[it->proFile()].names.append(current->testCasesString());
testCasesForProfile[it->proFile()].internalTargets.unite(
it->internalTargets());
cppMM->internalTargets(it->filePath()));
}
});
}
@@ -276,6 +283,9 @@ QList<ITestConfiguration *> CatchTreeItem::getFailedTestConfigurations() const
QList<ITestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Utils::FilePath &fileName) const
{
QList<ITestConfiguration *> result;
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return result);
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
if (!project || type() != Root)
return result;
@@ -300,7 +310,7 @@ QList<ITestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Ut
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
testConfig->setInternalTargets(item->internalTargets());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
result << testConfig;
}

View File

@@ -116,6 +116,8 @@ static bool matchesFilter(const QString &filter, const QString &fullTestName)
return positive.isEmpty();
}
QSet<QString> internalTargets(const TestTreeItem &item);
QVariant GTestTreeItem::data(int column, int role) const
{
switch (role) {
@@ -195,7 +197,7 @@ ITestConfiguration *GTestTreeItem::testConfiguration() const
return nullptr;
}
if (config)
config->setInternalTargets(internalTargets());
config->setInternalTargets(internalTargets(*this));
return config;
}
@@ -234,7 +236,7 @@ static void collectTestInfo(const GTestTreeItem *item,
testCasesForProFile[projectFile].filters.append(
gtestFilter(item->state()).arg(item->name()).arg('*'));
testCasesForProFile[projectFile].testSetCount += childCount - 1;
testCasesForProFile[projectFile].internalTargets.unite(item->internalTargets());
testCasesForProFile[projectFile].internalTargets.unite(internalTargets(*item));
} else if (item->checked() == Qt::PartiallyChecked) {
item->forFirstLevelChildItems([&testCasesForProFile, item](TestTreeItem *child){
QTC_ASSERT(child->type() == TestTreeItem::TestCase, return);
@@ -242,7 +244,7 @@ static void collectTestInfo(const GTestTreeItem *item,
testCasesForProFile[child->proFile()].filters.append(
gtestFilter(item->state()).arg(item->name()).arg(child->name()));
testCasesForProFile[child->proFile()].internalTargets.unite(
child->internalTargets());
internalTargets(*child));
}
});
}
@@ -262,7 +264,7 @@ static void collectFailedTestInfo(const GTestTreeItem *item,
testCasesForProfile[it->proFile()].filters.append(
gtestFilter(parent->state()).arg(parent->name()).arg(it->name()));
testCasesForProfile[it->proFile()].internalTargets.unite(
it->internalTargets());
internalTargets(*it));
}
});
}
@@ -348,7 +350,7 @@ QList<ITestConfiguration *> GTestTreeItem::getTestConfigurationsForFile(const Ut
GTestCases &cases = testCases[testCase->proFile()];
cases.filters.append(
gtestFilter(testCase->state()).arg(testCase->name(), node->name()));
cases.internalTargets.unite(node->internalTargets());
cases.internalTargets.unite(internalTargets(*node));
}
});
for (auto it = testCases.begin(), end = testCases.end(); it != end; ++it) {
@@ -517,23 +519,23 @@ QString GTestTreeItem::nameSuffix() const
return suffix;
}
QSet<QString> GTestTreeItem::internalTargets() const
QSet<QString> internalTargets(const TestTreeItem &item)
{
QSet<QString> result;
const auto cppMM = CppTools::CppModelManager::instance();
const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
const QString file = filePath();
const QString file = item.filePath();
const QVector<CppTools::ProjectPart::Ptr> projectParts = projectInfo.projectParts();
if (projectParts.isEmpty())
return TestTreeItem::dependingInternalTargets(cppMM, file);
return cppMM->dependingInternalTargets(file);
for (const CppTools::ProjectPart::Ptr &projectPart : projectParts) {
if (projectPart->projectFile == proFile()
if (projectPart->projectFile == item.proFile()
&& Utils::anyOf(projectPart->files, [&file] (const CppTools::ProjectFile &pf) {
return pf.path == file;
})) {
result.insert(projectPart->buildSystemTarget);
if (projectPart->buildTargetType != ProjectExplorer::BuildTargetType::Executable)
result.unite(TestTreeItem::dependingInternalTargets(cppMM, file));
result.unite(cppMM->dependingInternalTargets(file));
}
}
return result;

View File

@@ -75,7 +75,6 @@ public:
GTestTreeItem::TestStates state,
const QString &proFile) const;
QString nameSuffix() const;
QSet<QString> internalTargets() const override;
bool isGroupNodeFor(const TestTreeItem *other) const override;
bool isGroupable() const override;
TestTreeItem *applyFilters() override;

View File

@@ -28,6 +28,7 @@
#include "qttestparser.h"
#include "qttestframework.h"
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/session.h>
#include <utils/qtcassert.h>
@@ -111,6 +112,8 @@ ITestConfiguration *QtTestTreeItem::testConfiguration() const
{
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
QTC_ASSERT(project, return nullptr);
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return nullptr);
QtTestConfiguration *config = nullptr;
switch (type()) {
@@ -144,13 +147,15 @@ ITestConfiguration *QtTestTreeItem::testConfiguration() const
return nullptr;
}
if (config)
config->setInternalTargets(internalTargets());
config->setInternalTargets(cppMM->internalTargets(filePath()));
return config;
}
static void fillTestConfigurationsFromCheckState(const TestTreeItem *item,
QList<ITestConfiguration *> &testConfigurations)
{
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return);
QTC_ASSERT(item, return);
if (item->type() == TestTreeItem::GroupNode) {
for (int row = 0, count = item->childCount(); row < count; ++row)
@@ -185,13 +190,15 @@ static void fillTestConfigurationsFromCheckState(const TestTreeItem *item,
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
testConfig->setInternalTargets(item->internalTargets());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
testConfigurations << testConfig;
}
}
static void collectFailedTestInfo(TestTreeItem *item, QList<ITestConfiguration *> &testConfigs)
{
const auto cppMM = CppTools::CppModelManager::instance();
QTC_ASSERT(cppMM, return);
QTC_ASSERT(item, return);
if (item->type() == TestTreeItem::GroupNode) {
for (int row = 0, count = item->childCount(); row < count; ++row)
@@ -217,7 +224,7 @@ static void collectFailedTestInfo(TestTreeItem *item, QList<ITestConfiguration *
testConfig->setTestCases(testCases);
testConfig->setProjectFile(item->proFile());
testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
testConfig->setInternalTargets(item->internalTargets());
testConfig->setInternalTargets(cppMM->internalTargets(item->filePath()));
testConfigs << testConfig;
}

View File

@@ -36,6 +36,8 @@
namespace Autotest {
namespace Internal {
QSet<QString> internalTargets(const QString &proFile);
TestTreeItem *QuickTestTreeItem::copyWithoutChildren()
{
QuickTestTreeItem *copied = new QuickTestTreeItem(framework());
@@ -156,7 +158,7 @@ ITestConfiguration *QuickTestTreeItem::testConfiguration() const
return nullptr;
}
if (config)
config->setInternalTargets(internalTargets());
config->setInternalTargets(internalTargets(proFile()));
return config;
}
@@ -189,7 +191,7 @@ static QList<ITestConfiguration *> testConfigurationsFor(
auto tc = new QuickTestConfiguration(treeItem->framework());
tc->setProjectFile(treeItem->proFile());
tc->setProject(ProjectExplorer::SessionManager::startupProject());
tc->setInternalTargets(treeItem->internalTargets());
tc->setInternalTargets(internalTargets(treeItem->proFile()));
it = configurationForProFiles.insert(treeItem->proFile(), tc);
}
it.value()->setTestCases(it.value()->testCases() + functions);
@@ -216,7 +218,7 @@ struct Tests {
static void addTestsForItem(Tests &tests, const TestTreeItem *item)
{
tests.testCount += item->childCount();
tests.internalTargets = item->internalTargets();
tests.internalTargets = internalTargets(item->proFile());
}
QList<ITestConfiguration *> QuickTestTreeItem::getAllTestConfigurations() const
@@ -234,7 +236,7 @@ QList<ITestConfiguration *> QuickTestTreeItem::getAllTestConfigurations() const
child->forFirstLevelChildItems([&testsForProfile](TestTreeItem *grandChild) {
const QString &proFile = grandChild->proFile();
++(testsForProfile[proFile].testCount);
testsForProfile[proFile].internalTargets = grandChild->internalTargets();
testsForProfile[proFile].internalTargets = internalTargets(grandChild->proFile());
});
return;
}
@@ -384,7 +386,7 @@ bool QuickTestTreeItem::isGroupable() const
return type() == TestCase && !name().isEmpty() && !filePath().isEmpty();
}
QSet<QString> QuickTestTreeItem::internalTargets() const
QSet<QString> internalTargets(const QString &proFile)
{
QSet<QString> result;
const auto cppMM = CppTools::CppModelManager::instance();
@@ -392,7 +394,7 @@ QSet<QString> QuickTestTreeItem::internalTargets() const
for (const CppTools::ProjectPart::Ptr &projectPart : projectInfo.projectParts()) {
if (projectPart->buildTargetType != ProjectExplorer::BuildTargetType::Executable)
continue;
if (projectPart->projectFile == proFile())
if (projectPart->projectFile == proFile)
result.insert(projectPart->buildSystemTarget);
}
return result;

View File

@@ -59,7 +59,6 @@ public:
bool removeOnSweepIfEmpty() const override;
TestTreeItem *createParentGroupNode() const override;
bool isGroupable() const override;
QSet<QString> internalTargets() const override;
void markForRemovalRecursively(const QString &filePath) override;
private:
TestTreeItem *findChildByFileNameAndType(const QString &filePath, const QString &name,

View File

@@ -30,9 +30,6 @@
#include "itestparser.h"
#include "testconfiguration.h"
#include <cplusplus/Icons.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cpptoolsreuse.h>
#include <texteditor/texteditor.h>
#include <utils/utilsicons.h>
@@ -320,22 +317,6 @@ bool TestTreeItem::isGroupable() const
return true;
}
QSet<QString> TestTreeItem::internalTargets() const
{
auto cppMM = CppTools::CppModelManager::instance();
const QList<CppTools::ProjectPart::Ptr> projectParts = cppMM->projectPart(filePath());
// if we have no project parts it's most likely a header with declarations only and CMake based
if (projectParts.isEmpty())
return TestTreeItem::dependingInternalTargets(cppMM, filePath());
QSet<QString> targets;
for (const CppTools::ProjectPart::Ptr &part : projectParts) {
targets.insert(part->buildSystemTarget);
if (part->buildTargetType != ProjectExplorer::BuildTargetType::Executable)
targets.unite(TestTreeItem::dependingInternalTargets(cppMM, filePath()));
}
return targets;
}
void TestTreeItem::forAllChildItems(const std::function<void(TestTreeItem *)> &pred) const
{
for (int row = 0, end = childCount(); row < end; ++row) {
@@ -401,27 +382,4 @@ ITestFramework *TestTreeItem::framework() const
return static_cast<ITestFramework *>(testBase());
}
/*
* try to find build system target that depends on the given file - if the file is no header
* try to find the corresponding header and use this instead to find the respective target
*/
QSet<QString> TestTreeItem::dependingInternalTargets(CppTools::CppModelManager *cppMM,
const QString &file)
{
QSet<QString> result;
QTC_ASSERT(cppMM, return result);
const CPlusPlus::Snapshot snapshot = cppMM->snapshot();
QTC_ASSERT(snapshot.contains(file), return result);
bool wasHeader;
const QString correspondingFile
= CppTools::correspondingHeaderOrSource(file, &wasHeader, CppTools::CacheUsage::ReadOnly);
const Utils::FilePaths dependingFiles = snapshot.filesDependingOn(
wasHeader ? file : correspondingFile);
for (const Utils::FilePath &fn : dependingFiles) {
for (const CppTools::ProjectPart::Ptr &part : cppMM->projectPart(fn))
result.insert(part->buildSystemTarget);
}
return result;
}
} // namespace Autotest

View File

@@ -42,7 +42,6 @@ namespace {
};
}
namespace CppTools { class CppModelManager; }
namespace Utils { class FilePath; }
namespace Autotest {
@@ -166,7 +165,6 @@ public:
virtual TestTreeItem *applyFilters() { return nullptr; }
// decide whether an item should still be added to the treemodel
virtual bool shouldBeAddedAfterFiltering() const { return true; }
virtual QSet<QString> internalTargets() const;
void forAllChildItems(const std::function<void(TestTreeItem *)> &pred) const;
void forFirstLevelChildItems(const std::function<void(TestTreeItem *)> &pred) const;
@@ -174,8 +172,6 @@ public:
protected:
void copyBasicDataFrom(const TestTreeItem *other);
typedef std::function<bool(const TestTreeItem *)> CompareFunction;
static QSet<QString> dependingInternalTargets(CppTools::CppModelManager *cppMM,
const QString &file);
private:
bool modifyFilePath(const QString &filepath);

View File

@@ -1389,6 +1389,38 @@ void CppModelManager::onAboutToLoadSession()
GC();
}
QSet<QString> CppModelManager::dependingInternalTargets(const QString &file) const
{
QSet<QString> result;
const Snapshot snapshot = this->snapshot();
QTC_ASSERT(snapshot.contains(file), return result);
bool wasHeader;
const QString correspondingFile
= correspondingHeaderOrSource(file, &wasHeader, CacheUsage::ReadOnly);
const Utils::FilePaths dependingFiles = snapshot.filesDependingOn(
wasHeader ? file : correspondingFile);
for (const Utils::FilePath &fn : qAsConst(dependingFiles)) {
for (const ProjectPart::Ptr &part : projectPart(fn))
result.insert(part->buildSystemTarget);
}
return result;
}
QSet<QString> CppModelManager::internalTargets(const QString &filePath) const
{
const QList<ProjectPart::Ptr> projectParts = projectPart(filePath);
// if we have no project parts it's most likely a header with declarations only and CMake based
if (projectParts.isEmpty())
return dependingInternalTargets(filePath);
QSet<QString> targets;
for (const ProjectPart::Ptr &part : projectParts) {
targets.insert(part->buildSystemTarget);
if (part->buildTargetType != ProjectExplorer::BuildTargetType::Executable)
targets.unite(dependingInternalTargets(filePath));
}
return targets;
}
void CppModelManager::renameIncludes(const QString &oldFileName, const QString &newFileName)
{
if (oldFileName.isEmpty() || newFileName.isEmpty())

View File

@@ -230,6 +230,14 @@ public:
Core::IFindFilter *symbolsFindFilter() const;
Core::ILocatorFilter *currentDocumentFilter() const;
/*
* try to find build system target that depends on the given file - if the file is no header
* try to find the corresponding header and use this instead to find the respective target
*/
QSet<QString> dependingInternalTargets(const QString &file) const;
QSet<QString> internalTargets(const QString &filePath) const;
void renameIncludes(const QString &oldFileName, const QString &newFileName);
// for VcsBaseSubmitEditor