Provide basic test runner and output pane
@@ -15,7 +15,13 @@ SOURCES += \
|
|||||||
testvisitor.cpp \
|
testvisitor.cpp \
|
||||||
testinfo.cpp \
|
testinfo.cpp \
|
||||||
testcodeparser.cpp \
|
testcodeparser.cpp \
|
||||||
autotestplugin.cpp
|
autotestplugin.cpp \
|
||||||
|
testrunner.cpp \
|
||||||
|
testconfiguration.cpp \
|
||||||
|
testresult.cpp \
|
||||||
|
testresultspane.cpp \
|
||||||
|
testresultmodel.cpp \
|
||||||
|
testresultdelegate.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
testtreeview.h \
|
testtreeview.h \
|
||||||
@@ -26,7 +32,13 @@ HEADERS += \
|
|||||||
testcodeparser.h \
|
testcodeparser.h \
|
||||||
autotestplugin.h \
|
autotestplugin.h \
|
||||||
autotest_global.h \
|
autotest_global.h \
|
||||||
autotestconstants.h
|
autotestconstants.h \
|
||||||
|
testrunner.h \
|
||||||
|
testconfiguration.h \
|
||||||
|
testresult.h \
|
||||||
|
testresultspane.h \
|
||||||
|
testresultmodel.h \
|
||||||
|
testresultdelegate.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
autotest.qrc
|
autotest.qrc
|
||||||
|
@@ -5,5 +5,16 @@
|
|||||||
<file>images/expand.png</file>
|
<file>images/expand.png</file>
|
||||||
<file>images/collapse.png</file>
|
<file>images/collapse.png</file>
|
||||||
<file>images/sort.png</file>
|
<file>images/sort.png</file>
|
||||||
|
<file>images/leafsort.png</file>
|
||||||
|
<file>images/debug.png</file>
|
||||||
|
<file>images/fail.png</file>
|
||||||
|
<file>images/fatal.png</file>
|
||||||
|
<file>images/pass.png</file>
|
||||||
|
<file>images/skip.png</file>
|
||||||
|
<file>images/warn.png</file>
|
||||||
|
<file>images/xfail.png</file>
|
||||||
|
<file>images/xpass.png</file>
|
||||||
|
<file>images/run.png</file>
|
||||||
|
<file>images/runselected.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@@ -18,8 +18,10 @@
|
|||||||
|
|
||||||
#include "autotestplugin.h"
|
#include "autotestplugin.h"
|
||||||
#include "autotestconstants.h"
|
#include "autotestconstants.h"
|
||||||
|
#include "testrunner.h"
|
||||||
#include "testtreeview.h"
|
#include "testtreeview.h"
|
||||||
#include "testtreemodel.h"
|
#include "testtreemodel.h"
|
||||||
|
#include "testresultspane.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
@@ -48,6 +50,8 @@ AutotestPlugin::~AutotestPlugin()
|
|||||||
// Delete members
|
// Delete members
|
||||||
TestTreeModel *model = TestTreeModel::instance();
|
TestTreeModel *model = TestTreeModel::instance();
|
||||||
delete model;
|
delete model;
|
||||||
|
TestRunner *runner = TestRunner::instance();
|
||||||
|
delete runner;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorString)
|
bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorString)
|
||||||
@@ -74,6 +78,7 @@ bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorStri
|
|||||||
Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
|
Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
|
||||||
|
|
||||||
addAutoReleasedObject(new TestViewFactory);
|
addAutoReleasedObject(new TestViewFactory);
|
||||||
|
addAutoReleasedObject(TestResultsPane::instance());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
BIN
plugins/autotest/images/debug.png
Normal file
After Width: | Height: | Size: 621 B |
BIN
plugins/autotest/images/fail.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
plugins/autotest/images/fatal.png
Normal file
After Width: | Height: | Size: 597 B |
BIN
plugins/autotest/images/leafsort.png
Normal file
After Width: | Height: | Size: 415 B |
BIN
plugins/autotest/images/pass.png
Normal file
After Width: | Height: | Size: 595 B |
BIN
plugins/autotest/images/run.png
Normal file
After Width: | Height: | Size: 509 B |
BIN
plugins/autotest/images/runselected.png
Normal file
After Width: | Height: | Size: 709 B |
BIN
plugins/autotest/images/skip.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
plugins/autotest/images/warn.png
Normal file
After Width: | Height: | Size: 621 B |
BIN
plugins/autotest/images/xfail.png
Normal file
After Width: | Height: | Size: 547 B |
BIN
plugins/autotest/images/xpass.png
Normal file
After Width: | Height: | Size: 599 B |
@@ -50,8 +50,7 @@ void TestCodeParser::updateTestTree()
|
|||||||
TestTreeItem *autoTestRootItem = static_cast<TestTreeItem *>(autoTestRootIndex.internalPointer());
|
TestTreeItem *autoTestRootItem = static_cast<TestTreeItem *>(autoTestRootIndex.internalPointer());
|
||||||
autoTestRootItem->removeChildren();
|
autoTestRootItem->removeChildren();
|
||||||
m_model->endResetModel();
|
m_model->endResetModel();
|
||||||
ProjectExplorer::SessionManager *session = static_cast<ProjectExplorer::SessionManager *>(
|
ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance();
|
||||||
ProjectExplorer::SessionManager::instance());
|
|
||||||
if (!session || !session->hasProjects())
|
if (!session || !session->hasProjects())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -196,10 +195,10 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr doc)
|
|||||||
m_cppDocMap.insert(file, new TestInfo(tc, privSlots.keys(),
|
m_cppDocMap.insert(file, new TestInfo(tc, privSlots.keys(),
|
||||||
doc->revision(),
|
doc->revision(),
|
||||||
doc->editorRevision()));
|
doc->editorRevision()));
|
||||||
|
delete info;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete info;
|
|
||||||
delete ttItem;
|
delete ttItem;
|
||||||
} else {
|
} else {
|
||||||
m_model->beginInsertRows(autoTestRootIndex, autoTestRootItem->childCount(), autoTestRootItem->childCount());
|
m_model->beginInsertRows(autoTestRootIndex, autoTestRootItem->childCount(), autoTestRootItem->childCount());
|
||||||
|
@@ -38,7 +38,7 @@ class TestCodeParser : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit TestCodeParser(/*QObject*/TestTreeModel *parent = 0);
|
explicit TestCodeParser(TestTreeModel *parent = 0);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
71
plugins/autotest/testconfiguration.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "testconfiguration.h"
|
||||||
|
|
||||||
|
#include <projectexplorer/project.h>
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
TestConfiguration::TestConfiguration(const QString &testClass, const QStringList &testCases,
|
||||||
|
QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
m_testClass(testClass),
|
||||||
|
m_testCases(testCases),
|
||||||
|
m_project(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TestConfiguration::~TestConfiguration()
|
||||||
|
{
|
||||||
|
m_testCases.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestConfiguration::setTargetFile(const QString &targetFile)
|
||||||
|
{
|
||||||
|
m_targetFile = targetFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestConfiguration::setTargetName(const QString &targetName)
|
||||||
|
{
|
||||||
|
m_targetName = targetName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestConfiguration::setProFile(const QString &proFile)
|
||||||
|
{
|
||||||
|
m_proFile = proFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestConfiguration::setWorkingDirectory(const QString &workingDirectory)
|
||||||
|
{
|
||||||
|
m_workingDir = workingDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestConfiguration::setEnvironment(const Utils::Environment &env)
|
||||||
|
{
|
||||||
|
m_environment = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestConfiguration::setProject(ProjectExplorer::Project *project)
|
||||||
|
{
|
||||||
|
m_project = project;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
78
plugins/autotest/testconfiguration.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 TESTCONFIGURATION_H
|
||||||
|
#define TESTCONFIGURATION_H
|
||||||
|
|
||||||
|
#include <utils/environment.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
class Project;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class TestConfiguration : public QObject
|
||||||
|
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TestConfiguration(const QString &testClass, const QStringList &testCases,
|
||||||
|
QObject *parent = 0);
|
||||||
|
~TestConfiguration();
|
||||||
|
|
||||||
|
void setTargetFile(const QString &targetFile);
|
||||||
|
void setTargetName(const QString &targetName);
|
||||||
|
void setProFile(const QString &proFile);
|
||||||
|
void setWorkingDirectory(const QString &workingDirectory);
|
||||||
|
void setEnvironment(const Utils::Environment &env);
|
||||||
|
void setProject(ProjectExplorer::Project *project);
|
||||||
|
|
||||||
|
QString testClass() const { return m_testClass; }
|
||||||
|
QStringList testCases() const { return m_testCases; }
|
||||||
|
QString proFile() const { return m_proFile; }
|
||||||
|
QString targetFile() const { return m_targetFile; }
|
||||||
|
QString targetName() const { return m_targetName; }
|
||||||
|
QString workingDirectory() const { return m_workingDir; }
|
||||||
|
Utils::Environment environment() const { return m_environment; }
|
||||||
|
ProjectExplorer::Project *project() const { return m_project; }
|
||||||
|
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_testClass;
|
||||||
|
QStringList m_testCases;
|
||||||
|
QString m_proFile;
|
||||||
|
QString m_targetFile;
|
||||||
|
QString m_targetName;
|
||||||
|
QString m_workingDir;
|
||||||
|
Utils::Environment m_environment;
|
||||||
|
ProjectExplorer::Project *m_project;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
||||||
|
|
||||||
|
#endif // TESTCONFIGURATION_H
|
@@ -30,5 +30,10 @@ TestInfo::TestInfo(const QString &className, const QStringList &functions, unsig
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestInfo::~TestInfo()
|
||||||
|
{
|
||||||
|
m_functions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Autotest
|
} // namespace Autotest
|
||||||
|
@@ -30,6 +30,7 @@ public:
|
|||||||
explicit TestInfo(const QString &className, const QStringList &functions = QStringList(),
|
explicit TestInfo(const QString &className, const QStringList &functions = QStringList(),
|
||||||
unsigned revision = 0, unsigned editorRevision = 0);
|
unsigned revision = 0, unsigned editorRevision = 0);
|
||||||
|
|
||||||
|
~TestInfo();
|
||||||
const QString testClass() const { return m_className; }
|
const QString testClass() const { return m_className; }
|
||||||
void setTestClass(const QString &className) { m_className = className; }
|
void setTestClass(const QString &className) { m_className = className; }
|
||||||
const QStringList testFunctions() const { return m_functions; }
|
const QStringList testFunctions() const { return m_functions; }
|
||||||
|
101
plugins/autotest/testresult.cpp
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "testresult.h"
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
TestResult::TestResult(const QString &className, const QString &testCase, const QString &dataTag,
|
||||||
|
ResultType result, const QString &description)
|
||||||
|
: m_class(className),
|
||||||
|
m_case(testCase),
|
||||||
|
m_dataTag(dataTag),
|
||||||
|
m_result(result),
|
||||||
|
m_description(description),
|
||||||
|
m_line(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType TestResult::resultFromString(const QString &resultString)
|
||||||
|
{
|
||||||
|
if (resultString == QLatin1String("pass"))
|
||||||
|
return PASS;
|
||||||
|
if (resultString == QLatin1String("fail"))
|
||||||
|
return FAIL;
|
||||||
|
if (resultString == QLatin1String("xfail"))
|
||||||
|
return EXPECTED_FAIL;
|
||||||
|
if (resultString == QLatin1String("xpass"))
|
||||||
|
return UNEXPECTED_PASS;
|
||||||
|
if (resultString == QLatin1String("skip"))
|
||||||
|
return SKIP;
|
||||||
|
if (resultString == QLatin1String("qdebug"))
|
||||||
|
return MESSAGE_DEBUG;
|
||||||
|
if (resultString == QLatin1String("warn"))
|
||||||
|
return MESSAGE_WARN;
|
||||||
|
if (resultString == QLatin1String("qfatal"))
|
||||||
|
return MESSAGE_FATAL;
|
||||||
|
qDebug(" unexpected testresult...");
|
||||||
|
qDebug(resultString.toLatin1());
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TestResult::resultToString(const ResultType type)
|
||||||
|
{
|
||||||
|
switch(type) {
|
||||||
|
case PASS: return QLatin1String("PASS");
|
||||||
|
case FAIL: return QLatin1String("FAIL");
|
||||||
|
case EXPECTED_FAIL: return QLatin1String("XFAIL");
|
||||||
|
case UNEXPECTED_PASS: return QLatin1String("XPASS");
|
||||||
|
case SKIP: return QLatin1String("SKIP");
|
||||||
|
case MESSAGE_DEBUG: return QLatin1String("DEBUG");
|
||||||
|
case MESSAGE_WARN: return QLatin1String("WARN");
|
||||||
|
case MESSAGE_FATAL: return QLatin1String("FATAL");
|
||||||
|
case MESSAGE_INTERNAL: return QString();
|
||||||
|
default:
|
||||||
|
return QLatin1String("UNKNOWN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor TestResult::colorForType(const ResultType type)
|
||||||
|
{
|
||||||
|
switch(type) {
|
||||||
|
case PASS: return QColor("#009900");
|
||||||
|
case FAIL: return QColor("#a00000");
|
||||||
|
case EXPECTED_FAIL: return QColor("#00ff00");
|
||||||
|
case UNEXPECTED_PASS: return QColor("#ff0000");
|
||||||
|
case SKIP: return QColor("#787878");
|
||||||
|
case MESSAGE_DEBUG: return QColor("#329696");
|
||||||
|
case MESSAGE_WARN: return QColor("#d0bb00");
|
||||||
|
case MESSAGE_FATAL: return QColor("#640000");
|
||||||
|
case MESSAGE_INTERNAL: return QColor("transparent");
|
||||||
|
default:
|
||||||
|
return QColor("#000000");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const TestResult &t1, const TestResult &t2)
|
||||||
|
{
|
||||||
|
return t1.className() == t2.className()
|
||||||
|
&& t1.testCase() == t2.testCase()
|
||||||
|
&& t1.dataTag() == t2.dataTag()
|
||||||
|
&& t1.result() == t2.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
79
plugins/autotest/testresult.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 TESTRESULT_H
|
||||||
|
#define TESTRESULT_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
enum ResultType {
|
||||||
|
PASS,
|
||||||
|
FAIL,
|
||||||
|
EXPECTED_FAIL,
|
||||||
|
UNEXPECTED_PASS,
|
||||||
|
SKIP,
|
||||||
|
MESSAGE_DEBUG,
|
||||||
|
MESSAGE_WARN,
|
||||||
|
MESSAGE_FATAL,
|
||||||
|
MESSAGE_INTERNAL,
|
||||||
|
UNKNOWN // ???
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
TestResult(const QString &className, const QString &testCase, const QString &dataTag = QString(),
|
||||||
|
ResultType result = UNKNOWN, const QString &description = QString());
|
||||||
|
|
||||||
|
QString className() const { return m_class; }
|
||||||
|
QString testCase() const { return m_case; }
|
||||||
|
QString dataTag() const { return m_dataTag; }
|
||||||
|
ResultType result() const { return m_result; }
|
||||||
|
QString description() const { return m_description; }
|
||||||
|
QString fileName() const { return m_file; }
|
||||||
|
int line() const { return m_line; }
|
||||||
|
|
||||||
|
void setFileName(const QString &fileName) { m_file = fileName; }
|
||||||
|
void setLine(int line) { m_line = line; }
|
||||||
|
|
||||||
|
static ResultType resultFromString(const QString &resultString);
|
||||||
|
static QString resultToString(const ResultType type);
|
||||||
|
static QColor colorForType(const ResultType type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_class;
|
||||||
|
QString m_case;
|
||||||
|
QString m_dataTag;
|
||||||
|
ResultType m_result;
|
||||||
|
QString m_description;
|
||||||
|
QString m_file;
|
||||||
|
int m_line;
|
||||||
|
// environment?
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const TestResult &t1, const TestResult &t2);
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
||||||
|
|
||||||
|
#endif // TESTRESULT_H
|
244
plugins/autotest/testresultdelegate.cpp
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "testresultdelegate.h"
|
||||||
|
#include "testresultmodel.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemView>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QTextLayout>
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
TestResultDelegate::TestResultDelegate(QObject *parent)
|
||||||
|
: QStyledItemDelegate(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResultDelegate::~TestResultDelegate()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
QStyleOptionViewItemV4 opt = option;
|
||||||
|
initStyleOption(&opt, index);
|
||||||
|
painter->save();
|
||||||
|
|
||||||
|
QFontMetrics fm(opt.font);
|
||||||
|
QColor background;
|
||||||
|
QColor foreground;
|
||||||
|
|
||||||
|
const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget);
|
||||||
|
const bool selected = view->selectionModel()->currentIndex() == index;
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
painter->setBrush(opt.palette.highlight().color());
|
||||||
|
background = opt.palette.highlight().color();
|
||||||
|
foreground = opt.palette.highlightedText().color();
|
||||||
|
} else {
|
||||||
|
painter->setBrush(opt.palette.background().color());
|
||||||
|
background = opt.palette.background().color();
|
||||||
|
foreground = opt.palette.text().color();
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->setPen(Qt::NoPen);
|
||||||
|
painter->drawRect(opt.rect);
|
||||||
|
|
||||||
|
painter->setPen(foreground);
|
||||||
|
TestResultModel *resultModel = static_cast<TestResultModel *>(view->model());
|
||||||
|
LayoutPositions positions(opt, resultModel);
|
||||||
|
TestResult testResult = resultModel->testResult(index);
|
||||||
|
ResultType type = testResult.result();
|
||||||
|
|
||||||
|
QIcon icon = index.data(Qt::DecorationRole).value<QIcon>();
|
||||||
|
if (!icon.isNull())
|
||||||
|
painter->drawPixmap(positions.left(), positions.top(),
|
||||||
|
icon.pixmap(positions.iconSize(), positions.iconSize()));
|
||||||
|
|
||||||
|
QString typeStr = TestResult::resultToString(testResult.result());
|
||||||
|
if (selected) {
|
||||||
|
painter->drawText(positions.typeAreaLeft(), positions.top() + fm.ascent(), typeStr);
|
||||||
|
} else {
|
||||||
|
QPen tmp = painter->pen();
|
||||||
|
painter->setPen(TestResult::colorForType(testResult.result()));
|
||||||
|
painter->drawText(positions.typeAreaLeft(), positions.top() + fm.ascent(), typeStr);
|
||||||
|
painter->setPen(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString output;
|
||||||
|
switch (type) {
|
||||||
|
case ResultType::PASS:
|
||||||
|
case ResultType::FAIL:
|
||||||
|
case ResultType::EXPECTED_FAIL:
|
||||||
|
case ResultType::UNEXPECTED_PASS:
|
||||||
|
output = testResult.className() + QLatin1String("::") + testResult.testCase();
|
||||||
|
if (!testResult.dataTag().isEmpty())
|
||||||
|
output.append(QString::fromLatin1(" (%1)").arg(testResult.dataTag()));
|
||||||
|
if (selected && !testResult.description().isEmpty()) {
|
||||||
|
output.append(QLatin1Char('\n'));
|
||||||
|
output.append(testResult.description());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
output = testResult.description();
|
||||||
|
if (!selected)
|
||||||
|
output = output.split(QLatin1Char('\n')).first();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor mix;
|
||||||
|
mix.setRgb( static_cast<int>(0.7 * foreground.red() + 0.3 * background.red()),
|
||||||
|
static_cast<int>(0.7 * foreground.green() + 0.3 * background.green()),
|
||||||
|
static_cast<int>(0.7 * foreground.blue() + 0.3 * background.blue()));
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
int height = 0;
|
||||||
|
int leading = fm.leading();
|
||||||
|
int firstLineBreak = output.indexOf(QLatin1Char('\n'));
|
||||||
|
output.replace(QLatin1Char('\n'), QChar::LineSeparator);
|
||||||
|
QTextLayout tl(output);
|
||||||
|
if (firstLineBreak != -1) {
|
||||||
|
QTextLayout::FormatRange fr;
|
||||||
|
fr.start = firstLineBreak;
|
||||||
|
fr.length = output.length() - firstLineBreak;
|
||||||
|
fr.format.setFontStyleHint(QFont::Monospace);
|
||||||
|
fr.format.setForeground(mix);
|
||||||
|
tl.setAdditionalFormats(QList<QTextLayout::FormatRange>() << fr);
|
||||||
|
}
|
||||||
|
tl.beginLayout();
|
||||||
|
while (true) {
|
||||||
|
QTextLine tLine = tl.createLine();
|
||||||
|
if (!tLine.isValid())
|
||||||
|
break;
|
||||||
|
tLine.setLineWidth(positions.textAreaWidth());
|
||||||
|
height += leading;
|
||||||
|
tLine.setPosition(QPoint(0, height));
|
||||||
|
height += fm.ascent() + fm.descent();
|
||||||
|
}
|
||||||
|
tl.endLayout();
|
||||||
|
tl.draw(painter, QPoint(positions.textAreaLeft(), positions.top()));
|
||||||
|
|
||||||
|
painter->setPen(mix);
|
||||||
|
|
||||||
|
int bottomLine = positions.top() + fm.ascent() + height + leading;
|
||||||
|
painter->drawText(positions.textAreaLeft(), bottomLine, testResult.fileName());
|
||||||
|
} else {
|
||||||
|
painter->setClipRect(positions.textArea());
|
||||||
|
painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString file = testResult.fileName();
|
||||||
|
const int pos = file.lastIndexOf(QLatin1Char('/'));
|
||||||
|
if (pos != -1)
|
||||||
|
file = file.mid(pos + 1);
|
||||||
|
|
||||||
|
painter->setClipRect(positions.fileArea());
|
||||||
|
painter->drawText(positions.fileAreaLeft(), positions.top() + fm.ascent(), file);
|
||||||
|
|
||||||
|
|
||||||
|
if (testResult.line()) {
|
||||||
|
QString line = QString::number(testResult.line());
|
||||||
|
painter->setClipRect(positions.lineArea());
|
||||||
|
painter->drawText(positions.lineAreaLeft(), positions.top() + fm.ascent(), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->setClipRect(opt.rect);
|
||||||
|
painter->setPen(QColor::fromRgb(150, 150, 150));
|
||||||
|
painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
QStyleOptionViewItemV4 opt = option;
|
||||||
|
initStyleOption(&opt, index);
|
||||||
|
|
||||||
|
const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget);
|
||||||
|
const bool selected = view->selectionModel()->currentIndex() == index;
|
||||||
|
|
||||||
|
QFontMetrics fm(opt.font);
|
||||||
|
int fontHeight = fm.height();
|
||||||
|
TestResultModel *resultModel = static_cast<TestResultModel *>(view->model());
|
||||||
|
LayoutPositions positions(opt, resultModel);
|
||||||
|
QSize s;
|
||||||
|
s.setWidth(opt.rect.width());
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
TestResult testResult = resultModel->testResult(index);
|
||||||
|
|
||||||
|
QString output;
|
||||||
|
switch (testResult.result()) {
|
||||||
|
case ResultType::PASS:
|
||||||
|
case ResultType::FAIL:
|
||||||
|
case ResultType::EXPECTED_FAIL:
|
||||||
|
case ResultType::UNEXPECTED_PASS:
|
||||||
|
output = testResult.className() + QLatin1String("::") + testResult.testCase();
|
||||||
|
if (!testResult.dataTag().isEmpty())
|
||||||
|
output.append(QString::fromLatin1(" (%1)").arg(testResult.dataTag()));
|
||||||
|
if (!testResult.description().isEmpty()) {
|
||||||
|
output.append(QLatin1Char('\n'));
|
||||||
|
output.append(testResult.description());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
output = testResult.description();
|
||||||
|
}
|
||||||
|
|
||||||
|
output.replace(QLatin1Char('\n'), QChar::LineSeparator);
|
||||||
|
|
||||||
|
int height = 0;
|
||||||
|
int leading = fm.leading();
|
||||||
|
QTextLayout tl(output);
|
||||||
|
tl.beginLayout();
|
||||||
|
while (true) {
|
||||||
|
QTextLine line = tl.createLine();
|
||||||
|
if (!line.isValid())
|
||||||
|
break;
|
||||||
|
height += leading;
|
||||||
|
line.setPosition(QPoint(0, height));
|
||||||
|
height += fm.ascent() + fm.descent();
|
||||||
|
}
|
||||||
|
tl.endLayout();
|
||||||
|
|
||||||
|
s.setHeight(height + leading + 3 + (testResult.fileName().isEmpty() ? 0 : fontHeight));
|
||||||
|
} else {
|
||||||
|
s.setHeight(fontHeight + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.height() < positions.minimumHeight())
|
||||||
|
s.setHeight(positions.minimumHeight());
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultDelegate::emitSizeHintChanged(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
emit sizeHintChanged(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultDelegate::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||||
|
{
|
||||||
|
emit sizeHintChanged(current);
|
||||||
|
emit sizeHintChanged(previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
110
plugins/autotest/testresultdelegate.h
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 TESTRESULTDELEGATE_H
|
||||||
|
#define TESTRESULTDELEGATE_H
|
||||||
|
|
||||||
|
#include "testresultmodel.h"
|
||||||
|
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class TestResultDelegate : public QStyledItemDelegate
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TestResultDelegate(QObject *parent = 0);
|
||||||
|
~TestResultDelegate();
|
||||||
|
|
||||||
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||||
|
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||||
|
|
||||||
|
void emitSizeHintChanged(const QModelIndex &index);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class LayoutPositions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LayoutPositions(QStyleOptionViewItemV4 &options, TestResultModel *model)
|
||||||
|
: m_totalWidth(options.rect.width()),
|
||||||
|
m_maxFileLength(model->maxWidthOfFileName(options.font)),
|
||||||
|
m_maxLineLength(model->maxWidthOfLineNumber(options.font)),
|
||||||
|
m_realFileLength(m_maxFileLength),
|
||||||
|
m_top(options.rect.top()),
|
||||||
|
m_bottom(options.rect.bottom())
|
||||||
|
{
|
||||||
|
int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING;
|
||||||
|
if (m_maxFileLength > flexibleArea / 2)
|
||||||
|
m_realFileLength = flexibleArea / 2;
|
||||||
|
m_fontHeight = QFontMetrics(options.font).height();
|
||||||
|
m_typeAreaWidth = QFontMetrics(options.font).width(QLatin1String("XXXXXXXX"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int top() const { return m_top + ITEM_MARGIN; }
|
||||||
|
int left() const { return ITEM_MARGIN; }
|
||||||
|
int right() const { return m_totalWidth - ITEM_MARGIN; }
|
||||||
|
int bottom() const { return m_bottom; }
|
||||||
|
int minimumHeight() const { return ICON_SIZE + 2 * ITEM_MARGIN; }
|
||||||
|
|
||||||
|
int iconSize() const { return ICON_SIZE; }
|
||||||
|
int fontHeight() const { return m_fontHeight; }
|
||||||
|
int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; }
|
||||||
|
int typeAreaWidth() const { return m_typeAreaWidth; }
|
||||||
|
int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING; }
|
||||||
|
int textAreaWidth() const { return fileAreaLeft() - ITEM_SPACING - textAreaLeft(); }
|
||||||
|
int fileAreaLeft() const { return lineAreaLeft() - ITEM_SPACING - m_realFileLength; }
|
||||||
|
int lineAreaLeft() const { return right() - m_maxLineLength; }
|
||||||
|
|
||||||
|
QRect typeArea() const { return QRect(typeAreaLeft(), top(),
|
||||||
|
typeAreaWidth(), m_fontHeight); }
|
||||||
|
QRect textArea() const { return QRect(textAreaLeft(), top(),
|
||||||
|
fileAreaLeft() - ITEM_SPACING, m_fontHeight); }
|
||||||
|
QRect fileArea() const { return QRect(fileAreaLeft(), top(),
|
||||||
|
lineAreaLeft() - ITEM_SPACING, m_fontHeight); }
|
||||||
|
|
||||||
|
QRect lineArea() const { return QRect(lineAreaLeft(), top(),
|
||||||
|
m_maxLineLength, m_fontHeight); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_totalWidth;
|
||||||
|
int m_maxFileLength;
|
||||||
|
int m_maxLineLength;
|
||||||
|
int m_realFileLength;
|
||||||
|
int m_top;
|
||||||
|
int m_bottom;
|
||||||
|
int m_fontHeight;
|
||||||
|
int m_typeAreaWidth;
|
||||||
|
|
||||||
|
static const int ICON_SIZE = 16;
|
||||||
|
static const int ITEM_MARGIN = 2;
|
||||||
|
static const int ITEM_SPACING = 4;
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
||||||
|
|
||||||
|
#endif // TESTRESULTDELEGATE_H
|
163
plugins/autotest/testresultmodel.cpp
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "testresultmodel.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
TestResultModel::TestResultModel(QObject *parent) :
|
||||||
|
QAbstractItemModel(parent),
|
||||||
|
m_widthOfLineNumber(0),
|
||||||
|
m_maxWidthOfFileName(0),
|
||||||
|
m_lastMaxWidthIndex(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResultModel::~TestResultModel()
|
||||||
|
{
|
||||||
|
m_testResults.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex TestResultModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return QModelIndex();
|
||||||
|
return createIndex(row, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex TestResultModel::parent(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TestResultModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return parent.isValid() ? 0 : m_testResults.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TestResultModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return parent.isValid() ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QIcon testResultIcon(ResultType result) {
|
||||||
|
static QIcon icons[8] = {
|
||||||
|
QIcon(QLatin1String(":/images/pass.png")),
|
||||||
|
QIcon(QLatin1String(":/images/fail.png")),
|
||||||
|
QIcon(QLatin1String(":/images/xfail.png")),
|
||||||
|
QIcon(QLatin1String(":/images/xpass.png")),
|
||||||
|
QIcon(QLatin1String(":/images/skip.png")),
|
||||||
|
QIcon(QLatin1String(":/images/debug.png")),
|
||||||
|
QIcon(QLatin1String(":/images/warn.png")),
|
||||||
|
QIcon(QLatin1String(":/images/fatal.png")),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (result < 0 || result >= MESSAGE_INTERNAL)
|
||||||
|
return QIcon();
|
||||||
|
return icons[result];
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TestResultModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= m_testResults.count() || index.column() != 0)
|
||||||
|
return QVariant();
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
const TestResult &tr = m_testResults.at(index.row());
|
||||||
|
switch (tr.result()) {
|
||||||
|
case ResultType::PASS:
|
||||||
|
case ResultType::FAIL:
|
||||||
|
case ResultType::EXPECTED_FAIL:
|
||||||
|
case ResultType::UNEXPECTED_PASS:
|
||||||
|
case ResultType::SKIP:
|
||||||
|
return QString::fromLatin1("%1::%2 (%3) - %4").arg(tr.className(), tr.testCase(),
|
||||||
|
tr.dataTag(), tr.fileName());
|
||||||
|
default:
|
||||||
|
return tr.description();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (role == Qt::DecorationRole) {
|
||||||
|
const TestResult &tr = m_testResults.at(index.row());
|
||||||
|
return testResultIcon(tr.result());
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultModel::addTestResult(const TestResult &testResult)
|
||||||
|
{
|
||||||
|
beginInsertRows(QModelIndex(), m_testResults.size(), m_testResults.size());
|
||||||
|
m_testResults.append(testResult);
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultModel::clearTestResults()
|
||||||
|
{
|
||||||
|
if (m_testResults.size() == 0)
|
||||||
|
return;
|
||||||
|
beginRemoveRows(QModelIndex(), 0, m_testResults.size() - 1);
|
||||||
|
m_testResults.clear();
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult TestResultModel::testResult(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return TestResult(QString(), QString());
|
||||||
|
return m_testResults.at(index.row());
|
||||||
|
}
|
||||||
|
|
||||||
|
int TestResultModel::maxWidthOfFileName(const QFont &font)
|
||||||
|
{
|
||||||
|
int count = m_testResults.size();
|
||||||
|
if (count == 0)
|
||||||
|
return 0;
|
||||||
|
if (m_maxWidthOfFileName > 0 && font == m_measurementFont && m_lastMaxWidthIndex == count - 1)
|
||||||
|
return m_maxWidthOfFileName;
|
||||||
|
|
||||||
|
QFontMetrics fm(font);
|
||||||
|
m_measurementFont = font;
|
||||||
|
|
||||||
|
for (int i = m_lastMaxWidthIndex; i < count; ++i) {
|
||||||
|
QString filename = m_testResults.at(i).fileName();
|
||||||
|
const int pos = filename.lastIndexOf(QLatin1Char('/'));
|
||||||
|
if (pos != -1)
|
||||||
|
filename = filename.mid(pos +1);
|
||||||
|
|
||||||
|
m_maxWidthOfFileName = qMax(m_maxWidthOfFileName, fm.width(filename));
|
||||||
|
}
|
||||||
|
m_lastMaxWidthIndex = count - 1;
|
||||||
|
return m_maxWidthOfFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TestResultModel::maxWidthOfLineNumber(const QFont &font)
|
||||||
|
{
|
||||||
|
if (m_widthOfLineNumber == 0 || font != m_measurementFont) {
|
||||||
|
QFontMetrics fm(font);
|
||||||
|
m_measurementFont = font;
|
||||||
|
m_widthOfLineNumber = fm.width(QLatin1String("88888"));
|
||||||
|
}
|
||||||
|
return m_widthOfLineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
66
plugins/autotest/testresultmodel.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 TESTRESULTMODEL_H
|
||||||
|
#define TESTRESULTMODEL_H
|
||||||
|
|
||||||
|
#include "testresult.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include <QFont>
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class TestResultModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TestResultModel(QObject *parent = 0);
|
||||||
|
~TestResultModel();
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent) const;
|
||||||
|
QModelIndex parent(const QModelIndex &) const;
|
||||||
|
int rowCount(const QModelIndex &parent) const;
|
||||||
|
int columnCount(const QModelIndex &parent) const;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
|
|
||||||
|
void addTestResult(const TestResult &testResult);
|
||||||
|
void clearTestResults();
|
||||||
|
|
||||||
|
bool hasResults() const { return m_testResults.size() > 0; }
|
||||||
|
TestResult testResult(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
int maxWidthOfFileName(const QFont &font);
|
||||||
|
int maxWidthOfLineNumber(const QFont &font);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<TestResult> m_testResults;
|
||||||
|
int m_widthOfLineNumber;
|
||||||
|
int m_maxWidthOfFileName;
|
||||||
|
int m_lastMaxWidthIndex;
|
||||||
|
QFont m_measurementFont;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
||||||
|
|
||||||
|
#endif // TESTRESULTMODEL_H
|
216
plugins/autotest/testresultspane.cpp
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "testresultspane.h"
|
||||||
|
#include "testresultmodel.h"
|
||||||
|
#include "testresultdelegate.h"
|
||||||
|
#include "testrunner.h"
|
||||||
|
#include "testtreemodel.h"
|
||||||
|
|
||||||
|
#include <coreplugin/icontext.h>
|
||||||
|
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
#include <utils/itemviews.h>
|
||||||
|
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
TestResultsPane::TestResultsPane(QObject *parent) :
|
||||||
|
Core::IOutputPane(parent),
|
||||||
|
m_context(new Core::IContext(this))
|
||||||
|
{
|
||||||
|
m_listView = new Utils::ListView;
|
||||||
|
m_model = new TestResultModel(this);
|
||||||
|
m_listView->setModel(m_model);
|
||||||
|
TestResultDelegate *trd = new TestResultDelegate(this);
|
||||||
|
m_listView->setItemDelegate(trd);
|
||||||
|
|
||||||
|
createToolButtons();
|
||||||
|
|
||||||
|
connect(m_listView, &Utils::ListView::activated, this, &TestResultsPane::onItemActivated);
|
||||||
|
connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||||
|
trd, &TestResultDelegate::currentChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::createToolButtons()
|
||||||
|
{
|
||||||
|
m_runAll = new QToolButton(m_listView);
|
||||||
|
m_runAll->setIcon(QIcon(QLatin1String(":/images/run.png")));
|
||||||
|
m_runAll->setToolTip(tr("Run All Tests"));
|
||||||
|
connect(m_runAll, &QToolButton::clicked, this, &TestResultsPane::onRunAllTriggered);
|
||||||
|
|
||||||
|
m_runSelected = new QToolButton(m_listView);
|
||||||
|
m_runSelected->setIcon(QIcon(QLatin1String(":/images/runselected.png")));
|
||||||
|
m_runSelected->setToolTip(tr("Run Selected Tests"));
|
||||||
|
connect(m_runSelected, &QToolButton::clicked, this, &TestResultsPane::onRunSelectedTriggered);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResultsPane *m_instance = 0;
|
||||||
|
|
||||||
|
TestResultsPane *TestResultsPane::instance()
|
||||||
|
{
|
||||||
|
if (!m_instance)
|
||||||
|
m_instance = new TestResultsPane;
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResultsPane::~TestResultsPane()
|
||||||
|
{
|
||||||
|
delete m_listView;
|
||||||
|
m_instance = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::addTestResult(const TestResult &result)
|
||||||
|
{
|
||||||
|
m_model->addTestResult(result);
|
||||||
|
if (!m_listView->isVisible())
|
||||||
|
popup(Core::IOutputPane::NoModeSwitch);
|
||||||
|
flash();
|
||||||
|
navigateStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *TestResultsPane::outputWidget(QWidget *parent)
|
||||||
|
{
|
||||||
|
if (m_listView) {
|
||||||
|
m_listView->setParent(parent);
|
||||||
|
} else {
|
||||||
|
m_listView = new Utils::ListView(parent);
|
||||||
|
}
|
||||||
|
return m_listView;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QWidget *> TestResultsPane::toolBarWidgets() const
|
||||||
|
{
|
||||||
|
return QList<QWidget *>() << m_runAll << m_runSelected; // add filter as well
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TestResultsPane::displayName() const
|
||||||
|
{
|
||||||
|
return tr("Test Results");
|
||||||
|
}
|
||||||
|
|
||||||
|
int TestResultsPane::priorityInStatusBar() const
|
||||||
|
{
|
||||||
|
return -666;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::clearContents()
|
||||||
|
{
|
||||||
|
m_model->clearTestResults();
|
||||||
|
navigateStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::visibilityChanged(bool)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::setFocus()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestResultsPane::hasFocus() const
|
||||||
|
{
|
||||||
|
return m_listView->hasFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestResultsPane::canFocus() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestResultsPane::canNavigate() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestResultsPane::canNext() const
|
||||||
|
{
|
||||||
|
return m_model->hasResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestResultsPane::canPrevious() const
|
||||||
|
{
|
||||||
|
return m_model->hasResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::goToNext()
|
||||||
|
{
|
||||||
|
if (!canNext())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QModelIndex currentIndex = m_listView->currentIndex();
|
||||||
|
if (currentIndex.isValid()) {
|
||||||
|
int row = currentIndex.row() + 1;
|
||||||
|
if (row == m_model->rowCount(QModelIndex()))
|
||||||
|
row = 0;
|
||||||
|
currentIndex = m_model->index(row, 0, QModelIndex());
|
||||||
|
} else {
|
||||||
|
currentIndex = m_model->index(0, 0, QModelIndex());
|
||||||
|
}
|
||||||
|
m_listView->setCurrentIndex(currentIndex);
|
||||||
|
onItemActivated(currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::goToPrev()
|
||||||
|
{
|
||||||
|
if (!canPrevious())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QModelIndex currentIndex = m_listView->currentIndex();
|
||||||
|
if (currentIndex.isValid()) {
|
||||||
|
int row = currentIndex.row() - 1;
|
||||||
|
if (row < 0)
|
||||||
|
row = m_model->rowCount(QModelIndex()) - 1;
|
||||||
|
currentIndex = m_model->index(row, 0, QModelIndex());
|
||||||
|
} else {
|
||||||
|
currentIndex = m_model->index(m_model->rowCount(QModelIndex()) - 1, 0, QModelIndex());
|
||||||
|
}
|
||||||
|
m_listView->setCurrentIndex(currentIndex);
|
||||||
|
onItemActivated(currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::onItemActivated(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
TestResult tr = m_model->testResult(index);
|
||||||
|
if (!tr.fileName().isEmpty())
|
||||||
|
Core::EditorManager::openEditorAt(tr.fileName(), tr.line(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::onRunAllTriggered()
|
||||||
|
{
|
||||||
|
TestRunner *runner = TestRunner::instance();
|
||||||
|
runner->setSelectedTests(TestTreeModel::instance()->getAllTestCases());
|
||||||
|
runner->runTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestResultsPane::onRunSelectedTriggered()
|
||||||
|
{
|
||||||
|
TestRunner *runner = TestRunner::instance();
|
||||||
|
runner->setSelectedTests(TestTreeModel::instance()->getSelectedTests());
|
||||||
|
runner->runTests();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
91
plugins/autotest/testresultspane.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 TESTRESULTSPANE_H
|
||||||
|
#define TESTRESULTSPANE_H
|
||||||
|
|
||||||
|
#include <coreplugin/ioutputpane.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QModelIndex;
|
||||||
|
class QToolButton;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class IContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
class ListView;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class TestResult;
|
||||||
|
class TestResultModel;
|
||||||
|
|
||||||
|
class TestResultsPane : public Core::IOutputPane
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
virtual ~TestResultsPane();
|
||||||
|
static TestResultsPane *instance();
|
||||||
|
|
||||||
|
void addTestResult(const TestResult &result);
|
||||||
|
|
||||||
|
// IOutputPane interface
|
||||||
|
QWidget *outputWidget(QWidget *parent);
|
||||||
|
QList<QWidget *> toolBarWidgets() const;
|
||||||
|
QString displayName() const;
|
||||||
|
int priorityInStatusBar() const;
|
||||||
|
void clearContents();
|
||||||
|
void visibilityChanged(bool);
|
||||||
|
void setFocus();
|
||||||
|
bool hasFocus() const;
|
||||||
|
bool canFocus() const;
|
||||||
|
bool canNavigate() const;
|
||||||
|
bool canNext() const;
|
||||||
|
bool canPrevious() const;
|
||||||
|
void goToNext();
|
||||||
|
void goToPrev();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onItemActivated(const QModelIndex &index);
|
||||||
|
void onRunAllTriggered();
|
||||||
|
void onRunSelectedTriggered();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit TestResultsPane(QObject *parent = 0);
|
||||||
|
void createToolButtons();
|
||||||
|
|
||||||
|
Utils::ListView *m_listView;
|
||||||
|
TestResultModel *m_model;
|
||||||
|
Core::IContext *m_context;
|
||||||
|
QToolButton *m_runAll;
|
||||||
|
QToolButton *m_runSelected;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
||||||
|
|
||||||
|
#endif // TESTRESULTSPANE_H
|
371
plugins/autotest/testrunner.cpp
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "testresultspane.h"
|
||||||
|
#include "testrunner.h"
|
||||||
|
|
||||||
|
#include <QDebug> // REMOVE
|
||||||
|
|
||||||
|
#include <projectexplorer/buildmanager.h>
|
||||||
|
#include <projectexplorer/project.h>
|
||||||
|
#include <projectexplorer/projectexplorer.h>
|
||||||
|
#include <projectexplorer/projectexplorersettings.h>
|
||||||
|
|
||||||
|
#include <QTime>
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
static TestRunner* m_instance = 0;
|
||||||
|
|
||||||
|
TestRunner *TestRunner::instance()
|
||||||
|
{
|
||||||
|
if (!m_instance)
|
||||||
|
m_instance = new TestRunner;
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestRunner::TestRunner(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_building(false)
|
||||||
|
{
|
||||||
|
m_runner.setReadChannelMode(QProcess::MergedChannels);
|
||||||
|
m_runner.setReadChannel(QProcess::StandardOutput);
|
||||||
|
|
||||||
|
connect(&m_runner, &QProcess::readyReadStandardOutput,
|
||||||
|
this, &TestRunner::processOutput, Qt::DirectConnection);
|
||||||
|
connect(&m_runner, SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||||
|
this, SLOT(onRunnerFinished(int,QProcess::ExitStatus)), Qt::DirectConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestRunner::~TestRunner()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_selectedTests);
|
||||||
|
m_selectedTests.clear();
|
||||||
|
m_instance = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
|
||||||
|
{
|
||||||
|
qDeleteAll(m_selectedTests);
|
||||||
|
m_selectedTests.clear();
|
||||||
|
m_selectedTests = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRunner::runTests()
|
||||||
|
{
|
||||||
|
if (m_selectedTests.empty()) {
|
||||||
|
TestResultsPane::instance()->addTestResult(
|
||||||
|
TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
|
||||||
|
tr("*** No tests selected - canceling Test Run ***")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectExplorer::Project *project = m_selectedTests.at(0)->project();
|
||||||
|
|
||||||
|
if (!project) {// add a warning or info to output? possible at all?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance();
|
||||||
|
ProjectExplorer::Internal::ProjectExplorerSettings pes = pep->projectExplorerSettings();
|
||||||
|
if (pes.buildBeforeDeploy) {
|
||||||
|
if (!project->hasActiveBuildSettings()) {
|
||||||
|
qDebug() << "no active build settings...???"; // let it configure?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buildProject(project);
|
||||||
|
while (m_building) {
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_buildSucceeded) {
|
||||||
|
TestResultsPane::instance()->addTestResult(
|
||||||
|
TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
|
||||||
|
tr("*** Build failed - canceling Test Run ***")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear old log and output pane
|
||||||
|
m_xmlLog.clear();
|
||||||
|
TestResultsPane::instance()->clearContents();
|
||||||
|
|
||||||
|
foreach (TestConfiguration *tc, m_selectedTests) {
|
||||||
|
QString cmd = tc->targetFile();
|
||||||
|
QString workDir = tc->workingDirectory();
|
||||||
|
QStringList args;
|
||||||
|
Utils::Environment env = tc->environment();
|
||||||
|
|
||||||
|
args << QLatin1String("-xml");
|
||||||
|
if (tc->testCases().count())
|
||||||
|
args << tc->testCases();
|
||||||
|
|
||||||
|
exec(cmd, args, workDir, env);
|
||||||
|
}
|
||||||
|
qDebug("test run finished");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRunner::stopTestRun()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************** XML line parser helper ********************/
|
||||||
|
|
||||||
|
static bool xmlStartsWith(const QString &code, const QString &start, QString &result)
|
||||||
|
{
|
||||||
|
if (code.startsWith(start)) {
|
||||||
|
result = code.mid(start.length());
|
||||||
|
result = result.left(result.indexOf(QLatin1Char('"')));
|
||||||
|
result = result.left(result.indexOf(QLatin1String("</")));
|
||||||
|
return !result.isEmpty();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xmlCData(const QString &code, const QString &start, QString &result)
|
||||||
|
{
|
||||||
|
if (code.startsWith(start)) {
|
||||||
|
int index = code.indexOf(QLatin1String("<![CDATA[")) + 9;
|
||||||
|
result = code.mid(index, code.indexOf(QLatin1String("]]>"), index) - index);
|
||||||
|
return !result.isEmpty();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xmlExtractTypeFileLine(const QString &code, const QString &tagStart,
|
||||||
|
ResultType &result, QString &file, int &line)
|
||||||
|
{
|
||||||
|
if (code.startsWith(tagStart)) {
|
||||||
|
int start = code.indexOf(QLatin1String(" type=\"")) + 7;
|
||||||
|
result = TestResult::resultFromString(
|
||||||
|
code.mid(start, code.indexOf(QLatin1Char('"'), start) - start));
|
||||||
|
start = code.indexOf(QLatin1String(" file=\"")) + 7;
|
||||||
|
file = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start);
|
||||||
|
start = code.indexOf(QLatin1String(" line=\"")) + 7;
|
||||||
|
line = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start).toInt();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************** XML line parser helper end ******************/
|
||||||
|
|
||||||
|
void TestRunner::processOutput()
|
||||||
|
{
|
||||||
|
static QString className;
|
||||||
|
static QString testCase;
|
||||||
|
static QString dataTag;
|
||||||
|
static ResultType result = ResultType::UNKNOWN;
|
||||||
|
static QString description;
|
||||||
|
static QString file;
|
||||||
|
static int lineNumber = 0;
|
||||||
|
static QString duration;
|
||||||
|
static bool readingDescription = false;
|
||||||
|
static QString qtVersion;
|
||||||
|
static QString qtestVersion;
|
||||||
|
|
||||||
|
while (m_runner.canReadLine()) {
|
||||||
|
// TODO Qt5 uses UTF-8 - while Qt4 uses ISO-8859-1 - could this be a problem?
|
||||||
|
QString line = QString::fromUtf8(m_runner.readLine());
|
||||||
|
line = line.trimmed();
|
||||||
|
m_xmlLog.append(line);
|
||||||
|
if (line.isEmpty() || line.startsWith(QLatin1String("<?xml version"))) {
|
||||||
|
className = QString();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (xmlStartsWith(line, QLatin1String("<TestCase name=\""), className))
|
||||||
|
continue;
|
||||||
|
if (xmlStartsWith(line, QLatin1String("<TestFunction name=\""), testCase)) {
|
||||||
|
dataTag = QString();
|
||||||
|
description = QString();
|
||||||
|
duration = QString();
|
||||||
|
file = QString();
|
||||||
|
result = ResultType::UNKNOWN;
|
||||||
|
lineNumber = 0;
|
||||||
|
readingDescription = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (xmlStartsWith(line, QLatin1String("<Duration msecs=\""), duration)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (xmlExtractTypeFileLine(line, QLatin1String("<Message"), result, file, lineNumber))
|
||||||
|
continue;
|
||||||
|
if (xmlCData(line, QLatin1String("<DataTag>"), dataTag))
|
||||||
|
continue;
|
||||||
|
if (xmlCData(line, QLatin1String("<Description>"), description)) {
|
||||||
|
if (!line.endsWith(QLatin1String("</Description>")))
|
||||||
|
readingDescription = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (xmlExtractTypeFileLine(line, QLatin1String("<Incident"), result, file, lineNumber)) {
|
||||||
|
if (line.endsWith(QLatin1String("/>"))) {
|
||||||
|
TestResult testResult(className, testCase, dataTag, result, description);
|
||||||
|
if (!file.isEmpty())
|
||||||
|
file = QFileInfo(m_runner.workingDirectory(), file).canonicalFilePath();
|
||||||
|
testResult.setFileName(file);
|
||||||
|
testResult.setLine(lineNumber);
|
||||||
|
TestResultsPane::instance()->addTestResult(testResult);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line == QLatin1String("</Message>") || line == QLatin1String("</Incident>")) {
|
||||||
|
TestResult testResult(className, testCase, dataTag, result, description);
|
||||||
|
if (!file.isEmpty())
|
||||||
|
file = QFileInfo(m_runner.workingDirectory(), file).canonicalFilePath();
|
||||||
|
testResult.setFileName(file);
|
||||||
|
testResult.setLine(lineNumber);
|
||||||
|
TestResultsPane::instance()->addTestResult(testResult);
|
||||||
|
description = QString();
|
||||||
|
} else if (line == QLatin1String("</TestFunction>") && !duration.isEmpty()) {
|
||||||
|
TestResult testResult(className, testCase, QString(), ResultType::MESSAGE_INTERNAL,
|
||||||
|
tr("execution took %1ms").arg(duration));
|
||||||
|
TestResultsPane::instance()->addTestResult(testResult);
|
||||||
|
} else if (line == QLatin1String("</TestCase>") && !duration.isEmpty()) {
|
||||||
|
TestResult testResult(className, QString(), QString(), ResultType::MESSAGE_INTERNAL,
|
||||||
|
tr("Test execution took %1ms").arg(duration));
|
||||||
|
TestResultsPane::instance()->addTestResult(testResult);
|
||||||
|
} else if (readingDescription) {
|
||||||
|
if (line.endsWith(QLatin1String("]]></Description>"))) {
|
||||||
|
description.append(QLatin1Char('\n'));
|
||||||
|
description.append(line.left(line.indexOf(QLatin1String("]]></Description>"))));
|
||||||
|
readingDescription = false;
|
||||||
|
} else {
|
||||||
|
description.append(QLatin1Char('\n'));
|
||||||
|
description.append(line);
|
||||||
|
}
|
||||||
|
} else if (xmlStartsWith(line, QLatin1String("<QtVersion>"), qtVersion)) {
|
||||||
|
TestResultsPane::instance()->addTestResult(
|
||||||
|
TestResult(QString(), QString(), QString(), ResultType::MESSAGE_INTERNAL,
|
||||||
|
tr("Qt Version: %1").arg(qtVersion)));
|
||||||
|
} else if (xmlStartsWith(line, QLatin1String("<QTestVersion>"), qtestVersion)) {
|
||||||
|
TestResultsPane::instance()->addTestResult(
|
||||||
|
TestResult(QString(), QString(), QString(), ResultType::MESSAGE_INTERNAL,
|
||||||
|
tr("QTest Version: %1").arg(qtestVersion)));
|
||||||
|
} else {
|
||||||
|
// qDebug() << "Unhandled line:" << line; // TODO remove
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRunner::onRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||||
|
{
|
||||||
|
qDebug("runnerFinished");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRunner::buildProject(ProjectExplorer::Project *project)
|
||||||
|
{
|
||||||
|
m_building = true;
|
||||||
|
m_buildSucceeded = false;
|
||||||
|
ProjectExplorer::BuildManager *mgr = static_cast<ProjectExplorer::BuildManager *>(
|
||||||
|
ProjectExplorer::BuildManager::instance());
|
||||||
|
ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance();
|
||||||
|
pep->buildProject(project);
|
||||||
|
connect(mgr, &ProjectExplorer::BuildManager::buildQueueFinished,
|
||||||
|
this, &TestRunner::buildFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRunner::buildFinished(bool success)
|
||||||
|
{
|
||||||
|
ProjectExplorer::BuildManager *mgr = static_cast<ProjectExplorer::BuildManager *>(
|
||||||
|
ProjectExplorer::BuildManager::instance());
|
||||||
|
disconnect(mgr, &ProjectExplorer::BuildManager::buildQueueFinished,
|
||||||
|
this, &TestRunner::buildFinished);
|
||||||
|
m_building = false;
|
||||||
|
m_buildSucceeded = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString which(const QString &path, const QString &cmd)
|
||||||
|
{
|
||||||
|
if (path.isEmpty() || cmd.isEmpty())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
QStringList paths;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
paths = path.split(QLatin1Char(';'));
|
||||||
|
#else
|
||||||
|
paths = path.split(QLatin1Char(':'));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
foreach (const QString p, paths) {
|
||||||
|
QString fName = p + QDir::separator() + cmd;
|
||||||
|
QFileInfo fi(fName);
|
||||||
|
if (fi.exists() && fi.isExecutable())
|
||||||
|
return fName;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
fi = QFileInfo(fName + QLatin1String(".exe"));
|
||||||
|
if (fi.exists())
|
||||||
|
return fi.absoluteFilePath();
|
||||||
|
fi = QFileInfo(fName + QLatin1String(".bat"));
|
||||||
|
if (fi.exists())
|
||||||
|
return fi.absoluteFilePath();
|
||||||
|
fi = QFileInfo(fName + QLatin1String(".cmd"));
|
||||||
|
if (fi.exists())
|
||||||
|
return fi.absoluteFilePath();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestRunner::exec(const QString &cmd, const QStringList &args, const QString &workingDir,
|
||||||
|
const Utils::Environment &env, int timeout)
|
||||||
|
{
|
||||||
|
if (m_runner.state() != QProcess::NotRunning) { // kill the runner if it's running already
|
||||||
|
m_runner.kill();
|
||||||
|
m_runner.waitForFinished();
|
||||||
|
}
|
||||||
|
QString runCmd;
|
||||||
|
if (!QDir::toNativeSeparators(cmd).contains(QDir::separator())) {
|
||||||
|
if (env.hasKey(QLatin1String("PATH")))
|
||||||
|
runCmd = which(env.value(QLatin1String("PATH")), cmd);
|
||||||
|
} else if (QFileInfo(cmd).exists()) {
|
||||||
|
runCmd = cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runCmd.isEmpty()) {
|
||||||
|
qDebug("Could not find cmd...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_runner.setWorkingDirectory(workingDir);
|
||||||
|
m_runner.setProcessEnvironment(env.toProcessEnvironment());
|
||||||
|
QTime executionTimer;
|
||||||
|
|
||||||
|
if (args.count()) {
|
||||||
|
m_runner.start(runCmd, args);
|
||||||
|
} else {
|
||||||
|
m_runner.start(runCmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = m_runner.waitForStarted();
|
||||||
|
executionTimer.start();
|
||||||
|
if (ok) {
|
||||||
|
while (m_runner.state() == QProcess::Running && executionTimer.elapsed() < timeout) {
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok && executionTimer.elapsed() < timeout) {
|
||||||
|
return m_runner.exitCode() == 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
74
plugins/autotest/testrunner.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 TESTRUNNER_H
|
||||||
|
#define TESTRUNNER_H
|
||||||
|
|
||||||
|
#include "testconfiguration.h"
|
||||||
|
#include "testresult.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
namespace ProjectExplorer {
|
||||||
|
class Project;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Autotest {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class TestRunner : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static TestRunner* instance();
|
||||||
|
~TestRunner();
|
||||||
|
|
||||||
|
void setSelectedTests(const QList<TestConfiguration *> &selected);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void runTests();
|
||||||
|
void stopTestRun();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void processOutput();
|
||||||
|
void onRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
void buildProject(ProjectExplorer::Project *project);
|
||||||
|
void buildFinished(bool success);
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit TestRunner(QObject *parent = 0);
|
||||||
|
bool exec(const QString &cmd, const QStringList &args, const QString &workingDir = QString(),
|
||||||
|
const Utils::Environment &env = Utils::Environment(), int timeout = 60000);
|
||||||
|
|
||||||
|
QProcess m_runner;
|
||||||
|
QList<TestConfiguration *> m_selectedTests;
|
||||||
|
bool m_building;
|
||||||
|
bool m_buildSucceeded;
|
||||||
|
|
||||||
|
QStringList m_xmlLog; // holds complete xml log of the last test run
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Autotest
|
||||||
|
|
||||||
|
#endif // TESTRUNNER_H
|
@@ -24,6 +24,16 @@
|
|||||||
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
|
#include <projectexplorer/buildtargetinfo.h>
|
||||||
|
#include <projectexplorer/environmentaspect.h>
|
||||||
|
#include <projectexplorer/localapplicationrunconfiguration.h>
|
||||||
|
#include <projectexplorer/project.h>
|
||||||
|
#include <projectexplorer/runconfiguration.h>
|
||||||
|
#include <projectexplorer/session.h>
|
||||||
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
|
#include <cpptools/cppmodelmanager.h>
|
||||||
|
|
||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -115,6 +125,16 @@ int TestTreeModel::columnCount(const QModelIndex &) const
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QIcon testTreeIcon(TestTreeItem::Type type)
|
||||||
|
{
|
||||||
|
static QIcon icons[3] = {
|
||||||
|
QIcon(),
|
||||||
|
QIcon(QLatin1String(":/images/class.png")),
|
||||||
|
QIcon(QLatin1String(":/images/func.png"))
|
||||||
|
};
|
||||||
|
return icons[type];
|
||||||
|
}
|
||||||
|
|
||||||
QVariant TestTreeModel::data(const QModelIndex &index, int role) const
|
QVariant TestTreeModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
@@ -138,15 +158,7 @@ QVariant TestTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
return item->filePath();
|
return item->filePath();
|
||||||
case Qt::DecorationRole:
|
case Qt::DecorationRole:
|
||||||
switch(item->type()) {
|
return testTreeIcon(item->type());
|
||||||
case TestTreeItem::TEST_CLASS:
|
|
||||||
return QIcon(QLatin1String(":/images/class.png"));
|
|
||||||
case TestTreeItem::TEST_FUNCTION:
|
|
||||||
return QIcon(QLatin1String(":/images/func.png"));
|
|
||||||
case TestTreeItem::ROOT:
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
if (item->type() == TestTreeItem::ROOT)
|
if (item->type() == TestTreeItem::ROOT)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
@@ -234,6 +246,110 @@ bool TestTreeModel::hasTests() const
|
|||||||
return m_autoTestRootItem->childCount() > 0 /*|| m_quickTestRootItem->childCount() > 0*/;
|
return m_autoTestRootItem->childCount() > 0 /*|| m_quickTestRootItem->childCount() > 0*/;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void addProjectInformation(TestConfiguration *config, const QString &filePath)
|
||||||
|
{
|
||||||
|
QString targetFile;
|
||||||
|
QString targetName;
|
||||||
|
QString workDir;
|
||||||
|
QString proFile;
|
||||||
|
Utils::Environment env;
|
||||||
|
ProjectExplorer::Project *project = 0;
|
||||||
|
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
|
||||||
|
QList<CppTools::ProjectPart::Ptr> projParts = cppMM->projectPart(filePath);
|
||||||
|
if (!projParts.empty()) {
|
||||||
|
proFile = projParts.at(0)->projectFile;
|
||||||
|
project = projParts.at(0)->project; // necessary to grab this here? or should this be the current active startup project anyway?
|
||||||
|
}
|
||||||
|
if (project) {
|
||||||
|
ProjectExplorer::Target *target = project->activeTarget();
|
||||||
|
ProjectExplorer::BuildTargetInfoList appTargets = target->applicationTargets();
|
||||||
|
foreach (ProjectExplorer::BuildTargetInfo bti, appTargets.list) {
|
||||||
|
if (bti.isValid() && bti.projectFilePath.toString() == proFile) {
|
||||||
|
targetFile = bti.targetFilePath.toString();
|
||||||
|
targetName = bti.targetName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ProjectExplorer::RunConfiguration *> rcs = target->runConfigurations();
|
||||||
|
foreach (ProjectExplorer::RunConfiguration *rc, rcs) {
|
||||||
|
if (ProjectExplorer::LocalApplicationRunConfiguration *localRunConfiguration
|
||||||
|
= qobject_cast<ProjectExplorer::LocalApplicationRunConfiguration *>(rc)) {
|
||||||
|
if (localRunConfiguration->executable() == targetFile) {
|
||||||
|
workDir = localRunConfiguration->workingDirectory();
|
||||||
|
QList<ProjectExplorer::IRunConfigurationAspect *> aspects
|
||||||
|
= localRunConfiguration->extraAspects();
|
||||||
|
foreach (ProjectExplorer::IRunConfigurationAspect *aspect, aspects) {
|
||||||
|
if (ProjectExplorer::EnvironmentAspect *asp
|
||||||
|
= qobject_cast<ProjectExplorer::EnvironmentAspect *>(aspect)) {
|
||||||
|
env = asp->environment();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config->setTargetFile(targetFile);
|
||||||
|
config->setTargetName(targetName);
|
||||||
|
config->setWorkingDirectory(workDir);
|
||||||
|
config->setProFile(proFile);
|
||||||
|
config->setEnvironment(env);
|
||||||
|
config->setProject(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TestConfiguration *> TestTreeModel::getAllTestCases() const
|
||||||
|
{
|
||||||
|
QList<TestConfiguration *> result;
|
||||||
|
|
||||||
|
int count = m_autoTestRootItem->childCount();
|
||||||
|
for (int row = 0; row < count; ++row) {
|
||||||
|
TestTreeItem *child = m_autoTestRootItem->child(row);
|
||||||
|
|
||||||
|
TestConfiguration *tc = new TestConfiguration(child->name(), QStringList());
|
||||||
|
addProjectInformation(tc, child->filePath());
|
||||||
|
result << tc;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TestConfiguration *> TestTreeModel::getSelectedTests() const
|
||||||
|
{
|
||||||
|
QList<TestConfiguration *> result;
|
||||||
|
TestConfiguration *tc;
|
||||||
|
|
||||||
|
int count = m_autoTestRootItem->childCount();
|
||||||
|
for (int row = 0; row < count; ++row) {
|
||||||
|
TestTreeItem *child = m_autoTestRootItem->child(row);
|
||||||
|
|
||||||
|
switch (child->checked()) {
|
||||||
|
case Qt::Unchecked:
|
||||||
|
continue;
|
||||||
|
case Qt::Checked:
|
||||||
|
tc = new TestConfiguration(child->name(), QStringList());
|
||||||
|
addProjectInformation(tc, child->filePath());
|
||||||
|
result << tc;
|
||||||
|
continue;
|
||||||
|
case Qt::PartiallyChecked:
|
||||||
|
default:
|
||||||
|
QString childName = child->name();
|
||||||
|
int grandChildCount = child->childCount();
|
||||||
|
QStringList testCases;
|
||||||
|
for (int grandChildRow = 0; grandChildRow < grandChildCount; ++grandChildRow) {
|
||||||
|
const TestTreeItem *grandChild = child->child(grandChildRow);
|
||||||
|
if (grandChild->checked() == Qt::Checked)
|
||||||
|
testCases << grandChild->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
tc = new TestConfiguration(childName, testCases);
|
||||||
|
addProjectInformation(tc, child->filePath());
|
||||||
|
result << tc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void TestTreeModel::modifyAutoTestSubtree(int row, TestTreeItem *newItem)
|
void TestTreeModel::modifyAutoTestSubtree(int row, TestTreeItem *newItem)
|
||||||
{
|
{
|
||||||
static QVector<int> modificationRoles = QVector<int>() << Qt::DisplayRole
|
static QVector<int> modificationRoles = QVector<int>() << Qt::DisplayRole
|
||||||
|
@@ -19,6 +19,8 @@
|
|||||||
#ifndef TESTTREEMODEL_H
|
#ifndef TESTTREEMODEL_H
|
||||||
#define TESTTREEMODEL_H
|
#define TESTTREEMODEL_H
|
||||||
|
|
||||||
|
#include "testconfiguration.h"
|
||||||
|
|
||||||
#include <cplusplus/CppDocument.h>
|
#include <cplusplus/CppDocument.h>
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
@@ -56,6 +58,8 @@ public:
|
|||||||
|
|
||||||
TestCodeParser *parser() const { return m_parser; }
|
TestCodeParser *parser() const { return m_parser; }
|
||||||
bool hasTests() const;
|
bool hasTests() const;
|
||||||
|
QList<TestConfiguration *> getAllTestCases() const;
|
||||||
|
QList<TestConfiguration *> getSelectedTests() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
@@ -18,12 +18,15 @@
|
|||||||
|
|
||||||
#include "autotestconstants.h"
|
#include "autotestconstants.h"
|
||||||
#include "testcodeparser.h"
|
#include "testcodeparser.h"
|
||||||
|
#include "testrunner.h"
|
||||||
#include "testtreeitem.h"
|
#include "testtreeitem.h"
|
||||||
#include "testtreemodel.h"
|
#include "testtreemodel.h"
|
||||||
#include "testtreeview.h"
|
#include "testtreeview.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include <coreplugin/find/itemviewfind.h>
|
||||||
|
|
||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
|
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
@@ -48,12 +51,11 @@ TestTreeViewWidget::TestTreeViewWidget(QWidget *parent) :
|
|||||||
QVBoxLayout *layout = new QVBoxLayout;
|
QVBoxLayout *layout = new QVBoxLayout;
|
||||||
layout->setMargin(0);
|
layout->setMargin(0);
|
||||||
layout->setSpacing(0);
|
layout->setSpacing(0);
|
||||||
layout->addWidget(m_view);
|
layout->addWidget(Core::ItemViewFind::createSearchableWrapper(m_view));
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
TestCodeParser *parser = m_model->parser();
|
TestCodeParser *parser = m_model->parser();
|
||||||
ProjectExplorer::SessionManager *sm = static_cast<ProjectExplorer::SessionManager *>(
|
ProjectExplorer::SessionManager *sm = ProjectExplorer::SessionManager::instance();
|
||||||
ProjectExplorer::SessionManager::instance());
|
|
||||||
connect(sm, &ProjectExplorer::SessionManager::startupProjectChanged,
|
connect(sm, &ProjectExplorer::SessionManager::startupProjectChanged,
|
||||||
parser, &TestCodeParser::updateTestTree);
|
parser, &TestCodeParser::updateTestTree);
|
||||||
|
|
||||||
@@ -78,6 +80,8 @@ void TestTreeViewWidget::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
// TODO remove?
|
// TODO remove?
|
||||||
QAction *rescan = new QAction(tr("Rescan"), &menu);
|
QAction *rescan = new QAction(tr("Rescan"), &menu);
|
||||||
|
|
||||||
|
connect(runAll, &QAction::triggered, this, &TestTreeViewWidget::onRunAllTriggered);
|
||||||
|
connect(runSelected, &QAction::triggered, this, &TestTreeViewWidget::onRunSelectedTriggered);
|
||||||
connect(selectAll, &QAction::triggered, m_view, &TestTreeView::selectAll);
|
connect(selectAll, &QAction::triggered, m_view, &TestTreeView::selectAll);
|
||||||
connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll);
|
connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll);
|
||||||
connect(rescan, &QAction::triggered,
|
connect(rescan, &QAction::triggered,
|
||||||
@@ -103,11 +107,10 @@ QList<QToolButton *> TestTreeViewWidget::createToolButtons()
|
|||||||
{
|
{
|
||||||
QList<QToolButton *> list;
|
QList<QToolButton *> list;
|
||||||
|
|
||||||
|
m_sortAlphabetically = true;
|
||||||
m_sort = new QToolButton(this);
|
m_sort = new QToolButton(this);
|
||||||
m_sort->setIcon((QIcon(QLatin1String(":/images/sort.png"))));
|
m_sort->setIcon((QIcon(QLatin1String(":/images/leafsort.png"))));
|
||||||
m_sort->setToolTip(tr("Sort Alphabetically (not implemented yet)")); // TODO
|
m_sort->setToolTip(tr("Sort Naturally (not implemented yet)"));
|
||||||
m_sort->setCheckable(true);
|
|
||||||
m_sort->setChecked(true);
|
|
||||||
|
|
||||||
QToolButton *expand = new QToolButton(this);
|
QToolButton *expand = new QToolButton(this);
|
||||||
expand->setIcon(QIcon(QLatin1String(":/images/expand.png")));
|
expand->setIcon(QIcon(QLatin1String(":/images/expand.png")));
|
||||||
@@ -119,7 +122,7 @@ QList<QToolButton *> TestTreeViewWidget::createToolButtons()
|
|||||||
|
|
||||||
connect(expand, &QToolButton::clicked, m_view, &TestTreeView::expandAll);
|
connect(expand, &QToolButton::clicked, m_view, &TestTreeView::expandAll);
|
||||||
connect(collapse, &QToolButton::clicked, m_view, &TestTreeView::collapseAll);
|
connect(collapse, &QToolButton::clicked, m_view, &TestTreeView::collapseAll);
|
||||||
// connect(m_sort, &QToolButton::toggled, m_view, &TestTreeView::onSortToggled); // TODO
|
connect(m_sort, &QToolButton::clicked, this, &TestTreeViewWidget::onSortClicked);
|
||||||
|
|
||||||
list << m_sort << expand << collapse;
|
list << m_sort << expand << collapse;
|
||||||
return list;
|
return list;
|
||||||
@@ -136,6 +139,33 @@ void TestTreeViewWidget::onItemActivated(const QModelIndex &index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestTreeViewWidget::onRunAllTriggered()
|
||||||
|
{
|
||||||
|
TestRunner *runner = TestRunner::instance();
|
||||||
|
runner->setSelectedTests(m_model->getAllTestCases());
|
||||||
|
runner->runTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTreeViewWidget::onRunSelectedTriggered()
|
||||||
|
{
|
||||||
|
TestRunner *runner = TestRunner::instance();
|
||||||
|
runner->setSelectedTests(m_model->getSelectedTests());
|
||||||
|
runner->runTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTreeViewWidget::onSortClicked()
|
||||||
|
{
|
||||||
|
if (m_sortAlphabetically) {
|
||||||
|
m_sort->setIcon((QIcon(QLatin1String(":/images/sort.png"))));
|
||||||
|
m_sort->setToolTip(tr("Sort Alphabetically"));
|
||||||
|
} else {
|
||||||
|
m_sort->setIcon((QIcon(QLatin1String(":/images/leafsort.png"))));
|
||||||
|
m_sort->setToolTip(tr("Sort Naturally (not implemented yet)"));
|
||||||
|
}
|
||||||
|
// TODO trigger the sorting change..
|
||||||
|
m_sortAlphabetically = !m_sortAlphabetically;
|
||||||
|
}
|
||||||
|
|
||||||
TestViewFactory::TestViewFactory()
|
TestViewFactory::TestViewFactory()
|
||||||
{
|
{
|
||||||
setDisplayName(tr("Tests"));
|
setDisplayName(tr("Tests"));
|
||||||
@@ -145,10 +175,10 @@ TestViewFactory::TestViewFactory()
|
|||||||
|
|
||||||
Core::NavigationView TestViewFactory::createWidget()
|
Core::NavigationView TestViewFactory::createWidget()
|
||||||
{
|
{
|
||||||
TestTreeViewWidget *treeView = new TestTreeViewWidget;
|
TestTreeViewWidget *treeViewWidget = new TestTreeViewWidget;
|
||||||
Core::NavigationView view;
|
Core::NavigationView view;
|
||||||
view.widget = treeView;
|
view.widget = treeViewWidget;
|
||||||
view.dockToolBarWidgets = treeView->createToolButtons();
|
view.dockToolBarWidgets = treeViewWidget->createToolButtons();
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -68,11 +68,15 @@ public slots:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onItemActivated(const QModelIndex &index);
|
void onItemActivated(const QModelIndex &index);
|
||||||
|
void onRunAllTriggered();
|
||||||
|
void onRunSelectedTriggered();
|
||||||
|
void onSortClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TestTreeModel *m_model;
|
TestTreeModel *m_model;
|
||||||
TestTreeView *m_view;
|
TestTreeView *m_view;
|
||||||
QToolButton *m_sort;
|
QToolButton *m_sort;
|
||||||
|
bool m_sortAlphabetically;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|