AutoTest: Fix handling of multiple build targets

If project files are not mapped 1:1 to targets the result
of the chosen executable was more or less random.
Try to handle multiple targets as correct as possible by
checking for build targets already where we still know
which files are part of the respective test cases.

Task-number: QTCREATORBUG-17783
Task-number: QTCREATORBUG-18357
Change-Id: I82dcc26bf52c9918e2727b439a719af08879ef49
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Christian Stenger
2017-06-09 09:30:21 +02:00
parent bde8ebc56c
commit cce1e130d7
13 changed files with 78 additions and 23 deletions

View File

@@ -105,6 +105,8 @@ TestConfiguration *GTestTreeItem::testConfiguration() const
default: default:
return nullptr; return nullptr;
} }
if (config)
config->setInternalTargets(internalTargets());
return config; return config;
} }
@@ -153,6 +155,7 @@ QList<TestConfiguration *> GTestTreeItem::getAllTestConfigurations() const
return result; return result;
QHash<ProFileWithDisplayName, int> proFilesWithTestSets; QHash<ProFileWithDisplayName, int> proFilesWithTestSets;
QHash<ProFileWithDisplayName, QSet<QString> > proFilesWithInternalTargets;
for (int row = 0, count = childCount(); row < count; ++row) { for (int row = 0, count = childCount(); row < count; ++row) {
const GTestTreeItem *child = static_cast<const GTestTreeItem *>(childItem(row)); const GTestTreeItem *child = static_cast<const GTestTreeItem *>(childItem(row));
@@ -164,6 +167,7 @@ QList<TestConfiguration *> GTestTreeItem::getAllTestConfigurations() const
grandChild->proFile())); grandChild->proFile()));
proFilesWithTestSets.insert(key, proFilesWithTestSets[key] + 1); proFilesWithTestSets.insert(key, proFilesWithTestSets[key] + 1);
proFilesWithInternalTargets.insert(key, grandChild->internalTargets());
} }
} }
@@ -175,6 +179,7 @@ QList<TestConfiguration *> GTestTreeItem::getAllTestConfigurations() const
tc->setTestCaseCount(it.value()); tc->setTestCaseCount(it.value());
tc->setProjectFile(key.proFile); tc->setProjectFile(key.proFile);
tc->setProject(project); tc->setProject(project);
tc->setInternalTargets(proFilesWithInternalTargets.value(key));
result << tc; result << tc;
} }
@@ -202,6 +207,7 @@ QList<TestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const
return result; return result;
QHash<ProFileWithDisplayName, TestCases> proFilesWithCheckedTestSets; QHash<ProFileWithDisplayName, TestCases> proFilesWithCheckedTestSets;
QHash<ProFileWithDisplayName, QSet<QString> > proFilesWithInternalTargets;
for (int row = 0, count = childCount(); row < count; ++row) { for (int row = 0, count = childCount(); row < count; ++row) {
const GTestTreeItem *child = static_cast<const GTestTreeItem *>(childItem(row)); const GTestTreeItem *child = static_cast<const GTestTreeItem *>(childItem(row));
@@ -215,6 +221,8 @@ QList<TestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const
auto &testCases = proFilesWithCheckedTestSets[createProfile(child->childItem(0))]; auto &testCases = proFilesWithCheckedTestSets[createProfile(child->childItem(0))];
testCases.filters.append(gtestFilter(child->state()).arg(child->name()).arg('*')); testCases.filters.append(gtestFilter(child->state()).arg(child->name()).arg('*'));
testCases.additionalTestCaseCount += grandChildCount - 1; testCases.additionalTestCaseCount += grandChildCount - 1;
proFilesWithInternalTargets.insert(createProfile(child->childItem(0)),
child->internalTargets());
break; break;
} }
case Qt::PartiallyChecked: { case Qt::PartiallyChecked: {
@@ -223,6 +231,8 @@ QList<TestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const
if (grandChild->checked() == Qt::Checked) { if (grandChild->checked() == Qt::Checked) {
proFilesWithCheckedTestSets[createProfile(grandChild)].filters.append( proFilesWithCheckedTestSets[createProfile(grandChild)].filters.append(
gtestFilter(child->state()).arg(child->name()).arg(grandChild->name())); gtestFilter(child->state()).arg(child->name()).arg(grandChild->name()));
proFilesWithInternalTargets.insert(createProfile(grandChild),
grandChild->internalTargets());
} }
} }
break; break;
@@ -239,6 +249,7 @@ QList<TestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const
tc->setTestCaseCount(tc->testCaseCount() + it.value().additionalTestCaseCount); tc->setTestCaseCount(tc->testCaseCount() + it.value().additionalTestCaseCount);
tc->setProjectFile(proFileWithDisplayName.proFile); tc->setProjectFile(proFileWithDisplayName.proFile);
tc->setProject(project); tc->setProject(project);
tc->setInternalTargets(proFilesWithInternalTargets[proFileWithDisplayName]);
result << tc; result << tc;
} }

