Initial commit

This commit is contained in:
Christian Stenger
2014-10-07 12:30:54 +02:00
committed by Christian Stenger
commit 2f4139e5d3
37 changed files with 1978 additions and 0 deletions

6
autotest.pro Normal file
View File

@@ -0,0 +1,6 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += plugins/autotest shared
QMAKE_EXTRA_TARGETS = docs install_docs # dummy targets for consistency

View File

@@ -0,0 +1,15 @@
{
\"Name\" : \"AutoTest\",
\"Version\" : \"$$QTCREATOR_VERSION\",
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
\"Experimental\" : true,
\"Vendor\" : \"Digia Plc\",
\"Copyright\" : \"(C) 2014 Digia Plc\",
\"License\" : [ \"Commercial Usage\",
\"\",
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Digia.\"
],
\"Description\" : \"Auto Test plugin.\",
\"Url\" : \"http://qt.digia.com\",
$$dependencyList
}

View File

@@ -0,0 +1,33 @@
TARGET = AutoTest
TEMPLATE = lib
PROVIDER = Digia
include(../../qtcreatorplugin.pri)
include(autotest_dependencies.pri)
DEFINES += AUTOTEST_LIBRARY
SOURCES += \
testtreeview.cpp \
testtreemodel.cpp \
testtreeitem.cpp \
testvisitor.cpp \
testinfo.cpp \
testcodeparser.cpp \
autotestplugin.cpp
HEADERS += \
testtreeview.h \
testtreemodel.h \
testtreeitem.h \
testvisitor.h \
testinfo.h \
testcodeparser.h \
autotestplugin.h \
autotest_global.h \
autotestconstants.h
RESOURCES += \
autotest.qrc

View File

@@ -0,0 +1,9 @@
<RCC>
<qresource prefix="/">
<file>images/class.png</file>
<file>images/func.png</file>
<file>images/expand.png</file>
<file>images/collapse.png</file>
<file>images/sort.png</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,12 @@
QTC_PLUGIN_NAME = AutoTest
QTC_PLUGIN_DEPENDS += \
coreplugin \
projectexplorer \
cpptools
QTC_LIB_DEPENDS += \
cplusplus \
utils
#QTC_PLUGIN_RECOMMENDS += \

View File

