Improve support for Qt Quick Tests

This commit is contained in:
Christian Stenger
2014-11-13 12:31:58 +01:00
committed by Christian Stenger
parent 9a644d1257
commit b0c4a9cc3b
15 changed files with 353 additions and 91 deletions

View File

@@ -21,7 +21,8 @@ SOURCES += \
testresult.cpp \ testresult.cpp \
testresultspane.cpp \ testresultspane.cpp \
testresultmodel.cpp \ testresultmodel.cpp \
testresultdelegate.cpp testresultdelegate.cpp \
testtreeitemdelegate.cpp
HEADERS += \ HEADERS += \
testtreeview.h \ testtreeview.h \
@@ -38,7 +39,8 @@ HEADERS += \
testresult.h \ testresult.h \
testresultspane.h \ testresultspane.h \
testresultmodel.h \ testresultmodel.h \
testresultdelegate.h testresultdelegate.h \
testtreeitemdelegate.h
RESOURCES += \ RESOURCES += \
autotest.qrc autotest.qrc

View File

@@ -60,8 +60,10 @@ void TestCodeParser::updateTestTree()
m_model->removeAllAutoTests(); m_model->removeAllAutoTests();
m_model->removeAllQuickTests(); m_model->removeAllQuickTests();
const ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance(); const ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance();
if (!session || !session->hasProjects()) if (!session || !session->hasProjects()) {
m_currentProject = 0;
return; return;
}
m_currentProject = session->startupProject(); m_currentProject = session->startupProject();
if (!m_currentProject) if (!m_currentProject)
@@ -285,13 +287,13 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr doc)
} }
TestVisitor myVisitor(tc); TestVisitor myVisitor(tc);
myVisitor.accept(declaringDoc->globalNamespace()); myVisitor.accept(declaringDoc->globalNamespace());
const QMap<QString, TestCodeLocation> privSlots = myVisitor.privateSlots(); const QMap<QString, TestCodeLocationAndType> privSlots = myVisitor.privateSlots();
foreach (const QString &privS, privSlots.keys()) { foreach (const QString &privS, privSlots.keys()) {
const TestCodeLocation location = privSlots.value(privS); const TestCodeLocationAndType locationAndType = privSlots.value(privS);
TestTreeItem *ttSub = new TestTreeItem(privS, location.m_fileName, TestTreeItem *ttSub = new TestTreeItem(privS, locationAndType.m_fileName,
TestTreeItem::TEST_FUNCTION, ttItem); locationAndType.m_type, ttItem);
ttSub->setLine(location.m_line); ttSub->setLine(locationAndType.m_line);
ttSub->setColumn(location.m_column); ttSub->setColumn(locationAndType.m_column);
ttItem->appendChild(ttSub); ttItem->appendChild(ttSub);
} }
@@ -374,8 +376,8 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr doc)
QmlJS::AST::Node::accept(ast, &qmlVisitor); QmlJS::AST::Node::accept(ast, &qmlVisitor);
const QString tcName = qmlVisitor.testCaseName(); const QString tcName = qmlVisitor.testCaseName();
const TestCodeLocation tcLocation = qmlVisitor.testCaseLocation(); const TestCodeLocationAndType tcLocationAndType = qmlVisitor.testCaseLocation();
const QMap<QString, TestCodeLocation> testFunctions = qmlVisitor.testFunctions(); const QMap<QString, TestCodeLocationAndType> testFunctions = qmlVisitor.testFunctions();
const QModelIndex quickTestRootIndex = m_model->index(1, 0); const QModelIndex quickTestRootIndex = m_model->index(1, 0);
TestTreeItem *quickTestRootItem = static_cast<TestTreeItem *>(quickTestRootIndex.internalPointer()); TestTreeItem *quickTestRootItem = static_cast<TestTreeItem *>(quickTestRootIndex.internalPointer());
@@ -417,11 +419,11 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr doc)
} }
foreach (const QString &func, testFunctions.keys()) { foreach (const QString &func, testFunctions.keys()) {
const TestCodeLocation location = testFunctions.value(func); const TestCodeLocationAndType locationAndType = testFunctions.value(func);
TestTreeItem *ttSub = new TestTreeItem(func, location.m_fileName, TestTreeItem *ttSub = new TestTreeItem(func, locationAndType.m_fileName,
TestTreeItem::TEST_FUNCTION, ttItem); locationAndType.m_type, ttItem);
ttSub->setLine(location.m_line); ttSub->setLine(locationAndType.m_line);
ttSub->setColumn(location.m_column); ttSub->setColumn(locationAndType.m_column);
ttSub->setMainFile(doc->fileName()); ttSub->setMainFile(doc->fileName());
ttItem->appendChild(ttSub); ttItem->appendChild(ttSub);
} }
@@ -445,23 +447,23 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr doc)
} // end of handling test cases without name property } // end of handling test cases without name property
// construct new/modified TestTreeItem // construct new/modified TestTreeItem
TestTreeItem *ttItem = new TestTreeItem(tcName, tcLocation.m_fileName, TestTreeItem *ttItem = new TestTreeItem(tcName, tcLocationAndType.m_fileName,
TestTreeItem::TEST_CLASS, quickTestRootItem); tcLocationAndType.m_type, quickTestRootItem);
ttItem->setLine(tcLocation.m_line); ttItem->setLine(tcLocationAndType.m_line);
ttItem->setColumn(tcLocation.m_column); ttItem->setColumn(tcLocationAndType.m_column);
ttItem->setMainFile(doc->fileName()); ttItem->setMainFile(doc->fileName());
foreach (const QString &func, testFunctions.keys()) { foreach (const QString &func, testFunctions.keys()) {
const TestCodeLocation location = testFunctions.value(func); const TestCodeLocationAndType locationAndType = testFunctions.value(func);
TestTreeItem *ttSub = new TestTreeItem(func, location.m_fileName, TestTreeItem *ttSub = new TestTreeItem(func, locationAndType.m_fileName,
TestTreeItem::TEST_FUNCTION, ttItem); locationAndType.m_type, ttItem);
ttSub->setLine(location.m_line); ttSub->setLine(locationAndType.m_line);
ttSub->setColumn(location.m_column); ttSub->setColumn(locationAndType.m_column);
ttItem->appendChild(ttSub); ttItem->appendChild(ttSub);
} }
// update model and internal map // update model and internal map
const QString fileName(tcLocation.m_fileName); const QString fileName(tcLocationAndType.m_fileName);
const QmlJS::Document::Ptr qmlDoc = const QmlJS::Document::Ptr qmlDoc =
QmlJSTools::Internal::ModelManager::instance()->snapshot().document(fileName); QmlJSTools::Internal::ModelManager::instance()->snapshot().document(fileName);
@@ -497,7 +499,7 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr doc)
m_model->addQuickTest(ttItem); m_model->addQuickTest(ttItem);
TestInfo ti(tcName, testFunctions.keys(), 0, qmlDoc->editorRevision()); TestInfo ti(tcName, testFunctions.keys(), 0, qmlDoc->editorRevision());
ti.setReferencingFile(doc->fileName()); ti.setReferencingFile(doc->fileName());
m_quickDocMap.insert(tcLocation.m_fileName, ti); m_quickDocMap.insert(tcLocationAndType.m_fileName, ti);
} }
} }
} }