View File

@@ -135,6 +135,8 @@ TestConfiguration *QtTestTreeItem::testConfiguration() const
default: default:
return nullptr; return nullptr;
} }
if (config)
config->setInternalTargets(internalTargets());
return config; return config;
} }
@@ -161,6 +163,7 @@ QList<TestConfiguration *> QtTestTreeItem::getAllTestConfigurations() const
tc->setTestCaseCount(child->childCount()); tc->setTestCaseCount(child->childCount());
tc->setProjectFile(child->proFile()); tc->setProjectFile(child->proFile());
tc->setProject(project); tc->setProject(project);
tc->setInternalTargets(child->internalTargets());
result << tc; result << tc;
} }
return result; return result;
@@ -186,6 +189,7 @@ QList<TestConfiguration *> QtTestTreeItem::getSelectedTestConfigurations() const
testConfiguration->setTestCaseCount(child->childCount()); testConfiguration->setTestCaseCount(child->childCount());
testConfiguration->setProjectFile(child->proFile()); testConfiguration->setProjectFile(child->proFile());
testConfiguration->setProject(project); testConfiguration->setProject(project);
testConfiguration->setInternalTargets(child->internalTargets());
result << testConfiguration; result << testConfiguration;
continue; continue;
case Qt::PartiallyChecked: case Qt::PartiallyChecked:
@@ -211,6 +215,7 @@ QList<TestConfiguration *> QtTestTreeItem::getSelectedTestConfigurations() const
testConfiguration->setTestCases(testCases); testConfiguration->setTestCases(testCases);
testConfiguration->setProjectFile(child->proFile()); testConfiguration->setProjectFile(child->proFile());
testConfiguration->setProject(project); testConfiguration->setProject(project);
testConfiguration->setInternalTargets(child->internalTargets());
result << testConfiguration; result << testConfiguration;
} }
} }

View File

