summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Stenger <christian.stenger@digia.com>2014-10-07 12:30:54 +0200
committerChristian Stenger <christian.stenger@theqtcompany.com>2014-12-04 13:52:14 +0100
commit2f4139e5d313902683a3a4a98f9ef012afff8734 (patch)
tree5e1c10378f5a4d9ca2e45dd39b2a6f4dba06d6ee
downloadqt-creator-2f4139e5d313902683a3a4a98f9ef012afff8734.tar.gz
Initial commit
-rw-r--r--autotest.pro6
-rw-r--r--plugins/autotest/AutoTest.json.in15
-rw-r--r--plugins/autotest/autotest.pro33
-rw-r--r--plugins/autotest/autotest.qrc9
-rw-r--r--plugins/autotest/autotest_dependencies.pri12
-rw-r--r--plugins/autotest/autotest_global.h31
-rw-r--r--plugins/autotest/autotestconstants.h34
-rw-r--r--plugins/autotest/autotestplugin.cpp102
-rw-r--r--plugins/autotest/autotestplugin.h50
-rw-r--r--plugins/autotest/images/class.pngbin0 -> 573 bytes
-rw-r--r--plugins/autotest/images/collapse.pngbin0 -> 205 bytes
-rw-r--r--plugins/autotest/images/expand.pngbin0 -> 216 bytes
-rw-r--r--plugins/autotest/images/func.pngbin0 -> 709 bytes
-rw-r--r--plugins/autotest/images/sort.pngbin0 -> 233 bytes
-rw-r--r--plugins/autotest/testcodeparser.cpp261
-rw-r--r--plugins/autotest/testcodeparser.h63
-rw-r--r--plugins/autotest/testinfo.cpp34
-rw-r--r--plugins/autotest/testinfo.h52
-rw-r--r--plugins/autotest/testtreeitem.cpp136
-rw-r--r--plugins/autotest/testtreeitem.h76
-rw-r--r--plugins/autotest/testtreemodel.cpp329
-rw-r--r--plugins/autotest/testtreemodel.h80
-rw-r--r--plugins/autotest/testtreeview.cpp204
-rw-r--r--plugins/autotest/testtreeview.h94
-rw-r--r--plugins/autotest/testvisitor.cpp75
-rw-r--r--plugins/autotest/testvisitor.h54
-rw-r--r--qtcreatorplugin.pri8
-rw-r--r--shared/autotest/auto.pro3
-rw-r--r--shared/autotest/autotest_24.pngbin0 -> 683 bytes
-rw-r--r--shared/autotest/main.cpp18
-rw-r--r--shared/autotest/src.pro10
-rw-r--r--shared/autotest/tests.pro3
-rw-r--r--shared/autotest/tmp.pro12
-rw-r--r--shared/autotest/tst.pro14
-rw-r--r--shared/autotest/tst_src.cpp58
-rw-r--r--shared/autotest/wizard.xml92
-rw-r--r--shared/shared.pro10
37 files changed, 1978 insertions, 0 deletions
diff --git a/autotest.pro b/autotest.pro
new file mode 100644
index 0000000000..77543b34a5
--- /dev/null
+++ b/autotest.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS += plugins/autotest shared
+
+QMAKE_EXTRA_TARGETS = docs install_docs # dummy targets for consistency
diff --git a/plugins/autotest/AutoTest.json.in b/plugins/autotest/AutoTest.json.in
new file mode 100644
index 0000000000..dd25f250d2
--- /dev/null
+++ b/plugins/autotest/AutoTest.json.in
@@ -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
+}
diff --git a/plugins/autotest/autotest.pro b/plugins/autotest/autotest.pro
new file mode 100644
index 0000000000..19df6bcb2e
--- /dev/null
+++ b/plugins/autotest/autotest.pro
@@ -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
+
diff --git a/plugins/autotest/autotest.qrc b/plugins/autotest/autotest.qrc
new file mode 100644
index 0000000000..53dee42e31
--- /dev/null
+++ b/plugins/autotest/autotest.qrc
@@ -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>
diff --git a/plugins/autotest/autotest_dependencies.pri b/plugins/autotest/autotest_dependencies.pri
new file mode 100644
index 0000000000..af150efd0c
--- /dev/null
+++ b/plugins/autotest/autotest_dependencies.pri
@@ -0,0 +1,12 @@
+QTC_PLUGIN_NAME = AutoTest
+
+QTC_PLUGIN_DEPENDS += \
+ coreplugin \
+ projectexplorer \
+ cpptools
+
+QTC_LIB_DEPENDS += \
+ cplusplus \
+ utils
+
+#QTC_PLUGIN_RECOMMENDS += \
diff --git a/plugins/autotest/autotest_global.h b/plugins/autotest/autotest_global.h
new file mode 100644
index 0000000000..3f3c6ae192
--- /dev/null
+++ b/plugins/autotest/autotest_global.h
@@ -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
+
diff --git a/plugins/autotest/autotestconstants.h b/plugins/autotest/autotestconstants.h
new file mode 100644
index 0000000000..df3e26be11
--- /dev/null
+++ b/plugins/autotest/autotestconstants.h
@@ -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
+
diff --git a/plugins/autotest/autotestplugin.cpp b/plugins/autotest/autotestplugin.cpp
new file mode 100644
index 0000000000..b0da2c1cac
--- /dev/null
+++ b/plugins/autotest/autotestplugin.cpp
@@ -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."));
+}
+
diff --git a/plugins/autotest/autotestplugin.h b/plugins/autotest/autotestplugin.h
new file mode 100644
index 0000000000..6391f050fe
--- /dev/null
+++ b/plugins/autotest/autotestplugin.h
@@ -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
+
diff --git a/plugins/autotest/images/class.png b/plugins/autotest/images/class.png
new file mode 100644
index 0000000000..88432d2cb1
--- /dev/null
+++ b/plugins/autotest/images/class.png
Binary files differ
diff --git a/plugins/autotest/images/collapse.png b/plugins/autotest/images/collapse.png
new file mode 100644
index 0000000000..d6da22a5b8
--- /dev/null
+++ b/plugins/autotest/images/collapse.png
Binary files differ
diff --git a/plugins/autotest/images/expand.png b/plugins/autotest/images/expand.png
new file mode 100644
index 0000000000..a859960ea8
--- /dev/null
+++ b/plugins/autotest/images/expand.png
Binary files differ
diff --git a/plugins/autotest/images/func.png b/plugins/autotest/images/func.png
new file mode 100644
index 0000000000..8f585e875d
--- /dev/null
+++ b/plugins/autotest/images/func.png
Binary files differ
diff --git a/plugins/autotest/images/sort.png b/plugins/autotest/images/sort.png
new file mode 100644
index 0000000000..2718b01d6e
--- /dev/null
+++ b/plugins/autotest/images/sort.png
Binary files differ
diff --git a/plugins/autotest/testcodeparser.cpp b/plugins/autotest/testcodeparser.cpp
new file mode 100644
index 0000000000..dbee199976
--- /dev/null
+++ b/plugins/autotest/testcodeparser.cpp
@@ -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
diff --git a/plugins/autotest/testcodeparser.h b/plugins/autotest/testcodeparser.h
new file mode 100644
index 0000000000..0ef4eedc33
--- /dev/null
+++ b/plugins/autotest/testcodeparser.h
@@ -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
diff --git a/plugins/autotest/testinfo.cpp b/plugins/autotest/testinfo.cpp
new file mode 100644
index 0000000000..8e5850c84b
--- /dev/null
+++ b/plugins/autotest/testinfo.cpp
@@ -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
diff --git a/plugins/autotest/testinfo.h b/plugins/autotest/testinfo.h
new file mode 100644
index 0000000000..378afcab58
--- /dev/null
+++ b/plugins/autotest/testinfo.h
@@ -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
diff --git a/plugins/autotest/testtreeitem.cpp b/plugins/autotest/testtreeitem.cpp
new file mode 100644
index 0000000000..7fb8b85e19
--- /dev/null
+++ b/plugins/autotest/testtreeitem.cpp
@@ -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
diff --git a/plugins/autotest/testtreeitem.h b/plugins/autotest/testtreeitem.h
new file mode 100644
index 0000000000..edda539fd3
--- /dev/null
+++ b/plugins/autotest/testtreeitem.h
@@ -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
diff --git a/plugins/autotest/testtreemodel.cpp b/plugins/autotest/testtreemodel.cpp
new file mode 100644
index 0000000000..88bc653d4e
--- /dev/null
+++ b/plugins/autotest/testtreemodel.cpp
@@ -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
diff --git a/plugins/autotest/testtreemodel.h b/plugins/autotest/testtreemodel.h
new file mode 100644
index 0000000000..9ec25f0476
--- /dev/null
+++ b/plugins/autotest/testtreemodel.h
@@ -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
diff --git a/plugins/autotest/testtreeview.cpp b/plugins/autotest/testtreeview.cpp
new file mode 100644
index 0000000000..04b2ffb60f
--- /dev/null
+++ b/plugins/autotest/testtreeview.cpp
@@ -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
diff --git a/plugins/autotest/testtreeview.h b/plugins/autotest/testtreeview.h
new file mode 100644
index 0000000000..cb66fa12cc
--- /dev/null
+++ b/plugins/autotest/testtreeview.h
@@ -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
diff --git a/plugins/autotest/testvisitor.cpp b/plugins/autotest/testvisitor.cpp
new file mode 100644
index 0000000000..4f83c9d55b
--- /dev/null
+++ b/plugins/autotest/testvisitor.cpp
@@ -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
diff --git a/plugins/autotest/testvisitor.h b/plugins/autotest/testvisitor.h
new file mode 100644
index 0000000000..c3fafac6a9
--- /dev/null
+++ b/plugins/autotest/testvisitor.h
@@ -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
diff --git a/qtcreatorplugin.pri b/qtcreatorplugin.pri
new file mode 100644
index 0000000000..190dbed1c3
--- /dev/null
+++ b/qtcreatorplugin.pri
@@ -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)
diff --git a/shared/autotest/auto.pro b/shared/autotest/auto.pro
new file mode 100644
index 0000000000..b39fef654c
--- /dev/null
+++ b/shared/autotest/auto.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += %TestCaseName:l%
diff --git a/shared/autotest/autotest_24.png b/shared/autotest/autotest_24.png
new file mode 100644
index 0000000000..3f52e9bf08
--- /dev/null
+++ b/shared/autotest/autotest_24.png
Binary files differ
diff --git a/shared/autotest/main.cpp b/shared/autotest/main.cpp
new file mode 100644
index 0000000000..3ff500decd
--- /dev/null
+++ b/shared/autotest/main.cpp
@@ -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();
+}
diff --git a/shared/autotest/src.pro b/shared/autotest/src.pro
new file mode 100644
index 0000000000..3d0e33848b
--- /dev/null
+++ b/shared/autotest/src.pro
@@ -0,0 +1,10 @@
+@if "%RequireGUI%" == "true"
+QT += gui widgets
+@else
+QT += console
+CONFIG -= app_bundle
+@endif
+
+TEMPLATE = app
+
+SOURCES += main.%CppSourceSuffix%
diff --git a/shared/autotest/tests.pro b/shared/autotest/tests.pro
new file mode 100644
index 0000000000..f927700008
--- /dev/null
+++ b/shared/autotest/tests.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += auto
diff --git a/shared/autotest/tmp.pro b/shared/autotest/tmp.pro
new file mode 100644
index 0000000000..1569bd45cb
--- /dev/null
+++ b/shared/autotest/tmp.pro
@@ -0,0 +1,12 @@
+TEMPLATE = subdirs
+
+@if "%BuildTests%" == "always"
+SUBDIRS += src \
+ tests
+@else
+SUBDIRS += src
+
+CONFIG(debug, debug|release) {
+ SUBDIRS += tests
+}
+@endif
diff --git a/shared/autotest/tst.pro b/shared/autotest/tst.pro
new file mode 100644
index 0000000000..8aa61b767e
--- /dev/null
+++ b/shared/autotest/tst.pro
@@ -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%
diff --git a/shared/autotest/tst_src.cpp b/shared/autotest/tst_src.cpp
new file mode 100644
index 0000000000..65d49e9c18
--- /dev/null
+++ b/shared/autotest/tst_src.cpp
@@ -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"
diff --git a/shared/autotest/wizard.xml b/shared/autotest/wizard.xml
new file mode 100644
index 0000000000..a190405d38
--- /dev/null
+++ b/shared/autotest/wizard.xml
@@ -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>
diff --git a/shared/shared.pro b/shared/shared.pro
new file mode 100644
index 0000000000..e74d4d09ac
--- /dev/null
+++ b/shared/shared.pro
@@ -0,0 +1,10 @@
+TEMPLATE = aux
+
+content.files = \
+ autotest
+
+content.path = $$QTC_PREFIX/share/qtcreator/templates/wizards
+
+OTHER_FILES += $${content.files}
+
+INSTALLS += content