@@ -0,0 +1,31 @@
/****************************************************************************
**
** 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 AUTOTEST_GLOBAL_H
#define AUTOTEST_GLOBAL_H
#include <QtGlobal>
#if defined(AUTOTEST_LIBRARY)
# define AUTOTESTSHARED_EXPORT Q_DECL_EXPORT
#else
# define AUTOTESTSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // AUTOTEST_GLOBAL_H

View File

@@ -0,0 +1,34 @@
/****************************************************************************
**
** 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 AUTOTESTCONSTANTS_H
#define AUTOTESTCONSTANTS_H
namespace Autotest {
namespace Constants {
const char ACTION_ID[] = "AutoTest.Action";
const char MENU_ID[] = "AutoTest.Menu";
const char AUTOTEST_ID[] = "AutoTest.ATP";
const char AUTOTEST_CONTEXT[] = "Auto Tests";
} // namespace Autotest
} // namespace Constants
#endif // AUTOTESTCONSTANTS_H

View File

@@ -0,0 +1,102 @@
/****************************************************************************
**
** 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 "autotestplugin.h"
#include "autotestconstants.h"
#include "testtreeview.h"
#include "testtreemodel.h"
#include <coreplugin/icore.h>
#include <coreplugin/icontext.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/coreconstants.h>
#include <QAction>
#include <QMessageBox>
#include <QMainWindow>
#include <QMenu>
#include <QtPlugin>
using namespace Autotest::Internal;
AutotestPlugin::AutotestPlugin()
{
// Create your members
}
AutotestPlugin::~AutotestPlugin()
{
// Unregister objects from the plugin manager's object pool
// Delete members
TestTreeModel *model = TestTreeModel::instance();
delete model;
}
bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorString)
{
// Register objects in the plugin manager's object pool
// Load settings
// Add actions to menus
// Connect to other plugins' signals
// In the initialize function, a plugin can be sure that the plugins it
// depends on have initialized their members.
Q_UNUSED(arguments)
Q_UNUSED(errorString)
QAction *action = new QAction(tr("Autotest action"), this);
Core::Command *cmd = Core::ActionManager::registerAction(action, Constants::ACTION_ID,
Core::Context(Core::Constants::C_GLOBAL));
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
connect(action, SIGNAL(triggered()), this, SLOT(triggerAction()));
Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::MENU_ID);
menu->menu()->setTitle(tr("Tests"));
menu->addAction(cmd);
Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
addAutoReleasedObject(new TestViewFactory);
return true;
}
void AutotestPlugin::extensionsInitialized()
{
// Retrieve objects from the plugin manager's object pool
// In the extensionsInitialized function, a plugin can be sure that all
// plugins that depend on it are completely initialized.
}
ExtensionSystem::IPlugin::ShutdownFlag AutotestPlugin::aboutToShutdown()
{
// Save settings
// Disconnect from signals that are not needed during shutdown
// Hide UI (if you add UI that is not in the main window directly)
return SynchronousShutdown;
}
void AutotestPlugin::triggerAction()
{
QMessageBox::information(Core::ICore::mainWindow(),
tr("Action triggered"),
tr("This is an action from Autotest."));
}

View File

@@ -0,0 +1,50 @@
/****************************************************************************
**
** 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 AUTOTESTPLUGIN_H
#define AUTOTESTPLUGIN_H
#include "autotest_global.h"
#include <extensionsystem/iplugin.h>
namespace Autotest {
namespace Internal {
class AutotestPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "AutoTest.json")
public:
AutotestPlugin();
~AutotestPlugin();
bool initialize(const QStringList &arguments, QString *errorString);
void extensionsInitialized();
ShutdownFlag aboutToShutdown();
private slots:
void triggerAction();
};
} // namespace Internal
} // namespace Autotest
#endif // AUTOTESTPLUGIN_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

View File

@@ -0,0 +1,261 @@
/****************************************************************************
**
** 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 "testcodeparser.h"
#include "testinfo.h"
#include "testtreeitem.h"
#include "testtreemodel.h"
#include "testvisitor.h"
#include <cplusplus/LookupContext.h>
#include <cplusplus/TypeOfExpression.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cppworkingcopy.h>
#include <projectexplorer/session.h>
namespace Autotest {
namespace Internal {
TestCodeParser::TestCodeParser(TestTreeModel *parent)
: QObject(parent),
m_model(parent),
m_currentProject(0)
{
}
void TestCodeParser::updateTestTree()
{
qDebug("updating TestTreeModel");
m_model->beginResetModel();
qDeleteAll(m_cppDocMap);
m_cppDocMap.clear();
QModelIndex autoTestRootIndex = m_model->index(0, 0);
TestTreeItem *autoTestRootItem = static_cast<TestTreeItem *>(autoTestRootIndex.internalPointer());
autoTestRootItem->removeChildren();
m_model->endResetModel();
ProjectExplorer::SessionManager *session = static_cast<ProjectExplorer::SessionManager *>(
ProjectExplorer::SessionManager::instance());
if (!session || !session->hasProjects())
return;
m_currentProject = session->startupProject();
if (!m_currentProject)
return;
scanForTests();
}
/****** scan for QTest related stuff helpers ******/
static bool includesQtTest(const CPlusPlus::Document::Ptr &doc,
const CppTools::CppModelManager *cppMM)
{
QList<CPlusPlus::Document::Include> includes = doc->resolvedIncludes();
foreach (CPlusPlus::Document::Include inc, includes) {
// TODO this short cut works only for #include <QtTest>
// bad, as there could be much more different approaches
if (inc.unresolvedFileName() == QLatin1String("QtTest")
&& inc.resolvedFileName().endsWith(QLatin1String("QtTest/QtTest"))) {
return true;
}
}
if (cppMM) {
CPlusPlus::Snapshot snapshot = cppMM->snapshot();
QSet<QString> includes = snapshot.allIncludesForDocument(doc->fileName());
foreach (QString include, includes) {
if (include.endsWith(QLatin1String("QtTest/qtest.h"))) {
return true;
}
}
}
return false;
}
static bool qtTestLibDefined(const CppTools::CppModelManager *cppMM,
const QString &fileName)
{
QList<CppTools::ProjectPart::Ptr> parts = cppMM->projectPart(fileName);
if (parts.size() > 0) {
QByteArray projDefines = parts.at(0)->projectDefines;
return projDefines.contains("#define QT_TESTLIB_LIB 1");
}
return false;
}
static QString testClass(const CPlusPlus::Document::Ptr &doc)
{
static QByteArray qtTestMacros[] = {"QTEST_MAIN", "QTEST_APPLESS_MAIN", "QTEST_GUILESS_MAIN"};
QString tC;
QList<CPlusPlus::Document::MacroUse> macros = doc->macroUses();
foreach (CPlusPlus::Document::MacroUse macro, macros) {
if (!macro.isFunctionLike())
continue;
QByteArray name = macro.macro().name();
if (name == qtTestMacros[0] || name == qtTestMacros[1] || name == qtTestMacros[2]) {
CPlusPlus::Document::Block arg = macro.arguments().at(0);
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
// TODO high costs....?!
CppTools::WorkingCopy wc = cppMM->workingCopy();
if (wc.contains(doc->fileName())) {
QByteArray src = wc.source(doc->fileName());
tC = QLatin1String(src.mid(arg.bytesBegin(), arg.bytesEnd() - arg.bytesBegin()));
} else {
QFile current(doc->fileName());
if (current.exists() && current.open(QFile::ReadOnly)) {
CPlusPlus::Document::Block arg = macro.arguments().at(0);
if (current.seek(arg.bytesBegin())) {
QByteArray res = current.read(arg.bytesEnd() - arg.bytesBegin());
if (!res.isEmpty())
tC = QLatin1String(res);
}
current.close();
}
}
break;
}
}
return tC;
}
/****** end of helpers ******/
void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr doc)
{
const QString file = doc->fileName();
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
if (includesQtTest(doc, cppMM) && qtTestLibDefined(cppMM, file)) {
QString tc = testClass(doc);
if (tc.isEmpty()) {
// one might have used an approach without macros or defined own macros
QString src = QLatin1String(doc->utf8Source()); // does not work anymore - src is always ""
if (src.contains(QLatin1String("QTest::qExec"))) {
qDebug() << "Src\n===============\n" << src << "\n=============\n";
// TODO extract the class name by using the AST - using regex is too problematic as this
// does not take comments and typedefs or whatever into account....
qDebug() << "Currently not supported approach... (self-defined macro / QTest::qExec() call/...)";
}
} else {
QModelIndex autoTestRootIndex = m_model->index(0, 0);
TestTreeItem *autoTestRootItem = static_cast<TestTreeItem *>(autoTestRootIndex.internalPointer());
TestTreeItem *ttItem = new TestTreeItem(tc, file, TestTreeItem::TEST_CLASS,
autoTestRootItem);
CPlusPlus::TypeOfExpression toe;
toe.init(doc, cppMM->snapshot());
CPlusPlus::Document::Ptr declaringDoc = doc;
QList<CPlusPlus::LookupItem> toeItems = toe(tc.toUtf8(), doc->globalNamespace());
if (toeItems.size()) {
CPlusPlus::Class *toeClass = toeItems.first().declaration()->asClass();
QString declFileName = QLatin1String(toeClass->fileId()->chars(),
toeClass->fileId()->size());
declaringDoc = cppMM->snapshot().document(declFileName);
ttItem->setFilePath(declFileName);
ttItem->setLine(toeClass->line());
ttItem->setColumn(toeClass->column() - 1);
}
if (declaringDoc.isNull())
return;
TestVisitor myVisitor(tc);
myVisitor.accept(declaringDoc->globalNamespace());
QMap<QString, TestCodeLocation> privSlots = myVisitor.privateSlots();
foreach (const QString privS, privSlots.keys()) {
TestCodeLocation location = privSlots.value(privS);
TestTreeItem *ttSub = new TestTreeItem(privS, location.m_fileName,
TestTreeItem::TEST_FUNCTION, ttItem);
ttSub->setLine(location.m_line);
ttSub->setColumn(location.m_column);
ttItem->appendChild(ttSub);
}
if (m_cppDocMap.contains(file)) {
TestInfo *info = m_cppDocMap[file];
int count = autoTestRootItem->childCount();
for (int i = 0; i < count; ++i) {
TestTreeItem *currentItem = autoTestRootItem->child(i);
if (currentItem->filePath() == file) {
m_model->modifyAutoTestSubtree(i, ttItem);
m_cppDocMap.insert(file, new TestInfo(tc, privSlots.keys(),
doc->revision(),
doc->editorRevision()));
break;
}
}
delete info;
delete ttItem;
} else {
m_model->beginInsertRows(autoTestRootIndex, autoTestRootItem->childCount(), autoTestRootItem->childCount());
autoTestRootItem->appendChild(ttItem);
m_model->endInsertRows();
m_cppDocMap.insert(file, new TestInfo(tc, privSlots.keys(),
doc->revision(), doc->editorRevision()));
}
}
}
}
void TestCodeParser::onDocumentUpdated(CPlusPlus::Document::Ptr doc)
{
if (!m_currentProject)
return;
QString fileName = doc->fileName();
if (!m_cppDocMap.contains(fileName)) {
if (!m_currentProject->files(ProjectExplorer::Project::AllFiles).contains(fileName)) {
return;
}
} else {
if (m_cppDocMap[fileName]->revision() == doc->revision()
&& m_cppDocMap[fileName]->editorRevision() == doc->editorRevision()) {
qDebug("Skipped due revision equality"); // added to verify if this ever happens..
return;
}
}
checkDocumentForTestCode(doc);
}
void TestCodeParser::removeFiles(const QStringList &files)
{
foreach (QString file, files) {
if (m_cppDocMap.contains(file)) {
TestInfo *info = m_cppDocMap.value(file);
m_cppDocMap.remove(file);
m_model->removeAutoTestSubtreeByFilePath(file);
delete info;
}
}
}
void TestCodeParser::scanForTests()
{
QStringList list = m_currentProject->files(ProjectExplorer::Project::AllFiles);
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
CPlusPlus::Snapshot snapshot = cppMM->snapshot();
foreach (QString file, list) {
if (snapshot.contains(file)) {
CPlusPlus::Document::Ptr doc = snapshot.find(file).value();
checkDocumentForTestCode(doc);
}
}
}
} // namespace Internal
} // namespace Autotest