@@ -27,6 +27,7 @@
#include "quicktestconfiguration.h" #include "quicktestconfiguration.h"
#include "quicktestparser.h" #include "quicktestparser.h"
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/session.h> #include <projectexplorer/session.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -138,6 +139,8 @@ TestConfiguration *QuickTestTreeItem::testConfiguration() const
default: default:
return nullptr; return nullptr;
} }
if (config)
config->setInternalTargets(internalTargets());
return config; return config;
} }
@@ -150,6 +153,7 @@ QList<TestConfiguration *> QuickTestTreeItem::getAllTestConfigurations() const
return result; return result;
QHash<QString, int> foundProFiles; QHash<QString, int> foundProFiles;
QHash<QString, QSet<QString> > proFilesWithTargets;
for (int row = 0, count = childCount(); row < count; ++row) { for (int row = 0, count = childCount(); row < count; ++row) {
const TestTreeItem *child = childItem(row); const TestTreeItem *child = childItem(row);
// unnamed Quick Tests must be handled separately // unnamed Quick Tests must be handled separately
@@ -158,12 +162,14 @@ QList<TestConfiguration *> QuickTestTreeItem::getAllTestConfigurations() const
const TestTreeItem *grandChild = child->childItem(childRow); const TestTreeItem *grandChild = child->childItem(childRow);
const QString &proFile = grandChild->proFile(); const QString &proFile = grandChild->proFile();
foundProFiles.insert(proFile, foundProFiles[proFile] + 1); foundProFiles.insert(proFile, foundProFiles[proFile] + 1);
proFilesWithTargets.insert(proFile, grandChild->internalTargets());
} }
continue; continue;
} }
// named Quick Test // named Quick Test
const QString &proFile = child->proFile(); const QString &proFile = child->proFile();
foundProFiles.insert(proFile, foundProFiles[proFile] + child->childCount()); foundProFiles.insert(proFile, foundProFiles[proFile] + child->childCount());
proFilesWithTargets.insert(proFile, child->internalTargets());
} }
// create TestConfiguration for each project file // create TestConfiguration for each project file
QHash<QString, int>::ConstIterator it = foundProFiles.begin(); QHash<QString, int>::ConstIterator it = foundProFiles.begin();
@@ -173,6 +179,7 @@ QList<TestConfiguration *> QuickTestTreeItem::getAllTestConfigurations() const
tc->setTestCaseCount(it.value()); tc->setTestCaseCount(it.value());
tc->setProjectFile(it.key()); tc->setProjectFile(it.key());
tc->setProject(project); tc->setProject(project);
tc->setInternalTargets(proFilesWithTargets[it.key()]);
result << tc; result << tc;
} }
return result; return result;
@@ -203,6 +210,7 @@ QList<TestConfiguration *> QuickTestTreeItem::getSelectedTestConfigurations() co
tc->setUnnamedOnly(true); tc->setUnnamedOnly(true);
tc->setProjectFile(proFile); tc->setProjectFile(proFile);
tc->setProject(project); tc->setProject(project);
tc->setInternalTargets(grandChild->internalTargets());
foundProFiles.insert(proFile, tc); foundProFiles.insert(proFile, tc);
} }
} }
@@ -246,6 +254,7 @@ QList<TestConfiguration *> QuickTestTreeItem::getSelectedTestConfigurations() co
tc->setTestCases(testFunctions); tc->setTestCases(testFunctions);
tc->setProjectFile(child->proFile()); tc->setProjectFile(child->proFile());
tc->setProject(project); tc->setProject(project);
tc->setInternalTargets(child->internalTargets());
foundProFiles.insert(child->proFile(), tc); foundProFiles.insert(child->proFile(), tc);
} }
break; break;
@@ -307,6 +316,20 @@ bool QuickTestTreeItem::lessThan(const TestTreeItem *other, TestTreeItem::SortMo
return TestTreeItem::lessThan(other, mode); return TestTreeItem::lessThan(other, mode);
} }
QSet<QString> QuickTestTreeItem::internalTargets() const
{
QSet<QString> result;
const auto cppMM = CppTools::CppModelManager::instance();
const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
for (const CppTools::ProjectPart::Ptr projectPart : projectInfo.projectParts()) {
if (projectPart->projectFile == proFile()) {
result.insert(projectPart->buildSystemTarget);
break;
}
}
return result;
}
TestTreeItem *QuickTestTreeItem::unnamedQuickTests() const TestTreeItem *QuickTestTreeItem::unnamedQuickTests() const
{ {
if (type() != Root) if (type() != Root)

View File

@@ -45,7 +45,7 @@ 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;
QSet<QString> internalTargets() const override;
private: private:
TestTreeItem *unnamedQuickTests() const; TestTreeItem *unnamedQuickTests() const;
}; };

View File