View File

@@ -81,6 +81,11 @@ void TestConfiguration::setWorkingDirectory(const QString &workingDirectory)
m_workingDir = workingDirectory; m_workingDir = workingDirectory;
} }
void TestConfiguration::setDisplayName(const QString &displayName)
{
m_displayName = displayName;
}
void TestConfiguration::setEnvironment(const Utils::Environment &env) void TestConfiguration::setEnvironment(const Utils::Environment &env)
{ {
m_environment = env; m_environment = env;

View File

@@ -46,6 +46,7 @@ public:
void setTargetName(const QString &targetName); void setTargetName(const QString &targetName);
void setProFile(const QString &proFile); void setProFile(const QString &proFile);
void setWorkingDirectory(const QString &workingDirectory); void setWorkingDirectory(const QString &workingDirectory);
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);
@@ -56,6 +57,7 @@ public:
QString targetFile() const { return m_targetFile; } QString targetFile() const { return m_targetFile; }
QString targetName() const { return m_targetName; } QString targetName() const { return m_targetName; }
QString workingDirectory() const { return m_workingDir; } QString workingDirectory() const { return m_workingDir; }
QString displayName() const { return m_displayName; }
Utils::Environment environment() const { return m_environment; } Utils::Environment environment() const { return m_environment; }
ProjectExplorer::Project *project() const { return m_project; } ProjectExplorer::Project *project() const { return m_project; }
@@ -72,6 +74,7 @@ private:
QString m_targetFile; QString m_targetFile;
QString m_targetName; QString m_targetName;
QString m_workingDir; QString m_workingDir;
QString m_displayName;
Utils::Environment m_environment; Utils::Environment m_environment;
ProjectExplorer::Project *m_project; ProjectExplorer::Project *m_project;
}; };