View File

@@ -0,0 +1,63 @@
/****************************************************************************
**
** 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 TESTCODEPARSER_H
#define TESTCODEPARSER_H
#include <cplusplus/CppDocument.h>
#include <QObject>
#include <QMap>
namespace ProjectExplorer {
class Project;
}
namespace Autotest {
namespace Internal {
class TestInfo;
class TestTreeModel;
class TestCodeParser : public QObject
{
Q_OBJECT
public:
explicit TestCodeParser(/*QObject*/TestTreeModel *parent = 0);
signals:
public slots:
void updateTestTree();
void checkDocumentForTestCode(CPlusPlus::Document::Ptr doc);
void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
void removeFiles(const QStringList &files);
private:
void scanForTests();
TestTreeModel *m_model;
QMap<QString, TestInfo*> m_cppDocMap;
ProjectExplorer::Project *m_currentProject;
};
} // namespace Internal
} // Autotest
#endif // TESTCODEPARSER_H

View File

@@ -0,0 +1,34 @@
/****************************************************************************
**
** 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 "testinfo.h"
namespace Autotest {
namespace Internal {
TestInfo::TestInfo(const QString &className, const QStringList &functions, unsigned revision,
unsigned editorRevision)
: m_className(className),
m_functions(functions),
m_revision(revision),
m_editorRevision(editorRevision)
{
}
} // namespace Internal
} // namespace Autotest

View File

@@ -0,0 +1,52 @@
/****************************************************************************
**
** 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 TESTINFO_H
#define TESTINFO_H
#include <QStringList>
namespace Autotest {
namespace Internal {
class TestInfo
{
public:
explicit TestInfo(const QString &className, const QStringList &functions = QStringList(),
unsigned revision = 0, unsigned editorRevision = 0);
const QString testClass() const { return m_className; }
void setTestClass(const QString &className) { m_className = className; }
const QStringList testFunctions() const { return m_functions; }
void setTestFunctions(const QStringList &functions) { m_functions = functions; }
unsigned revision() const { return m_revision; }
void setRevision(unsigned revision) { m_revision = revision; }
unsigned editorRevision() const { return m_editorRevision; }
void setEditorRevision(unsigned editorRevision) { m_editorRevision = editorRevision; }
private:
QString m_className;
QStringList m_functions;
unsigned m_revision;
unsigned m_editorRevision;
};
} // namespace Internal
} // namespace Autotest
#endif // TESTINFO_H

View File

@@ -0,0 +1,136 @@
/****************************************************************************
**
** 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 "testtreeitem.h"
namespace Autotest {
namespace Internal {
TestTreeItem::TestTreeItem(const QString &name, const QString &filePath, Type type, TestTreeItem *parent)
: m_name(name),
m_filePath(filePath),
m_checked(type == ROOT ? Qt::Unchecked : Qt::Checked),
m_type(type),
m_line(0),
m_parent(parent)
{
}
TestTreeItem::~TestTreeItem()
{
removeChildren();
}
TestTreeItem *TestTreeItem::child(int row)
{
return m_children.at(row);
}
TestTreeItem *TestTreeItem::parent() const
{
return m_parent;
}
void TestTreeItem::appendChild(TestTreeItem *child)
{
m_children.append(child);
}
int TestTreeItem::row() const
{
if (m_parent)
return m_parent->m_children.indexOf(const_cast<TestTreeItem *>(this));
return 0;
}
int TestTreeItem::childCount() const
{
return m_children.size();
}
void TestTreeItem::removeChildren()
{
qDeleteAll(m_children);
m_children.clear();
}
bool TestTreeItem::removeChild(int row)
{
if (row < 0 || row >= m_children.size())
return false;
TestTreeItem *child = m_children.at(row);
m_children.removeAt(row);
delete child;
return true;
}
bool TestTreeItem::modifyContent(const TestTreeItem *modified)
{
bool hasBeenModified = false;
if (m_filePath != modified->m_filePath) {
m_filePath = modified->m_filePath;
hasBeenModified = true;
}
if (m_name != modified->m_name) {
m_name = modified->m_name;
hasBeenModified = true;
}
if (m_line != modified->m_line) {
m_line = modified->m_line;
hasBeenModified = true;
}
return hasBeenModified;
}
void TestTreeItem::setChecked(const Qt::CheckState checkState)
{
switch (m_type) {
case ROOT:
return;
case TEST_FUNCTION:
m_checked = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked);
m_parent->revalidateCheckState();
break;
case TEST_CLASS:
Qt::CheckState usedState = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked);
foreach (TestTreeItem *child, m_children) {
child->setChecked(usedState);
}
m_checked = usedState;
}
}
void TestTreeItem::revalidateCheckState()
{
if (m_children.size() == 0)
return;
bool foundChecked = false;
bool foundUnchecked = false;
foreach (const TestTreeItem *child, m_children) {
foundChecked |= (child->m_checked != Qt::Unchecked);
foundUnchecked |= (child->m_checked == Qt::Unchecked);
if (foundChecked && foundUnchecked) {
m_checked = Qt::PartiallyChecked;
return;
}
}
m_checked = (foundUnchecked ? Qt::Unchecked : Qt::Checked);
}
} // namespace Internal
} // namespace Autotest

View File

@@ -0,0 +1,76 @@
/****************************************************************************
**
** 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 TESTTREEITEM_H
#define TESTTREEITEM_H
#include <QList>
#include <QString>
namespace Autotest {
namespace Internal {
class TestTreeItem
{
public:
enum Type {
ROOT, TEST_CLASS, TEST_FUNCTION
};
TestTreeItem(const QString &name, const QString &filePath, Type type, TestTreeItem *parent = 0);
virtual ~TestTreeItem();
TestTreeItem *child(int row);
TestTreeItem *parent() const;
void appendChild(TestTreeItem *child);
int row() const;
int childCount() const;
void removeChildren();
bool removeChild(int row);
bool modifyContent(const TestTreeItem *modified);
const QString name() const { return m_name; }
void setName(const QString &name) { m_name = name; }
const QString filePath() const { return m_filePath; }
void setFilePath(const QString &filePath) { m_filePath = filePath; }
void setLine(unsigned line) { m_line = line;}
unsigned line() const { return m_line; }
void setColumn(unsigned column) { m_column = column; }
unsigned column() const { return m_column; }
void setChecked(const Qt::CheckState checked);
Qt::CheckState checked() const { return m_checked; }
Type type() const { return m_type; }
private:
void revalidateCheckState();
QString m_name;
QString m_filePath;
Qt::CheckState m_checked;
Type m_type;
unsigned m_line;
unsigned m_column;
TestTreeItem *m_parent;
QList<TestTreeItem *> m_children;
};
} // namespace Internal
} // namespace Autotest
#endif // TESTTREEITEM_H

View File

@@ -0,0 +1,329 @@
/****************************************************************************
**
** 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 "testcodeparser.h"
#include "testtreeitem.h"
#include "testtreemodel.h"
#include <texteditor/texteditor.h>
#include <QIcon>
namespace Autotest {
namespace Internal {
TestTreeModel::TestTreeModel(QObject *parent) :
QAbstractItemModel(parent),
m_rootItem(new TestTreeItem(QString(), QString(), TestTreeItem::ROOT)),
m_autoTestRootItem(new TestTreeItem(tr("Auto Tests"), QString(), TestTreeItem::ROOT, m_rootItem)),
// m_quickTestRootItem(new TestTreeItem(tr("Qt Quick Tests"), QString(), TestTreeItem::ROOT, m_rootItem)),
m_parser(new TestCodeParser(this))
{
m_rootItem->appendChild(m_autoTestRootItem);
// m_rootItem->appendChild(m_quickTestRootItem);
m_parser->updateTestTree();
// CppTools::CppModelManagerInterface *cppMM = CppTools::CppModelManagerInterface::instance();
// if (cppMM) {
// // replace later on by
// // cppMM->registerAstProcessor([this](const CplusPlus::Document::Ptr &doc,
// // const CPlusPlus::Snapshot &snapshot) {
// // checkForQtTestStuff(doc, snapshot);
// // });
// connect(cppMM, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
// this, SLOT(checkForQtTestStuff(CPlusPlus::Document::Ptr)),
// Qt::DirectConnection);
// }
}
static TestTreeModel *m_instance = 0;
TestTreeModel *TestTreeModel::instance()
{
if (!m_instance)
m_instance = new TestTreeModel;
return m_instance;
}
TestTreeModel::~TestTreeModel()
{
delete m_rootItem;
m_instance = 0;
}
QModelIndex TestTreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
TestTreeItem *parentItem = parent.isValid()
? static_cast<TestTreeItem *>(parent.internalPointer())
: m_rootItem;
TestTreeItem *childItem = parentItem->child(row);
return childItem ? createIndex(row, column, childItem) : QModelIndex();
}
QModelIndex TestTreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TestTreeItem *childItem = static_cast<TestTreeItem *>(index.internalPointer());
TestTreeItem *parentItem = childItem->parent();
if (parentItem == m_rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
bool TestTreeModel::hasChildren(const QModelIndex &parent) const
{
if (!parent.isValid())
return true;
TestTreeItem *item = static_cast<TestTreeItem *>(parent.internalPointer());
return item->childCount() > 0;
}
int TestTreeModel::rowCount(const QModelIndex &parent) const
{
TestTreeItem *parentItem = parent.isValid()
? static_cast<TestTreeItem *>(parent.internalPointer()) : m_rootItem;
return parentItem->childCount();
}
int TestTreeModel::columnCount(const QModelIndex &) const
{
return 1;
}
QVariant TestTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
TestTreeItem *item = static_cast<TestTreeItem *>(index.internalPointer());
if (!item)
return QVariant();
if (role == Qt::DisplayRole) {
if ((item == m_autoTestRootItem && m_autoTestRootItem->childCount() == 0)
/*|| (item == m_quickTestRootItem && m_quickTestRootItem->childCount() == 0)*/) {
return QString(item->name() + tr(" (none)"));
} else {
return item->name();
}
return QVariant(); // TODO ?
}
switch(role) {
case Qt::ToolTipRole:
return item->filePath();
case Qt::DecorationRole:
switch(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:
if (item->type() == TestTreeItem::ROOT)
return QVariant();
return item->checked();
case LinkRole:
QVariant itemLink;
TextEditor::TextEditorWidget::Link link(item->filePath(), item->line(), item->column());
itemLink.setValue(link);
return itemLink;
}
// TODO ?
return QVariant();
}
bool TestTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
if (role == Qt::CheckStateRole) {
TestTreeItem *item = static_cast<TestTreeItem *>(index.internalPointer());
Qt::CheckState old = item->checked();
item->setChecked((Qt::CheckState)value.toInt());
if (item->checked() != old) {
switch(item->type()) {
case TestTreeItem::TEST_CLASS:
emit dataChanged(index, index);
if (item->childCount() > 0) {
emit dataChanged(index.child(0, 0), index.child(item->childCount() - 1, 0));
}
break;
case TestTreeItem::TEST_FUNCTION:
emit dataChanged(index, index);
emit dataChanged(index.parent(), index.parent());
break;
default: // avoid warning regarding unhandled enum member
break;
}
return true;
}
}
return false;
}
Qt::ItemFlags TestTreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
TestTreeItem *item = static_cast<TestTreeItem *>(index.internalPointer());
switch(item->type()) {
case TestTreeItem::TEST_CLASS:
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsTristate | Qt::ItemIsUserCheckable;
case TestTreeItem::TEST_FUNCTION:
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
case TestTreeItem::ROOT:
default:
return Qt::ItemIsEnabled;
}
}
bool TestTreeModel::removeRows(int row, int count, const QModelIndex &parent)
{
if (!parent.isValid())
return false;
TestTreeItem *parentItem = static_cast<TestTreeItem *>(parent.internalPointer());
if (!parentItem)
return false;
bool subItemsSuccess = true;
bool itemSuccess = true;
for (int i = row + count - 1; i >= row; --i) {
QModelIndex child = index(i, 0, parent);
subItemsSuccess &= removeRows(0, rowCount(child), child);
beginRemoveRows(parent, i, i);
itemSuccess &= parentItem->removeChild(i);
endRemoveRows();
}
return subItemsSuccess && itemSuccess;
}
bool TestTreeModel::hasTests() const
{
return m_autoTestRootItem->childCount() > 0 /*|| m_quickTestRootItem->childCount() > 0*/;
}
void TestTreeModel::modifyAutoTestSubtree(int row, TestTreeItem *newItem)
{
static QVector<int> modificationRoles = QVector<int>() << Qt::DisplayRole
<< Qt::ToolTipRole << LinkRole;
QModelIndex toBeModifiedIndex = index(0, 0).child(row, 0);
if (!toBeModifiedIndex.isValid())
return;
TestTreeItem *toBeModifiedItem = static_cast<TestTreeItem *>(toBeModifiedIndex.internalPointer());
if (toBeModifiedItem->modifyContent(newItem))
emit dataChanged(toBeModifiedIndex, toBeModifiedIndex, modificationRoles);
// process sub-items as well...
int childCount = toBeModifiedItem->childCount();
int newChildCount = newItem->childCount();
// for keeping the CheckState on modifications
QHash<QString, Qt::CheckState> originalItems;
for (int row = 0; row < childCount; ++row) {
const TestTreeItem *child = toBeModifiedItem->child(row);
originalItems.insert(child->name(), child->checked());
}
if (childCount <= newChildCount) {
for (int row = 0; row < childCount; ++row) {
QModelIndex child = toBeModifiedIndex.child(row, 0);
TestTreeItem *toBeModifiedChild = toBeModifiedItem->child(row);
TestTreeItem *modifiedChild = newItem->child(row);
if (toBeModifiedChild->modifyContent(modifiedChild)) {
emit dataChanged(child, child, modificationRoles);
}
if (originalItems.contains(toBeModifiedChild->name())) {
Qt::CheckState state = originalItems.value(toBeModifiedChild->name());
if (state != toBeModifiedChild->checked()) {
toBeModifiedChild->setChecked(state);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
} else { // newly added (BAD: happens for renaming as well)
toBeModifiedChild->setChecked(Qt::Checked);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
}
if (childCount < newChildCount) { // add aditional items
for (int row = childCount; row < newChildCount; ++row) {
TestTreeItem *newChild = newItem->child(row);
TestTreeItem *toBeAdded = new TestTreeItem(newChild->name(), newChild->filePath(),
newChild->type(), toBeModifiedItem);
toBeAdded->setLine(newChild->line());
beginInsertRows(toBeModifiedIndex, row, row);
toBeModifiedItem->appendChild(toBeAdded);
endInsertRows();
if (originalItems.contains(toBeAdded->name())
&& originalItems.value(toBeAdded->name()) != Qt::Checked)
toBeAdded->setChecked(originalItems.value(toBeAdded->name()));
}
}
} else {
for (int row = 0; row < newChildCount; ++row) {
QModelIndex child = toBeModifiedIndex.child(row, 0);
TestTreeItem *toBeModifiedChild = toBeModifiedItem->child(row);
TestTreeItem *modifiedChild = newItem->child(row);
if (toBeModifiedChild->modifyContent(modifiedChild))
emit dataChanged(child, child, modificationRoles);
if (originalItems.contains(toBeModifiedChild->name())) {
Qt::CheckState state = originalItems.value(toBeModifiedChild->name());
if (state != toBeModifiedChild->checked()) {
toBeModifiedChild->setChecked(state);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
} else { // newly added (BAD: happens for renaming as well)
toBeModifiedChild->setChecked(Qt::Checked);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
} // remove rest of the items
removeRows(newChildCount, childCount - newChildCount, toBeModifiedIndex);
}
}
void TestTreeModel::removeAutoTestSubtreeByFilePath(const QString &file)
{
QModelIndex atRootIndex = index(0, 0);
int count = rowCount(atRootIndex);
for (int row = 0; row < count; ++row) {
QModelIndex childIndex = atRootIndex.child(row, 0);
TestTreeItem *childItem = static_cast<TestTreeItem *>(childIndex.internalPointer());
if (file == childItem->filePath()) {
removeRow(row, atRootIndex);
break;
}
}
}
} // namespace Internal
} // namespace Autotest

View File

@@ -0,0 +1,80 @@
/****************************************************************************
**
** 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 TESTTREEMODEL_H
#define TESTTREEMODEL_H
#include <cplusplus/CppDocument.h>
#include <QAbstractItemModel>
namespace {
enum ItemRole {
// AnnotationRole = Qt::UserRole + 1,
LinkRole = Qt::UserRole + 2 // can be removed if AnnotationRole comes back
};
}
namespace Autotest {
namespace Internal {
class TestCodeParser;
class TestInfo;
class TestTreeItem;
class TestTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
static TestTreeModel* instance();
~TestTreeModel();
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
bool hasChildren(const QModelIndex &parent) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
Qt::ItemFlags flags(const QModelIndex &index) const;
bool removeRows(int row, int count, const QModelIndex &parent);
TestCodeParser *parser() const { return m_parser; }
bool hasTests() const;
signals:
public slots:
private:
void modifyAutoTestSubtree(int row, TestTreeItem *newItem);
void removeAutoTestSubtreeByFilePath(const QString &file);
explicit TestTreeModel(QObject *parent = 0);
TestTreeItem *m_rootItem;
TestTreeItem *m_autoTestRootItem;
// TestTreeItem *m_quickTestRootItem;
TestCodeParser *m_parser;
friend class TestCodeParser;
};
} // namespace Internal
} // namespace Autotest
#endif // TESTTREEMODEL_H

View File

@@ -0,0 +1,204 @@
/****************************************************************************
**
** 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 "autotestconstants.h"
#include "testcodeparser.h"
#include "testtreeitem.h"
#include "testtreemodel.h"
#include "testtreeview.h"
#include <coreplugin/icore.h>
#include <cpptools/cppmodelmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <texteditor/texteditor.h>
#include <QToolButton>
#include <QVBoxLayout>
namespace Autotest {
namespace Internal {
TestTreeViewWidget::TestTreeViewWidget(QWidget *parent) :
QWidget(parent)
{
setWindowTitle(tr("Tests"));
m_model = TestTreeModel::instance();
m_view = new TestTreeView(this);
m_view->setModel(m_model);
QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(0);
layout->setSpacing(0);
layout->addWidget(m_view);
setLayout(layout);
TestCodeParser *parser = m_model->parser();
ProjectExplorer::SessionManager *sm = static_cast<ProjectExplorer::SessionManager *>(
ProjectExplorer::SessionManager::instance());
connect(sm, &ProjectExplorer::SessionManager::startupProjectChanged,
parser, &TestCodeParser::updateTestTree);
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
connect(cppMM, &CppTools::CppModelManager::documentUpdated,
parser, &TestCodeParser::onDocumentUpdated, Qt::QueuedConnection);
connect(cppMM, &CppTools::CppModelManager::aboutToRemoveFiles,
parser, &TestCodeParser::removeFiles, Qt::QueuedConnection);
connect(m_view, &TestTreeView::activated, this, &TestTreeViewWidget::onItemActivated);
}
void TestTreeViewWidget::contextMenuEvent(QContextMenuEvent *event)
{
bool hasTests = m_model->hasTests();
QMenu menu;
QAction *runAll = new QAction(tr("Run All Tests"), &menu);
QAction *runSelected = new QAction(tr("Run Selected Tests"), &menu);
QAction *selectAll = new QAction(tr("Select All"), &menu);
QAction *deselectAll = new QAction(tr("Deselect All"), &menu);
// TODO remove?
QAction *rescan = new QAction(tr("Rescan"), &menu);
connect(selectAll, &QAction::triggered, m_view, &TestTreeView::selectAll);
connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll);
connect(rescan, &QAction::triggered,
TestTreeModel::instance()->parser(), &TestCodeParser::updateTestTree);
runAll->setEnabled(hasTests);
runSelected->setEnabled(hasTests);
selectAll->setEnabled(hasTests);
deselectAll->setEnabled(hasTests);
menu.addAction(runAll);
menu.addAction(runSelected);
menu.addSeparator();
menu.addAction(selectAll);
menu.addAction(deselectAll);
menu.addSeparator();
menu.addAction(rescan);
menu.exec(mapToGlobal(event->pos()));
}
QList<QToolButton *> TestTreeViewWidget::createToolButtons()
{
QList<QToolButton *> list;
m_sort = new QToolButton(this);
m_sort->setIcon((QIcon(QLatin1String(":/images/sort.png"))));
m_sort->setToolTip(tr("Sort Alphabetically (not implemented yet)")); // TODO
m_sort->setCheckable(true);
m_sort->setChecked(true);
QToolButton *expand = new QToolButton(this);
expand->setIcon(QIcon(QLatin1String(":/images/expand.png")));
expand->setToolTip(tr("Expand All"));
QToolButton *collapse = new QToolButton(this);
collapse->setIcon(QIcon(QLatin1String(":/images/collapse.png")));
collapse->setToolTip(tr("Collapse All"));
connect(expand, &QToolButton::clicked, m_view, &TestTreeView::expandAll);
connect(collapse, &QToolButton::clicked, m_view, &TestTreeView::collapseAll);
// connect(m_sort, &QToolButton::toggled, m_view, &TestTreeView::onSortToggled); // TODO
list << m_sort << expand << collapse;
return list;
}
void TestTreeViewWidget::onItemActivated(const QModelIndex &index)
{
const TextEditor::TextEditorWidget::Link link
= index.data(LinkRole).value<TextEditor::TextEditorWidget::Link>();
if (link.hasValidTarget()) {
Core::EditorManager::openEditorAt(link.targetFileName,
link.targetLine,
link.targetColumn);
}
}
TestViewFactory::TestViewFactory()
{
setDisplayName(tr("Tests"));
setId(Autotest::Constants::AUTOTEST_ID);
setPriority(666);
}
Core::NavigationView TestViewFactory::createWidget()
{
TestTreeViewWidget *treeView = new TestTreeViewWidget;
Core::NavigationView view;
view.widget = treeView;
view.dockToolBarWidgets = treeView->createToolButtons();
return view;
}
TestTreeView::TestTreeView(QWidget *parent)
: NavigationTreeView(parent),
m_context(new Core::IContext(this))
{
setExpandsOnDoubleClick(false);
m_context->setWidget(this);
m_context->setContext(Core::Context(Constants::AUTOTEST_CONTEXT));
Core::ICore::addContextObject(m_context);
}
void TestTreeView::selectAll()
{
selectOrDeselectAll(Qt::Checked);
}
void TestTreeView::deselectAll()
{
selectOrDeselectAll(Qt::Unchecked);
}
// this avoids the re-evaluation of parent nodes when modifying the child nodes (setData())
void TestTreeView::selectOrDeselectAll(const Qt::CheckState checkState)
{
TestTreeModel *model = TestTreeModel::instance();
QModelIndex autoTestsIndex = model->index(0, 0, rootIndex());
if (!autoTestsIndex.isValid())
return;
int count = model->rowCount(autoTestsIndex);
QModelIndex last;
for (int i = 0; i < count; ++i) {
QModelIndex classesIndex = model->index(i, 0, autoTestsIndex);
int funcCount = model->rowCount(classesIndex);
TestTreeItem *item = static_cast<TestTreeItem *>(classesIndex.internalPointer());
if (item) {
item->setChecked(checkState);
if (!item->childCount())
last = classesIndex;
}
for (int j = 0; j < funcCount; ++j) {
last = model->index(j, 0, classesIndex);
TestTreeItem *item = static_cast<TestTreeItem *>(last.internalPointer());
if (item)
item->setChecked(checkState);
}
}
emit dataChanged(autoTestsIndex, last);
}
} // namespace Internal
} // namespace Autotest

View File

@@ -0,0 +1,94 @@
/****************************************************************************
**
** 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 TESTTREEVIEW_H
#define TESTTREEVIEW_H
#include <coreplugin/inavigationwidgetfactory.h>
#include <utils/navigationtreeview.h>
QT_BEGIN_NAMESPACE
class QModelIndex;
class QToolButton;
QT_END_NAMESPACE
namespace Core {
class IContext;
}
namespace Autotest {
namespace Internal {
class TestTreeModel;
class TestTreeView : public Utils::NavigationTreeView
{
Q_OBJECT
public:
TestTreeView(QWidget *parent = 0);
void selectAll();
void deselectAll();
private:
void selectOrDeselectAll(const Qt::CheckState checkState);
Core::IContext *m_context;
};
class TestTreeViewWidget : public QWidget
{
Q_OBJECT
public:
explicit TestTreeViewWidget(QWidget *parent = 0);
void contextMenuEvent(QContextMenuEvent *event);
QList<QToolButton *> createToolButtons();
signals:
public slots:
private slots:
void onItemActivated(const QModelIndex &index);
private:
TestTreeModel *m_model;
TestTreeView *m_view;
QToolButton *m_sort;
};
class TestViewFactory : public Core::INavigationWidgetFactory
{
Q_OBJECT
public:
TestViewFactory();
private:
Core::NavigationView createWidget();
};
} // namespace Internal
} // namespace Autotest
#endif // TESTTREEVIEW_H

View File

@@ -0,0 +1,75 @@
/****************************************************************************
**
** 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 "testvisitor.h"
#include <cplusplus/LookupContext.h>
#include <cplusplus/Overview.h>
#include <cplusplus/Symbols.h>
#include <QList>
namespace Autotest {
namespace Internal {
TestVisitor::TestVisitor(const QString &fullQualifiedClassName)
: m_className(fullQualifiedClassName)
{
}
TestVisitor::~TestVisitor()
{
}
static QList<QString> ignoredFunctions = QList<QString>() << QLatin1String("initTestCase")
<< QLatin1String("cleanupTestCase")
<< QLatin1String("init")
<< QLatin1String("cleanup");
bool TestVisitor::visit(CPlusPlus::Class *symbol)
{
const CPlusPlus::Overview o;
CPlusPlus::LookupContext lc;
unsigned count = symbol->memberCount();
for (unsigned i = 0; i < count; ++i) {
CPlusPlus::Symbol *member = symbol->memberAt(i);
CPlusPlus::Type *type = member->type().type();
const QString className = o.prettyName(lc.fullyQualifiedName(member->enclosingClass()));
if (className != m_className)
continue;
if (auto func = type->asFunctionType()) {
if (func->isSlot() && member->isPrivate()) {
QString name = o.prettyName(func->name());
if (!ignoredFunctions.contains(name) && !name.endsWith(QLatin1String("_data"))) {
TestCodeLocation location;
location.m_fileName = QLatin1String(member->fileName());
location.m_line = member->line();
location.m_column = member->column() - 1;
m_privSlots.insert(name, location);
}
}
}
}
return true;
}
} // namespace Internal
} // namespace Autotest

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** 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 TESTVISITOR_H
#define TESTVISITOR_H
#include <cplusplus/SymbolVisitor.h>
#include <QMap>
#include <QString>
namespace Autotest {
namespace Internal {
struct TestCodeLocation {
QString m_fileName;
unsigned m_line;
unsigned m_column;
};
class TestVisitor : public CPlusPlus::SymbolVisitor
{
public:
TestVisitor(const QString &fullQualifiedClassName);
virtual ~TestVisitor();
QMap<QString, TestCodeLocation> privateSlots() const { return m_privSlots; }
bool visit(CPlusPlus::Class *symbol);
private:
QString m_className;
QMap<QString, TestCodeLocation> m_privSlots;
};
} // namespace Internal
} // namespace Autotest
#endif // TESTVISITOR_H

8
qtcreatorplugin.pri Normal file
View File

@@ -0,0 +1,8 @@
isEmpty(IDE_SOURCE_TREE): IDE_SOURCE_TREE=$$(QTC_SOURCE)
isEmpty(IDE_BUILD_TREE): IDE_BUILD_TREE=$$(QTC_BUILD)
isEmpty(IDE_SOURCE_TREE):error(Set QTC_SOURCE environment variable)
isEmpty(IDE_BUILD_TREE):error(Set QTC_BUILD environment variable)
INCLUDEPATH+= $$PWD/plugins
include($$IDE_SOURCE_TREE/src/qtcreatorplugin.pri)

3
shared/autotest/auto.pro Normal file
View File

@@ -0,0 +1,3 @@
TEMPLATE = subdirs
SUBDIRS += %TestCaseName:l%

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

18
shared/autotest/main.cpp Normal file
View File

@@ -0,0 +1,18 @@
@if "%RequireGUI%" == "true"
#include <QApplication>
@else
#include <QCoreApplication>
@endif
// add necessary includes here
int main(int argc, char *argv[])
{
@if "%RequireGUI%" == "true"
QApplication a(argc, argv);
@else
QCoreApplication a(argc, argv);
@endif
return a.exec();
}

10
shared/autotest/src.pro Normal file
View File

@@ -0,0 +1,10 @@
@if "%RequireGUI%" == "true"
QT += gui widgets
@else
QT += console
CONFIG -= app_bundle
@endif
TEMPLATE = app
SOURCES += main.%CppSourceSuffix%

View File

@@ -0,0 +1,3 @@
TEMPLATE = subdirs
SUBDIRS += auto

12
shared/autotest/tmp.pro Normal file
View File

@@ -0,0 +1,12 @@
TEMPLATE = subdirs
@if "%BuildTests%" == "always"
SUBDIRS += src \
tests
@else
SUBDIRS += src
CONFIG(debug, debug|release) {
SUBDIRS += tests
}
@endif

14
shared/autotest/tst.pro Normal file
View File

@@ -0,0 +1,14 @@
QT += testlib
@if "%RequireGUI%" == "false"
QT -= gui
CONFIG += qt console warn_on depend_includepath testcase
CONFIG -= app_bundle
@else
QT += gui
CONFIG += qt warn_on depend_includepath testcase
@endif
TEMPLATE = app
SOURCES += tst_%TestCaseName:l%.%CppSourceSuffix%

View File

@@ -0,0 +1,58 @@
#include <QtTest>
@if "%RequireApplication%" == "true"
#include <QCoreApplication>
@endif
// add necessary includes here
class %TestCaseName% : public QObject
{
Q_OBJECT
public:
%TestCaseName%();
~%TestCaseName%();
private slots:
@if "%GenerateInitAndCleanup%" == "true"
void initTestCase();
void cleanupTestCase();
@endif
void test_case1();
};
%TestCaseName%::%TestCaseName%()
{
}
%TestCaseName%::~%TestCaseName%()
{
}
@if "%GenerateInitAndCleanup%" == "true"
void %TestCaseName%::initTestCase()
{
}
void %TestCaseName%::cleanupTestCase()
{
}
@endif
void %TestCaseName%::test_case1()
{
}
@if "%RequireApplication%" == "true"
QTEST_MAIN(%TestCaseName%)
@else
QTEST_APPLESS_MAIN(%TestCaseName%)
@endif
#include "tst_%TestCaseName:l%.moc"

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
Custom project wizard configuration example file. Note that by convention,
the project file goes last.
The "class" and "firstpage" attributes specify that it is a Qt 4 wizard and
leave room for the Qt 4 target page.
-->
<wizard version="1" kind="project"
class="qmakeproject" firstpage="10"
id="R.AutoTest" category="H.Project"
featuresRequired="QtSupport.Wizards.FeatureQt,QtSupport.Wizards.FeatureDesktop">
<!-- "Plugin.AutotestGenerator" as required feature would disable the template if the
plugin is disabled, but it fails on the kit selection page for having no valid kit -->
<icon>autotest_24.png</icon>
<description>Creates a new project including auto test skeleton.</description>
<displayname>Auto Test</displayname>;
<displaycategory>Other Project</displaycategory>
<files>
<file source="src.pro" target="src/src.pro" />
<file source="tests.pro" target="tests/tests.pro" />
<file source="auto.pro" target="tests/auto/auto.pro"/>
<file source="tst.pro" target="tests/auto/%TestCaseName:l%/%TestCaseName:l%.pro" />
<file source="tst_src.cpp"
target="tests/auto/%TestCaseName:l%/tst_%TestCaseName:l%.%CppSourceSuffix%"
openeditor="true"/>
<file source="main.cpp" target="src/main.%CppSourceSuffix%" openeditor="true" />
<file source="tmp.pro" target="%ProjectName:l%.pro" openproject="true" />
</files>
<!-- Create a 2nd wizard page with parameters -->
<fieldpagetitle>Project and Test Information</fieldpagetitle>
<fields>
<field name="RequireGUI">
<fieldcontrol class="QCheckBox" />
<fielddescription>GUI Application</fielddescription>
</field>
<field mandatory="true" name="TestCaseName">
<fieldcontrol class="QLineEdit" validator="^[a-zA-Z_0-9]+$"
/><!-- defaulttext="" /> -->
<fielddescription>Test Case Name:</fielddescription>
</field>
<field name="RequireApplication">
<fieldcontrol class="QCheckBox" />
<fielddescription>Requires QApplication</fielddescription>
</field>
<field name="GenerateInitAndCleanup">
<fieldcontrol class="QCheckBox" />
<fielddescription>Generate initialization and cleanup code</fielddescription>
</field>
<field name="BuildAutoTests">
<fieldcontrol class="QComboBox" defaultindex="0" >
<comboentries>
<comboentry value="always" >
<comboentrytext>always</comboentrytext>
</comboentry>
<comboentry value="debug" >
<comboentrytext>debug only</comboentrytext>
</comboentry>
</comboentries>
</fieldcontrol>
<fielddescription>Build auto tests</fielddescription>
</field>
</fields>
</wizard>

10
shared/shared.pro Normal file
View File

@@ -0,0 +1,10 @@
TEMPLATE = aux
content.files = \
autotest
content.path = $$QTC_PREFIX/share/qtcreator/templates/wizards
OTHER_FILES += $${content.files}
INSTALLS += content