@@ -64,6 +64,7 @@ static bool isLocal(RunConfiguration *runConfiguration)
void TestConfiguration::completeTestInformation(int runMode) void TestConfiguration::completeTestInformation(int runMode)
{ {
QTC_ASSERT(!m_projectFile.isEmpty(), return); QTC_ASSERT(!m_projectFile.isEmpty(), return);
QTC_ASSERT(!m_buildTargets.isEmpty(), return);
Project *project = SessionManager::startupProject(); Project *project = SessionManager::startupProject();
if (!project) if (!project)
@@ -73,23 +74,16 @@ void TestConfiguration::completeTestInformation(int runMode)
if (!target) if (!target)
return; return;
const auto cppMM = CppTools::CppModelManager::instance(); const QSet<QString> buildSystemTargets = m_buildTargets;
const QVector<CppTools::ProjectPart::Ptr> projectParts = cppMM->projectInfo(project).projectParts();
const QVector<CppTools::ProjectPart::Ptr> relevantParts
= Utils::filtered(projectParts, [this] (const CppTools::ProjectPart::Ptr &part) {
return part->selectedForBuilding && part->projectFile == m_projectFile;
});
const QSet<QString> buildSystemTargets
= Utils::transform<QSet>(relevantParts, [] (const CppTools::ProjectPart::Ptr &part) {
return part->buildSystemTarget;
});
const Utils::FileName fn = Utils::FileName::fromString(m_projectFile);
const BuildTargetInfo targetInfo const BuildTargetInfo targetInfo
= Utils::findOrDefault(target->applicationTargets().list, = Utils::findOrDefault(target->applicationTargets().list,
[&buildSystemTargets, &fn] (const BuildTargetInfo &bti) { [&buildSystemTargets] (const BuildTargetInfo &bti) {
return Utils::anyOf(buildSystemTargets, [&fn, &bti](const QString &b) { return Utils::anyOf(buildSystemTargets, [&bti](const QString &b) {
return b == bti.targetName || (b.contains(bti.targetName) && bti.projectFilePath == fn); const QStringList targWithProjectFile = b.split('|');
if (targWithProjectFile.size() != 2) // some build targets might miss the project file
return false;
return targWithProjectFile.at(0) == bti.targetName
&& targWithProjectFile.at(1).startsWith(bti.projectFilePath.toString());
}); });
}); });
const Utils::FileName executable = targetInfo.targetFilePath; // empty if BTI is default created const Utils::FileName executable = targetInfo.targetFilePath; // empty if BTI is default created
@@ -97,7 +91,8 @@ void TestConfiguration::completeTestInformation(int runMode)
if (!isLocal(runConfig)) // TODO add device support if (!isLocal(runConfig)) // TODO add device support
continue; continue;
if (buildSystemTargets.contains(runConfig->buildSystemTarget())) { const QString bst = runConfig->buildSystemTarget() + '|' + m_projectFile;
if (buildSystemTargets.contains(bst)) {
Runnable runnable = runConfig->runnable(); Runnable runnable = runConfig->runnable();
if (!runnable.is<StandardRunnable>()) if (!runnable.is<StandardRunnable>())
continue; continue;
@@ -146,7 +141,7 @@ void TestConfiguration::completeTestInformation(int runMode)
} }
if (m_displayName.isEmpty()) // happens e.g. when guessing the TestConfiguration or error if (m_displayName.isEmpty()) // happens e.g. when guessing the TestConfiguration or error
m_displayName = buildSystemTargets.isEmpty() ? "unknown" : *buildSystemTargets.begin(); m_displayName = buildSystemTargets.isEmpty() ? "unknown" : (*buildSystemTargets.begin()).split('|').first();
} }
/** /**
@@ -204,6 +199,11 @@ void TestConfiguration::setProject(Project *project)
m_project = project; m_project = project;
} }
void TestConfiguration::setInternalTargets(const QSet<QString> &targets)
{
m_buildTargets = targets;
}
QString TestConfiguration::executableFilePath() const QString TestConfiguration::executableFilePath() const
{ {
if (m_executableFile.isEmpty()) if (m_executableFile.isEmpty())

View File

@@ -66,6 +66,7 @@ public:
void setDisplayName(const QString &displayName); void setDisplayName(const QString &displayName);
void setEnvironment(const Utils::Environment &env); void setEnvironment(const Utils::Environment &env);
void setProject(ProjectExplorer::Project *project); void setProject(ProjectExplorer::Project *project);
void setInternalTargets(const QSet<QString> &targets);
QStringList testCases() const { return m_testCases; } QStringList testCases() const { return m_testCases; }
int testCaseCount() const { return m_testCaseCount; } int testCaseCount() const { return m_testCaseCount; }
@@ -97,6 +98,7 @@ private:
QPointer<ProjectExplorer::Project> m_project; QPointer<ProjectExplorer::Project> m_project;
bool m_guessedConfiguration = false; bool m_guessedConfiguration = false;
TestRunConfiguration *m_runConfig = 0; TestRunConfiguration *m_runConfig = 0;
QSet<QString> m_buildTargets;
}; };
class DebuggableTestConfiguration : public TestConfiguration class DebuggableTestConfiguration : public TestConfiguration

View File

@@ -30,6 +30,7 @@
#include "testtreeitem.h" #include "testtreeitem.h"
#include <cplusplus/Icons.h> #include <cplusplus/Icons.h>
#include <cpptools/cppmodelmanager.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <QIcon> #include <QIcon>
@@ -284,6 +285,16 @@ bool TestTreeItem::lessThan(const TestTreeItem *other, SortMode mode) const
} }
} }
QSet<QString> TestTreeItem::internalTargets() const
{
auto cppMM = CppTools::CppModelManager::instance();
const QList<CppTools::ProjectPart::Ptr> projectParts = cppMM->projectPart(filePath());
QSet<QString> targets;
for (const CppTools::ProjectPart::Ptr part : projectParts)
targets.insert(part->buildSystemTarget);
return targets;
}
void TestTreeItem::revalidateCheckState() void TestTreeItem::revalidateCheckState()
{ {
const Type ttiType = type(); const Type ttiType = type();

View File

@@ -111,6 +111,7 @@ public:
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 QSet<QString> internalTargets() const;
protected: protected:
bool modifyFilePath(const QString &filePath); bool modifyFilePath(const QString &filePath);
typedef std::function<bool(const TestTreeItem *)> CompareFunction; typedef std::function<bool(const TestTreeItem *)> CompareFunction;

View File

@@ -498,8 +498,10 @@ void CMakeProject::updateApplicationAndDeploymentTargets()
if (ct.targetType == ExecutableType || ct.targetType == DynamicLibraryType) if (ct.targetType == ExecutableType || ct.targetType == DynamicLibraryType)
deploymentData.addFile(ct.executable.toString(), deploymentPrefix + buildDir.relativeFilePath(ct.executable.toFileInfo().dir().path()), DeployableFile::TypeExecutable); deploymentData.addFile(ct.executable.toString(), deploymentPrefix + buildDir.relativeFilePath(ct.executable.toFileInfo().dir().path()), DeployableFile::TypeExecutable);
if (ct.targetType == ExecutableType) { if (ct.targetType == ExecutableType) {
FileName srcWithTrailingSlash = FileName::fromString(ct.sourceDirectory.toString());
srcWithTrailingSlash.appendString('/');
// TODO: Put a path to corresponding .cbp file into projectFilePath? // TODO: Put a path to corresponding .cbp file into projectFilePath?
appTargetList.list << BuildTargetInfo(ct.title, ct.executable, ct.executable); appTargetList.list << BuildTargetInfo(ct.title, ct.executable, srcWithTrailingSlash);
} }
} }

View File

@@ -310,7 +310,7 @@ void ServerModeReader::updateCodeModel(CppTools::RawProjectParts &rpps)
CppTools::RawProjectPart rpp; CppTools::RawProjectPart rpp;
rpp.setProjectFileLocation(fg->target->sourceDirectory.toString() + "/CMakeLists.txt"); rpp.setProjectFileLocation(fg->target->sourceDirectory.toString() + "/CMakeLists.txt");
rpp.setBuildSystemTarget(fg->target->name); rpp.setBuildSystemTarget(fg->target->name + '|' + rpp.projectFile);
rpp.setDisplayName(fg->target->name + QString::number(counter)); rpp.setDisplayName(fg->target->name + QString::number(counter));
rpp.setDefines(defineArg.toUtf8()); rpp.setDefines(defineArg.toUtf8());
rpp.setIncludePaths(includes); rpp.setIncludePaths(includes);

View File

@@ -367,7 +367,7 @@ void TeaLeafReader::updateCodeModel(CppTools::RawProjectParts &rpps)
includePaths += m_parameters.buildDirectory.toString(); includePaths += m_parameters.buildDirectory.toString();
CppTools::RawProjectPart rpp; CppTools::RawProjectPart rpp;
rpp.setProjectFileLocation(QString()); // No project file information available! rpp.setProjectFileLocation(QString()); // No project file information available!
rpp.setBuildSystemTarget(cbt.title); rpp.setBuildSystemTarget(cbt.title + '|');
rpp.setIncludePaths(includePaths); rpp.setIncludePaths(includePaths);
CppTools::RawProjectPartFlags cProjectFlags; CppTools::RawProjectPartFlags cProjectFlags;

View File

@@ -955,7 +955,7 @@ void QbsProject::updateCppCodeModel()
rpp.setDisplayName(grp.name()); rpp.setDisplayName(grp.name());
rpp.setProjectFileLocation(grp.location().filePath(), rpp.setProjectFileLocation(grp.location().filePath(),
grp.location().line(), grp.location().column()); grp.location().line(), grp.location().column());
rpp.setBuildSystemTarget(prd.name()); rpp.setBuildSystemTarget(prd.name() + '|' + rpp.projectFile);
QHash<QString, qbs::ArtifactData> filePathToSourceArtifact; QHash<QString, qbs::ArtifactData> filePathToSourceArtifact;
bool hasCFiles = false; bool hasCFiles = false;

View File

@@ -287,7 +287,7 @@ void QmakeProject::updateCppCodeModel()
CppTools::RawProjectPart rpp; CppTools::RawProjectPart rpp;
rpp.setDisplayName(pro->displayName()); rpp.setDisplayName(pro->displayName());
rpp.setProjectFileLocation(pro->filePath().toString()); rpp.setProjectFileLocation(pro->filePath().toString());
rpp.setBuildSystemTarget(pro->targetInformation().target); rpp.setBuildSystemTarget(pro->targetInformation().target + '|' + rpp.projectFile);
// TODO: Handle QMAKE_CFLAGS // TODO: Handle QMAKE_CFLAGS
rpp.setFlagsForCxx({cxxToolChain, pro->variableValue(Variable::CppFlags)}); rpp.setFlagsForCxx({cxxToolChain, pro->variableValue(Variable::CppFlags)});
rpp.setDefines(pro->cxxDefines()); rpp.setDefines(pro->cxxDefines());