View File

@@ -50,9 +50,9 @@ TestResultsPane::TestResultsPane(QObject *parent) :
QPalette pal; QPalette pal;
pal.setColor(QPalette::Window, pal.setColor(QPalette::Window,
Utils::creatorTheme()->color(Utils::Theme::SearchResultWidgetBackgroundColor)); Utils::creatorTheme()->color(Utils::Theme::InfoBarBackground));
pal.setColor(QPalette::WindowText, pal.setColor(QPalette::WindowText,
Utils::creatorTheme()->color(Utils::Theme::SearchResultWidgetTextColor)); Utils::creatorTheme()->color(Utils::Theme::InfoBarText));
m_summaryWidget = new QFrame; m_summaryWidget = new QFrame;
m_summaryWidget->setPalette(pal); m_summaryWidget->setPalette(pal);
m_summaryWidget->setAutoFillBackground(true); m_summaryWidget->setAutoFillBackground(true);

View File

@@ -351,8 +351,14 @@ void TestRunner::runTests()
} }
ProjectExplorer::Project *project = m_selectedTests.at(0)->project(); ProjectExplorer::Project *project = m_selectedTests.at(0)->project();
if (!project) // add a warning or info to output? possible at all? if (!project) {
TestResultsPane::instance()->addTestResult(
TestResult(QString(), QString(), QString(), ResultType::MESSAGE_WARN,
tr("*** Project is null - canceling Test Run ***\n"
"Actually only Desktop kits are supported - make sure the "
"current active kit is a Desktop kit.")));
return; return;
}
ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance(); ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance();
ProjectExplorer::Internal::ProjectExplorerSettings pes = pep->projectExplorerSettings(); ProjectExplorer::Internal::ProjectExplorerSettings pes = pep->projectExplorerSettings();

View File

