forked from qt-creator/qt-creator
AutoTest: Add basic boost test support
Provide experimental support for Boost UTF. This patch adds the basic implementation for * parsing the code for Boost tests * executing the found tests * displaying respective results This is just a basic and limited support which needs to be enhanced and improved later on. Task-number: QTCREATORBUG-21169 Change-Id: Ie0da5f51f90fb1fa7217eac461ebfc5214395ef6 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
354
src/plugins/autotest/boost/boosttesttreeitem.cpp
Normal file
354
src/plugins/autotest/boost/boosttesttreeitem.cpp
Normal file
@@ -0,0 +1,354 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "boosttesttreeitem.h"
|
||||
#include "boosttestconstants.h"
|
||||
#include "boosttestconfiguration.h"
|
||||
#include "boosttestparser.h"
|
||||
#include "../testframeworkmanager.h"
|
||||
|
||||
#include <projectexplorer/session.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestTreeItem *BoostTestTreeItem::copyWithoutChildren()
|
||||
{
|
||||
BoostTestTreeItem *copied = new BoostTestTreeItem;
|
||||
copied->copyBasicDataFrom(this);
|
||||
copied->m_state = m_state;
|
||||
copied->m_fullName = m_fullName;
|
||||
return copied;
|
||||
}
|
||||
|
||||
QVariant BoostTestTreeItem::data(int column, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
if (type() == Root)
|
||||
break;
|
||||
return QString(name() + nameSuffix());
|
||||
case Qt::CheckStateRole:
|
||||
return checked();
|
||||
case ItalicRole:
|
||||
return false;
|
||||
case EnabledRole:
|
||||
return enabled();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TestTreeItem::data(column, role);
|
||||
}
|
||||
|
||||
TestTreeItem *BoostTestTreeItem::find(const TestParseResult *result)
|
||||
{
|
||||
QTC_ASSERT(result, return nullptr);
|
||||
|
||||
const BoostTestParseResult *bResult = static_cast<const BoostTestParseResult *>(result);
|
||||
|
||||
switch (type()) {
|
||||
case Root:
|
||||
if (TestFrameworkManager::instance()->groupingEnabled(result->frameworkId)) {
|
||||
const QFileInfo fileInfo(bResult->fileName);
|
||||
const QFileInfo base(fileInfo.absolutePath());
|
||||
for (int row = 0; row < childCount(); ++row) {
|
||||
BoostTestTreeItem *group = static_cast<BoostTestTreeItem *>(childAt(row));
|
||||
if (group->filePath() != base.absoluteFilePath())
|
||||
continue;
|
||||
if (auto groupChild = group->findChildByNameStateAndFile(
|
||||
bResult->name, bResult->state, bResult->proFile)) {
|
||||
return groupChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
return findChildByNameStateAndFile(bResult->name, bResult->state, bResult->proFile);
|
||||
case GroupNode:
|
||||
case TestSuite:
|
||||
return findChildByNameStateAndFile(bResult->name, bResult->state, bResult->proFile);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TestTreeItem *BoostTestTreeItem::findChild(const TestTreeItem *other)
|
||||
{
|
||||
QTC_ASSERT(other, return nullptr);
|
||||
const Type otherType = other->type();
|
||||
|
||||
switch (type()) {
|
||||
case Root: {
|
||||
TestTreeItem *result = nullptr;
|
||||
if (otherType == GroupNode) {
|
||||
result = findChildByNameAndFile(other->name(), other->filePath());
|
||||
} else if (otherType == TestSuite) {
|
||||
auto bOther = static_cast<const BoostTestTreeItem *>(other);
|
||||
result = findChildByNameStateAndFile(bOther->name(), bOther->state(),
|
||||
bOther->proFile());
|
||||
}
|
||||
return (result && result->type() == otherType) ? result : nullptr;
|
||||
}
|
||||
case GroupNode: {
|
||||
auto bOther = static_cast<const BoostTestTreeItem *>(other);
|
||||
return otherType == TestSuite
|
||||
? findChildByNameStateAndFile(bOther->name(), bOther->state(), bOther->proFile())
|
||||
: nullptr;
|
||||
}
|
||||
case TestSuite: {
|
||||
if (otherType == TestCase) {
|
||||
return findChildByNameAndFile(other->name(), other->filePath());
|
||||
} else if (otherType == TestSuite) {
|
||||
auto bOther = static_cast<const BoostTestTreeItem *>(other);
|
||||
return findChildByNameStateAndFile(other->name(), bOther->state(), other->proFile());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool BoostTestTreeItem::modify(const TestParseResult *result)
|
||||
{
|
||||
QTC_ASSERT(result, return false);
|
||||
return (type() == TestCase || type() == TestSuite)
|
||||
? modifyTestContent(static_cast<const BoostTestParseResult *>(result))
|
||||
: false;
|
||||
}
|
||||
|
||||
TestTreeItem *BoostTestTreeItem::createParentGroupNode() const
|
||||
{
|
||||
const QFileInfo fileInfo(filePath());
|
||||
const QFileInfo base(fileInfo.absolutePath());
|
||||
return new BoostTestTreeItem(base.baseName(), fileInfo.absolutePath(), TestTreeItem::GroupNode);
|
||||
}
|
||||
|
||||
QString BoostTestTreeItem::prependWithParentsSuitePaths(const QString &testName) const
|
||||
{
|
||||
QString prepend = type() == TestSuite ? m_fullName.left(m_fullName.lastIndexOf('/'))
|
||||
: m_fullName.left(m_fullName.indexOf("::"));
|
||||
if (prepend.startsWith(BoostTest::Constants::BOOST_MASTER_SUITE))
|
||||
prepend = prepend.mid(QString(BoostTest::Constants::BOOST_MASTER_SUITE).length());
|
||||
|
||||
return prepend + '/' + testName;
|
||||
}
|
||||
|
||||
static QString handleSpecialFunctionNames(const QString &name)
|
||||
{
|
||||
static const QRegularExpression function(".*\\((.*),.*\\)");
|
||||
const QRegularExpressionMatch match = function.match(name);
|
||||
if (!match.hasMatch())
|
||||
return name;
|
||||
QString result = match.captured(1);
|
||||
int index = result.lastIndexOf(':');
|
||||
if (index != -1)
|
||||
result = result.mid(index + 1);
|
||||
result.prepend('*').append('*');
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<TestConfiguration *> BoostTestTreeItem::getAllTestConfigurations() const
|
||||
{
|
||||
QList<TestConfiguration *> result;
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
if (!project || type() != Root)
|
||||
return result;
|
||||
|
||||
struct BoostTestCases {
|
||||
int testCases;
|
||||
QSet<QString> internalTargets;
|
||||
};
|
||||
|
||||
// we only need the unique project files (and number of test cases for the progress indicator)
|
||||
QHash<QString, BoostTestCases> testsPerProjectfile;
|
||||
forAllChildren([&testsPerProjectfile](TreeItem *it){
|
||||
auto item = static_cast<BoostTestTreeItem *>(it);
|
||||
if (item->type() != TestSuite)
|
||||
return;
|
||||
int funcChildren = 0;
|
||||
item->forAllChildren([&funcChildren](TreeItem *child){
|
||||
if (static_cast<BoostTestTreeItem *>(child)->type() == TestCase)
|
||||
++funcChildren;
|
||||
});
|
||||
if (funcChildren) {
|
||||
testsPerProjectfile[item->proFile()].testCases += funcChildren;
|
||||
testsPerProjectfile[item->proFile()].internalTargets.unite(item->internalTargets());
|
||||
}
|
||||
});
|
||||
|
||||
for (auto it = testsPerProjectfile.begin(), end = testsPerProjectfile.end(); it != end; ++it) {
|
||||
BoostTestConfiguration *config = new BoostTestConfiguration;
|
||||
config->setProject(project);
|
||||
config->setProjectFile(it.key());
|
||||
config->setTestCaseCount(it.value().testCases);
|
||||
config->setInternalTargets(it.value().internalTargets);
|
||||
result.append(config);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<TestConfiguration *> BoostTestTreeItem::getSelectedTestConfigurations() const
|
||||
{
|
||||
QList<TestConfiguration *> result;
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
if (!project || type() != Root)
|
||||
return result;
|
||||
|
||||
struct BoostTestCases {
|
||||
QStringList testCases;
|
||||
QSet<QString> internalTargets;
|
||||
};
|
||||
|
||||
QHash<QString, BoostTestCases> testCasesForProjectFile;
|
||||
forAllChildren([&testCasesForProjectFile](TreeItem *it){
|
||||
auto item = static_cast<BoostTestTreeItem *>(it);
|
||||
if (item->type() != TestCase)
|
||||
return;
|
||||
if (!item->enabled()) // ignore child tests known to be disabled when using run selected
|
||||
return;
|
||||
if (item->checked() == Qt::Checked) {
|
||||
QString tcName = handleSpecialFunctionNames(item->name());
|
||||
testCasesForProjectFile[item->proFile()].testCases.append(
|
||||
item->prependWithParentsSuitePaths(tcName));
|
||||
testCasesForProjectFile[item->proFile()].internalTargets.unite(item->internalTargets());
|
||||
}
|
||||
});
|
||||
|
||||
auto end = testCasesForProjectFile.cend();
|
||||
for (auto it = testCasesForProjectFile.cbegin(); it != end; ++it) {
|
||||
BoostTestConfiguration *config = new BoostTestConfiguration;
|
||||
config->setProject(project);
|
||||
config->setProjectFile(it.key());
|
||||
config->setTestCases(it.value().testCases);
|
||||
config->setInternalTargets(it.value().internalTargets);
|
||||
result.append(config);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TestConfiguration *BoostTestTreeItem::testConfiguration() const
|
||||
{
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
QTC_ASSERT(project, return nullptr);
|
||||
|
||||
const Type itemType = type();
|
||||
if (itemType == TestSuite || itemType == TestCase) {
|
||||
QStringList testCases;
|
||||
if (itemType == TestSuite) {
|
||||
forFirstLevelChildren([&testCases](TestTreeItem *child) {
|
||||
QTC_ASSERT(child, return);
|
||||
if (auto boostItem = static_cast<BoostTestTreeItem *>(child)) {
|
||||
if (boostItem->enabled()) {
|
||||
QString tcName = handleSpecialFunctionNames(boostItem->name());
|
||||
if (boostItem->type() == TestSuite) // execute everything below a suite
|
||||
tcName.append("/*");
|
||||
testCases.append(boostItem->prependWithParentsSuitePaths(tcName));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
testCases.append(prependWithParentsSuitePaths(handleSpecialFunctionNames(name())));
|
||||
}
|
||||
|
||||
BoostTestConfiguration *config = new BoostTestConfiguration;
|
||||
config->setProjectFile(proFile());
|
||||
config->setProject(project);
|
||||
config->setTestCases(testCases);
|
||||
config->setInternalTargets(internalTargets());
|
||||
return config;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TestConfiguration *BoostTestTreeItem::debugConfiguration() const
|
||||
{
|
||||
BoostTestConfiguration *config = static_cast<BoostTestConfiguration *>(testConfiguration());
|
||||
if (config)
|
||||
config->setRunMode(TestRunMode::Debug);
|
||||
return config;
|
||||
}
|
||||
|
||||
QString BoostTestTreeItem::nameSuffix() const
|
||||
{
|
||||
static QString markups[] = {QCoreApplication::translate("BoostTestTreeItem", "parameterized"),
|
||||
QCoreApplication::translate("BoostTestTreeItem", "fixture")};
|
||||
QString suffix;
|
||||
if (m_state & Parameterized)
|
||||
suffix = QString(" [") + markups[0];
|
||||
if (m_state & Fixture)
|
||||
suffix += (suffix.isEmpty() ? QString(" [") : QString(", ")) + markups[1];
|
||||
if (!suffix.isEmpty())
|
||||
suffix += ']';
|
||||
return suffix;
|
||||
}
|
||||
|
||||
bool BoostTestTreeItem::enabled() const
|
||||
{
|
||||
if (m_state & ExplicitlyEnabled)
|
||||
return true;
|
||||
|
||||
if (m_state & Disabled)
|
||||
return false;
|
||||
|
||||
const TestTreeItem *parent = parentItem();
|
||||
if (parent && parent->type() == TestSuite) // take test suites into account
|
||||
return static_cast<const BoostTestTreeItem *>(parent)->enabled();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TestTreeItem *BoostTestTreeItem::findChildByNameStateAndFile(const QString &name,
|
||||
BoostTestTreeItem::TestStates state,
|
||||
const QString &proFile) const
|
||||
{
|
||||
return static_cast<TestTreeItem *>(
|
||||
findAnyChild([name, state, proFile](const Utils::TreeItem *other){
|
||||
const BoostTestTreeItem *boostItem = static_cast<const BoostTestTreeItem *>(other);
|
||||
return boostItem->proFile() == proFile && boostItem->fullName() == name
|
||||
&& boostItem->state() == state;
|
||||
}));
|
||||
}
|
||||
|
||||
bool BoostTestTreeItem::modifyTestContent(const BoostTestParseResult *result)
|
||||
{
|
||||
bool hasBeenModified = modifyLineAndColumn(result);
|
||||
|
||||
if (m_state != result->state) {
|
||||
m_state = result->state;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
if (m_fullName != result->name) {
|
||||
m_fullName = result->name;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
return hasBeenModified;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
Reference in New Issue
Block a user