@@ -24,11 +24,29 @@ namespace Internal {
TestTreeItem::TestTreeItem(const QString &name, const QString &filePath, Type type, TestTreeItem *parent) TestTreeItem::TestTreeItem(const QString &name, const QString &filePath, Type type, TestTreeItem *parent)
: m_name(name), : m_name(name),
m_filePath(filePath), m_filePath(filePath),
m_checked(type == ROOT ? Qt::Unchecked : Qt::Checked),
m_type(type), m_type(type),
m_line(0), m_line(0),
m_parent(parent) m_parent(parent)
{ {
switch (m_type) {
case ROOT:
m_checked = Qt::Unchecked;
break;
case TEST_CLASS:
case TEST_FUNCTION:
m_checked = Qt::Checked;
break;
case TEST_DATAFUNCTION:
case TEST_SPECIALFUNCTION:
if (m_parent)
m_checked = m_parent->checked() == Qt::PartiallyChecked ? Qt::Unchecked
: m_parent->checked();
else
m_checked = Qt::Unchecked;
break;
default:
m_checked = Qt::Unchecked;
}
} }
TestTreeItem::~TestTreeItem() TestTreeItem::~TestTreeItem()
@@ -121,19 +139,37 @@ bool TestTreeItem::modifyContent(const TestTreeItem *modified)
void TestTreeItem::setChecked(const Qt::CheckState checkState) void TestTreeItem::setChecked(const Qt::CheckState checkState)
{ {
switch (m_type) { switch (m_type) {
case ROOT: case TEST_FUNCTION: {
return;
case TEST_FUNCTION:
m_checked = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked); m_checked = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked);
m_parent->revalidateCheckState(); m_parent->revalidateCheckState();
break; break;
case TEST_CLASS: }
case TEST_CLASS: {
Qt::CheckState usedState = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked); Qt::CheckState usedState = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked);
foreach (TestTreeItem *child, m_children) { foreach (TestTreeItem *child, m_children) {
child->setChecked(usedState); child->setChecked(usedState);
} }
m_checked = usedState; m_checked = usedState;
} }
default:
return;
}
}
Qt::CheckState TestTreeItem::checked() const
{
switch (m_type) {
case TEST_CLASS:
case TEST_FUNCTION:
return m_checked;
case TEST_DATAFUNCTION:
case TEST_SPECIALFUNCTION:
return m_parent->m_checked == Qt::PartiallyChecked ? Qt::Unchecked : m_parent->m_checked;
default:
if (m_parent)
return m_parent->m_checked;
}
return Qt::Unchecked;
} }
void TestTreeItem::revalidateCheckState() void TestTreeItem::revalidateCheckState()
@@ -143,8 +179,16 @@ void TestTreeItem::revalidateCheckState()
bool foundChecked = false; bool foundChecked = false;
bool foundUnchecked = false; bool foundUnchecked = false;
foreach (const TestTreeItem *child, m_children) { foreach (const TestTreeItem *child, m_children) {
foundChecked |= (child->m_checked != Qt::Unchecked); switch (child->type()) {
foundUnchecked |= (child->m_checked == Qt::Unchecked); case TEST_DATAFUNCTION:
case TEST_SPECIALFUNCTION:
continue;
default:
break;
}
foundChecked |= (child->checked() != Qt::Unchecked);
foundUnchecked |= (child->checked() == Qt::Unchecked);
if (foundChecked && foundUnchecked) { if (foundChecked && foundUnchecked) {
m_checked = Qt::PartiallyChecked; m_checked = Qt::PartiallyChecked;
return; return;

View File

@@ -30,7 +30,11 @@ class TestTreeItem
public: public:
enum Type { enum Type {
ROOT, TEST_CLASS, TEST_FUNCTION ROOT,
TEST_CLASS,
TEST_FUNCTION,
TEST_DATAFUNCTION,
TEST_SPECIALFUNCTION
}; };
TestTreeItem(const QString &name, const QString &filePath, Type type, TestTreeItem *parent = 0); TestTreeItem(const QString &name, const QString &filePath, Type type, TestTreeItem *parent = 0);
@@ -57,7 +61,7 @@ public:
QString mainFile() const { return m_mainFile; } QString mainFile() const { return m_mainFile; }
void setMainFile(const QString &mainFile) { m_mainFile = mainFile; } void setMainFile(const QString &mainFile) { m_mainFile = mainFile; }
void setChecked(const Qt::CheckState checked); void setChecked(const Qt::CheckState checked);
Qt::CheckState checked() const { return m_checked; } Qt::CheckState checked() const;
Type type() const { return m_type; } Type type() const { return m_type; }
void setParent(TestTreeItem *parent) { m_parent = parent; } void setParent(TestTreeItem *parent) { m_parent = parent; }

View File

@@ -0,0 +1,69 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc
** All rights reserved.
** For any questions to Digia, please use contact form at http://qt.digia.com
**
** This file is part of the Qt Creator Enterprise Auto Test Add-on.
**
** Licensees holding valid Qt Enterprise licenses may use this file in
** accordance with the Qt Enterprise License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.digia.com
**
****************************************************************************/
#include "testtreeitemdelegate.h"
#include "testtreemodel.h"
#include <QPainter>
namespace Autotest {
namespace Internal {
TestTreeItemDelegate::TestTreeItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
TestTreeItemDelegate::~TestTreeItemDelegate()
{
}
void TestTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
bool italic = index.data(ItalicRole).toBool();
if (italic) {
QFont font(option.font);
font.setItalic(true);
opt.font = font;
// correct margin of items without a checkbox (except for root items)
QStyleOptionButton styleOpt;
styleOpt.initFrom(opt.widget);
const QSize sz; // no text, no icon - we just need the size of the check box
QSize cbSize = opt.widget->style()->sizeFromContents(QStyle::CT_CheckBox, &styleOpt, sz);
// the 6 results from hard coded margins of the checkbox itself (2x2) and the item (1x2)
opt.rect.setLeft(opt.rect.left() + cbSize.width() + 6);
// HACK make sure the pixels that have been moved right are painted for selections
if (opt.state & QStyle::State_Selected) {
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled
? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active))
cg = QPalette::Inactive;
painter->fillRect(option.rect, opt.palette.brush(cg, QPalette::Highlight));
}
}
QStyledItemDelegate::paint(painter, opt, index);
}
} // namespace Internal
} // namespace Autotest

View File

@@ -0,0 +1,41 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc
** All rights reserved.
** For any questions to Digia, please use contact form at http://qt.digia.com
**
** This file is part of the Qt Creator Enterprise Auto Test Add-on.
**
** Licensees holding valid Qt Enterprise licenses may use this file in
** accordance with the Qt Enterprise License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.digia.com
**
****************************************************************************/
#ifndef TESTTREEITEMDELEGATE_H
#define TESTTREEITEMDELEGATE_H
#include <QStyledItemDelegate>
namespace Autotest {
namespace Internal {
class TestTreeItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
TestTreeItemDelegate(QObject *parent = 0);
~TestTreeItemDelegate();
public:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
} // namespace Internal
} // namespace Autotest
#endif // TESTTREEITEMDELEGATE_H

View File

@@ -135,6 +135,8 @@ static QIcon testTreeIcon(TestTreeItem::Type type)
QIcon(QLatin1String(":/images/class.png")), QIcon(QLatin1String(":/images/class.png")),
QIcon(QLatin1String(":/images/func.png")) QIcon(QLatin1String(":/images/func.png"))
}; };
if (type >= 3)
return icons[2];
return icons[type]; return icons[type];
} }
@@ -169,15 +171,46 @@ QVariant TestTreeModel::data(const QModelIndex &index, int role) const
case Qt::DecorationRole: case Qt::DecorationRole:
return testTreeIcon(item->type()); return testTreeIcon(item->type());
case Qt::CheckStateRole: case Qt::CheckStateRole:
if (item->type() == TestTreeItem::ROOT) switch (item->type()) {
case TestTreeItem::ROOT:
case TestTreeItem::TEST_DATAFUNCTION:
case TestTreeItem::TEST_SPECIALFUNCTION:
return QVariant(); return QVariant();
return item->checked(); case TestTreeItem::TEST_CLASS:
case LinkRole: if (item->name().isEmpty())
return QVariant();
else
return item->checked();
case TestTreeItem::TEST_FUNCTION:
if (TestTreeItem *parent = item->parent())
return parent->name().isEmpty() ? QVariant() : item->checked();
else
return item->checked();
default:
return item->checked();
}
case LinkRole: {
QVariant itemLink; QVariant itemLink;
TextEditor::TextEditorWidget::Link link(item->filePath(), item->line(), item->column()); TextEditor::TextEditorWidget::Link link(item->filePath(), item->line(), item->column());
itemLink.setValue(link); itemLink.setValue(link);
return itemLink; return itemLink;
} }
case ItalicRole:
switch (item->type()) {
case TestTreeItem::TEST_DATAFUNCTION:
case TestTreeItem::TEST_SPECIALFUNCTION:
return true;
case TestTreeItem::TEST_CLASS:
return item->name().isEmpty();
case TestTreeItem::TEST_FUNCTION:
if (TestTreeItem *parent = item->parent())
return parent->name().isEmpty();
else
return false;
default:
return false;
}
}
// TODO ? // TODO ?
return QVariant(); return QVariant();
@@ -222,15 +255,18 @@ Qt::ItemFlags TestTreeModel::flags(const QModelIndex &index) const
switch(item->type()) { switch(item->type()) {
case TestTreeItem::TEST_CLASS: case TestTreeItem::TEST_CLASS:
if (item->name().isEmpty()) if (item->name().isEmpty())
return Qt::ItemIsSelectable | Qt::ItemIsTristate; return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsTristate | Qt::ItemIsUserCheckable; return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsTristate | Qt::ItemIsUserCheckable;
case TestTreeItem::TEST_FUNCTION: case TestTreeItem::TEST_FUNCTION:
if (item->parent()->name().isEmpty()) if (item->parent()->name().isEmpty())
return Qt::ItemIsSelectable; return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
case TestTreeItem::ROOT: case TestTreeItem::ROOT:
default:
return Qt::ItemIsEnabled; return Qt::ItemIsEnabled;
case TestTreeItem::TEST_DATAFUNCTION:
case TestTreeItem::TEST_SPECIALFUNCTION:
default:
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
} }
} }
@@ -261,18 +297,38 @@ bool TestTreeModel::hasTests() const
static void addProjectInformation(TestConfiguration *config, const QString &filePath) static void addProjectInformation(TestConfiguration *config, const QString &filePath)
{ {
const ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance();
if (!session || !session->hasProjects())
return;
ProjectExplorer::Project *project = session->startupProject();
if (!project)
return;
QString targetFile; QString targetFile;
QString targetName; QString targetName;
QString workDir; QString workDir;
QString proFile; QString proFile;
QString displayName;
Utils::Environment env; Utils::Environment env;
ProjectExplorer::Project *project = 0; bool hasDesktopTarget = false;
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
QList<CppTools::ProjectPart::Ptr> projParts = cppMM->projectPart(filePath); QList<CppTools::ProjectPart::Ptr> projParts = cppMM->projectInfo(project).projectParts();
if (!projParts.empty()) { if (!projParts.empty()) {
proFile = projParts.at(0)->projectFile; foreach (const CppTools::ProjectPart::Ptr &part, projParts) {
project = projParts.at(0)->project; // necessary to grab this here? or should this be the current active startup project anyway? foreach (const CppTools::ProjectFile currentFile, part->files) {
if (currentFile.path == filePath) {
proFile = part->projectFile;
displayName = part->displayName;
break;
}
}
if (!proFile.isEmpty()) // maybe better use a goto instead of the break above??
break;
}
} }
if (project) { if (project) {
if (auto target = project->activeTarget()) { if (auto target = project->activeTarget()) {
ProjectExplorer::BuildTargetInfoList appTargets = target->applicationTargets(); ProjectExplorer::BuildTargetInfoList appTargets = target->applicationTargets();
@@ -289,6 +345,7 @@ static void addProjectInformation(TestConfiguration *config, const QString &file
if (ProjectExplorer::LocalApplicationRunConfiguration *localRunConfiguration if (ProjectExplorer::LocalApplicationRunConfiguration *localRunConfiguration
= qobject_cast<ProjectExplorer::LocalApplicationRunConfiguration *>(rc)) { = qobject_cast<ProjectExplorer::LocalApplicationRunConfiguration *>(rc)) {
if (localRunConfiguration->executable() == targetFile) { if (localRunConfiguration->executable() == targetFile) {
hasDesktopTarget = true;
workDir = Utils::FileUtils::normalizePathName( workDir = Utils::FileUtils::normalizePathName(
localRunConfiguration->workingDirectory()); localRunConfiguration->workingDirectory());
ProjectExplorer::EnvironmentAspect *envAsp ProjectExplorer::EnvironmentAspect *envAsp
@@ -300,12 +357,19 @@ static void addProjectInformation(TestConfiguration *config, const QString &file
} }
} }
} }
config->setTargetFile(targetFile);
config->setTargetName(targetName); if (hasDesktopTarget) {
config->setWorkingDirectory(workDir); config->setTargetFile(targetFile);
config->setProFile(proFile); config->setTargetName(targetName);
config->setEnvironment(env); config->setWorkingDirectory(workDir);
config->setProject(project); config->setProFile(proFile);
config->setEnvironment(env);
config->setProject(project);
config->setDisplayName(displayName);
} else {
config->setProFile(proFile);
config->setDisplayName(displayName);
}
} }
QList<TestConfiguration *> TestTreeModel::getAllTestCases() const QList<TestConfiguration *> TestTreeModel::getAllTestCases() const
@@ -392,17 +456,18 @@ QList<TestConfiguration *> TestTreeModel::getSelectedTests() const
QMap<QString, TestConfiguration *> foundMains; QMap<QString, TestConfiguration *> foundMains;
TestTreeItem *unnamed = unnamedQuickTests(); if (TestTreeItem *unnamed = unnamedQuickTests()) {
for (int childRow = 0, ccount = unnamed->childCount(); childRow < ccount; ++ childRow) { for (int childRow = 0, ccount = unnamed->childCount(); childRow < ccount; ++ childRow) {
const TestTreeItem *grandChild = unnamed->child(childRow); const TestTreeItem *grandChild = unnamed->child(childRow);
const QString mainFile = grandChild->mainFile(); const QString mainFile = grandChild->mainFile();
if (foundMains.contains(mainFile)) { if (foundMains.contains(mainFile)) {
foundMains[mainFile]->setTestCaseCount(tc->testCaseCount() + 1); foundMains[mainFile]->setTestCaseCount(tc->testCaseCount() + 1);
} else { } else {
TestConfiguration *tc = new TestConfiguration(QString(), QStringList()); TestConfiguration *tc = new TestConfiguration(QString(), QStringList());
tc->setTestCaseCount(1); tc->setTestCaseCount(1);
addProjectInformation(tc, mainFile); addProjectInformation(tc, mainFile);
foundMains.insert(mainFile, tc); foundMains.insert(mainFile, tc);
}
} }
} }
@@ -695,6 +760,8 @@ bool TestTreeSortFilterModel::lessThan(const QModelIndex &left, const QModelInde
switch (m_sortMode) { switch (m_sortMode) {
case Alphabetically: case Alphabetically:
if (leftVal == rightVal)
return left.row() > right.row();
return leftVal > rightVal; return leftVal > rightVal;
case Naturally: { case Naturally: {
const TextEditor::TextEditorWidget::Link leftLink = const TextEditor::TextEditorWidget::Link leftLink =
@@ -717,13 +784,20 @@ bool TestTreeSortFilterModel::lessThan(const QModelIndex &left, const QModelInde
bool TestTreeSortFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const bool TestTreeSortFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{ {
// TODO add filtering capabilities
QModelIndex index = m_sourceModel->index(sourceRow, 0,sourceParent); QModelIndex index = m_sourceModel->index(sourceRow, 0,sourceParent);
if (!index.isValid()) if (!index.isValid())
return false; return false;
const TestTreeItem *item = static_cast<TestTreeItem *>(index.internalPointer());
return true; switch (item->type()) {
case TestTreeItem::TEST_DATAFUNCTION:
return m_filterMode & ShowTestData;
case TestTreeItem::TEST_SPECIALFUNCTION:
return m_filterMode & ShowInitAndCleanup;
default:
return true;
}
} }
} // namespace Internal } // namespace Internal

View File

@@ -29,7 +29,8 @@
namespace { namespace {
enum ItemRole { enum ItemRole {
// AnnotationRole = Qt::UserRole + 1, // AnnotationRole = Qt::UserRole + 1,
LinkRole = Qt::UserRole + 2 // can be removed if AnnotationRole comes back LinkRole = Qt::UserRole + 2, // can be removed if AnnotationRole comes back
ItalicRole // used only inside the delegate
}; };
} }

View File

@@ -20,6 +20,7 @@
#include "testcodeparser.h" #include "testcodeparser.h"
#include "testrunner.h" #include "testrunner.h"
#include "testtreeitem.h" #include "testtreeitem.h"
#include "testtreeitemdelegate.h"
#include "testtreemodel.h" #include "testtreemodel.h"
#include "testtreeview.h" #include "testtreeview.h"
@@ -53,6 +54,7 @@ TestTreeViewWidget::TestTreeViewWidget(QWidget *parent) :
m_view = new TestTreeView(this); m_view = new TestTreeView(this);
m_view->setModel(m_sortFilterModel); m_view->setModel(m_sortFilterModel);
m_view->setSortingEnabled(true); m_view->setSortingEnabled(true);
m_view->setItemDelegate(new TestTreeItemDelegate(this));
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(0); layout->setMargin(0);

View File

@@ -44,7 +44,7 @@ TestVisitor::~TestVisitor()
{ {
} }
static QList<QString> ignoredFunctions = QList<QString>() << QLatin1String("initTestCase") static QList<QString> specialFunctions = QList<QString>() << QLatin1String("initTestCase")
<< QLatin1String("cleanupTestCase") << QLatin1String("cleanupTestCase")
<< QLatin1String("init") << QLatin1String("init")
<< QLatin1String("cleanup"); << QLatin1String("cleanup");
@@ -66,14 +66,18 @@ bool TestVisitor::visit(CPlusPlus::Class *symbol)
if (const auto func = type->asFunctionType()) { if (const auto func = type->asFunctionType()) {
if (func->isSlot() && member->isPrivate()) { if (func->isSlot() && member->isPrivate()) {
const QString name = o.prettyName(func->name()); const QString name = o.prettyName(func->name());
if (!ignoredFunctions.contains(name) && !name.endsWith(QLatin1String("_data"))) { // TODO use definition of function instead of declaration!
// TODO use definition of function instead of declaration! TestCodeLocationAndType locationAndType;
TestCodeLocation location; locationAndType.m_fileName = QLatin1String(member->fileName());
location.m_fileName = QLatin1String(member->fileName()); locationAndType.m_line = member->line();
location.m_line = member->line(); locationAndType.m_column = member->column() - 1;
location.m_column = member->column() - 1; if (specialFunctions.contains(name))
m_privSlots.insert(name, location); locationAndType.m_type = TestTreeItem::TEST_SPECIALFUNCTION;
} else if (name.endsWith(QLatin1String("_data")))
locationAndType.m_type = TestTreeItem::TEST_DATAFUNCTION;
else
locationAndType.m_type = TestTreeItem::TEST_FUNCTION;
m_privSlots.insert(name, locationAndType);
} }
} }
} }
@@ -146,13 +150,14 @@ bool TestQmlVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
{ {
const QStringRef name = ast->qualifiedTypeNameId->name; const QStringRef name = ast->qualifiedTypeNameId->name;
if (name != QLatin1String("TestCase")) if (name != QLatin1String("TestCase"))
return false; return true; // find nested TestCase items as well
m_currentTestCaseName.clear(); m_currentTestCaseName.clear();
const auto sourceLocation = ast->firstSourceLocation(); const auto sourceLocation = ast->firstSourceLocation();
m_testCaseLocation.m_fileName = m_currentDoc->fileName(); m_testCaseLocation.m_fileName = m_currentDoc->fileName();
m_testCaseLocation.m_line = sourceLocation.startLine; m_testCaseLocation.m_line = sourceLocation.startLine;
m_testCaseLocation.m_column = sourceLocation.startColumn - 1; m_testCaseLocation.m_column = sourceLocation.startColumn - 1;
m_testCaseLocation.m_type = TestTreeItem::TEST_CLASS;
return true; return true;
} }
@@ -173,12 +178,13 @@ bool TestQmlVisitor::visit(QmlJS::AST::FunctionDeclaration *ast)
const QStringRef name = ast->name; const QStringRef name = ast->name;
if (name.startsWith(QLatin1String("test_"))) { if (name.startsWith(QLatin1String("test_"))) {
const auto sourceLocation = ast->firstSourceLocation(); const auto sourceLocation = ast->firstSourceLocation();
TestCodeLocation location; TestCodeLocationAndType locationAndType;
location.m_fileName = m_currentDoc->fileName(); locationAndType.m_fileName = m_currentDoc->fileName();
location.m_line = sourceLocation.startLine; locationAndType.m_line = sourceLocation.startLine;
location.m_column = sourceLocation.startColumn - 1; locationAndType.m_column = sourceLocation.startColumn - 1;
locationAndType.m_type = TestTreeItem::TEST_FUNCTION;
m_testFunctions.insert(name.toString(), location); m_testFunctions.insert(name.toString(), locationAndType);
} }
return false; return false;
} }

View File

@@ -19,6 +19,8 @@
#ifndef TESTVISITOR_H #ifndef TESTVISITOR_H
#define TESTVISITOR_H #define TESTVISITOR_H
#include "testtreeitem.h"
#include <cplusplus/ASTVisitor.h> #include <cplusplus/ASTVisitor.h>
#include <cplusplus/CppDocument.h> #include <cplusplus/CppDocument.h>
#include <cplusplus/Scope.h> #include <cplusplus/Scope.h>
@@ -33,10 +35,11 @@
namespace Autotest { namespace Autotest {
namespace Internal { namespace Internal {
struct TestCodeLocation { struct TestCodeLocationAndType {
QString m_fileName; QString m_fileName;
unsigned m_line; unsigned m_line;
unsigned m_column; unsigned m_column;
TestTreeItem::Type m_type;
}; };
class TestVisitor : public CPlusPlus::SymbolVisitor class TestVisitor : public CPlusPlus::SymbolVisitor
@@ -45,13 +48,13 @@ public:
TestVisitor(const QString &fullQualifiedClassName); TestVisitor(const QString &fullQualifiedClassName);
virtual ~TestVisitor(); virtual ~TestVisitor();
QMap<QString, TestCodeLocation> privateSlots() const { return m_privSlots; } QMap<QString, TestCodeLocationAndType> privateSlots() const { return m_privSlots; }
bool visit(CPlusPlus::Class *symbol); bool visit(CPlusPlus::Class *symbol);
private: private:
QString m_className; QString m_className;
QMap<QString, TestCodeLocation> m_privSlots; QMap<QString, TestCodeLocationAndType> m_privSlots;
}; };
class TestAstVisitor : public CPlusPlus::ASTVisitor class TestAstVisitor : public CPlusPlus::ASTVisitor
@@ -85,14 +88,14 @@ public:
bool visit(QmlJS::AST::StringLiteral *ast); bool visit(QmlJS::AST::StringLiteral *ast);
QString testCaseName() const { return m_currentTestCaseName; } QString testCaseName() const { return m_currentTestCaseName; }
TestCodeLocation testCaseLocation() const { return m_testCaseLocation; } TestCodeLocationAndType testCaseLocation() const { return m_testCaseLocation; }
QMap<QString, TestCodeLocation> testFunctions() const { return m_testFunctions; } QMap<QString, TestCodeLocationAndType> testFunctions() const { return m_testFunctions; }
private: private:
QmlJS::Document::Ptr m_currentDoc; QmlJS::Document::Ptr m_currentDoc;
QString m_currentTestCaseName; QString m_currentTestCaseName;
TestCodeLocation m_testCaseLocation; TestCodeLocationAndType m_testCaseLocation;
QMap<QString, TestCodeLocation> m_testFunctions; QMap<QString, TestCodeLocationAndType> m_testFunctions;
}; };