summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Stenger <christian.stenger@digia.com>2014-11-06 16:01:06 +0100
committerChristian Stenger <christian.stenger@theqtcompany.com>2014-12-04 13:52:16 +0100
commit0357b0e98bf049ddb2e365a6ddcace67c2f0e836 (patch)
tree4a28be6a20b83325f9bb580ba7495e799104f4cc
parent85efa2c3c969392afbe869ed0fc4a4af91c739f4 (diff)
downloadqt-creator-0357b0e98bf049ddb2e365a6ddcace67c2f0e836.tar.gz
Add basic support for Qt Quick Test
-rw-r--r--plugins/autotest/autotest_dependencies.pri2
-rw-r--r--plugins/autotest/testcodeparser.cpp299
-rw-r--r--plugins/autotest/testcodeparser.h7
-rw-r--r--plugins/autotest/testconfiguration.cpp20
-rw-r--r--plugins/autotest/testconfiguration.h2
-rw-r--r--plugins/autotest/testrunner.cpp2
-rw-r--r--plugins/autotest/testtreeitem.cpp4
-rw-r--r--plugins/autotest/testtreeitem.h3
-rw-r--r--plugins/autotest/testtreemodel.cpp262
-rw-r--r--plugins/autotest/testtreemodel.h11
-rw-r--r--plugins/autotest/testtreeview.cpp53
-rw-r--r--plugins/autotest/testvisitor.cpp79
-rw-r--r--plugins/autotest/testvisitor.h27
13 files changed, 681 insertions, 90 deletions
diff --git a/plugins/autotest/autotest_dependencies.pri b/plugins/autotest/autotest_dependencies.pri
index c5d76107a0..6546683f45 100644
--- a/plugins/autotest/autotest_dependencies.pri
+++ b/plugins/autotest/autotest_dependencies.pri
@@ -4,10 +4,12 @@ QTC_PLUGIN_DEPENDS += \
coreplugin \
projectexplorer \
cpptools \
+ qmljstools \
licensechecker
QTC_LIB_DEPENDS += \
cplusplus \
+ qmljs \
utils
#QTC_PLUGIN_RECOMMENDS += \
diff --git a/plugins/autotest/testcodeparser.cpp b/plugins/autotest/testcodeparser.cpp
index 23006677fe..f58b2a4489 100644
--- a/plugins/autotest/testcodeparser.cpp
+++ b/plugins/autotest/testcodeparser.cpp
@@ -30,6 +30,10 @@
#include <projectexplorer/session.h>
+#include <qmljs/parser/qmljsast_p.h>
+#include <qmljs/qmljsdialect.h>
+#include <qmljstools/qmljsmodelmanager.h>
+
#include <utils/textfileformat.h>
namespace Autotest {
@@ -53,6 +57,7 @@ void TestCodeParser::updateTestTree()
clearMaps();
m_model->removeAllAutoTests();
+ m_model->removeAllQuickTests();
const ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance();
if (!session || !session->hasProjects())
return;
@@ -88,7 +93,7 @@ static bool includesQtTest(const CPlusPlus::Document::Ptr &doc,
{
const QList<CPlusPlus::Document::Include> includes = doc->resolvedIncludes();
- foreach (const CPlusPlus::Document::Include inc, includes) {
+ foreach (const 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")
@@ -100,7 +105,7 @@ static bool includesQtTest(const CPlusPlus::Document::Ptr &doc,
if (cppMM) {
CPlusPlus::Snapshot snapshot = cppMM->snapshot();
const QSet<QString> allIncludes = snapshot.allIncludesForDocument(doc->fileName());
- foreach (const QString include, allIncludes) {
+ foreach (const QString &include, allIncludes) {
if (include.endsWith(QLatin1String("QtTest/qtest.h"))) {
return true;
}
@@ -109,6 +114,27 @@ static bool includesQtTest(const CPlusPlus::Document::Ptr &doc,
return false;
}
+static bool includesQtQuickTest(const CPlusPlus::Document::Ptr &doc,
+ const CppTools::CppModelManager *cppMM)
+{
+ const QList<CPlusPlus::Document::Include> includes = doc->resolvedIncludes();
+
+ foreach (const CPlusPlus::Document::Include &inc, includes) {
+ if (inc.unresolvedFileName() == QLatin1String("QtQuickTest/quicktest.h")
+ && inc.resolvedFileName().endsWith(QLatin1String("QtQuickTest/quicktest.h"))) {
+ return true;
+ }
+ }
+
+ if (cppMM) {
+ foreach (const QString &include, cppMM->snapshot().allIncludesForDocument(doc->fileName())) {
+ if (include.endsWith(QLatin1String("QtQuickTest/quicktest.h")))
+ return true;
+ }
+ }
+ return false;
+}
+
static bool qtTestLibDefined(const CppTools::CppModelManager *cppMM,
const QString &fileName)
{
@@ -118,24 +144,91 @@ static bool qtTestLibDefined(const CppTools::CppModelManager *cppMM,
return false;
}
+static QString quickTestSrcDir(const CppTools::CppModelManager *cppMM,
+ const QString &fileName)
+{
+ static const QByteArray qtsd(" QUICK_TEST_SOURCE_DIR ");
+ const QList<CppTools::ProjectPart::Ptr> parts = cppMM->projectPart(fileName);
+ if (parts.size() > 0) {
+ QByteArray projDefines(parts.at(0)->projectDefines);
+ foreach (const QByteArray &line, projDefines.split('\n')) {
+ if (line.contains(qtsd)) {
+ QByteArray result = line.mid(line.indexOf(qtsd) + qtsd.length());
+ if (result.startsWith('"'))
+ result.remove(result.length() - 1, 1).remove(0, 1);
+ if (result.startsWith("\\\""))
+ result.remove(result.length() - 2, 2).remove(0, 2);
+ return QLatin1String(result);
+ }
+ }
+ }
+ return QString();
+}
+
static QString testClass(const CPlusPlus::Document::Ptr &doc)
{
- static QByteArray qtTestMacros[] = {"QTEST_MAIN", "QTEST_APPLESS_MAIN", "QTEST_GUILESS_MAIN"};
- QString tC;
+ static const QByteArray qtTestMacros[] = {"QTEST_MAIN", "QTEST_APPLESS_MAIN", "QTEST_GUILESS_MAIN"};
+ const QList<CPlusPlus::Document::MacroUse> macros = doc->macroUses();
+
+ foreach (const CPlusPlus::Document::MacroUse &macro, macros) {
+ if (!macro.isFunctionLike())
+ continue;
+ const QByteArray name = macro.macro().name();
+ if (name == qtTestMacros[0] || name == qtTestMacros[1] || name == qtTestMacros[2]) {
+ const CPlusPlus::Document::Block arg = macro.arguments().at(0);
+ return QLatin1String(getFileContent(doc->fileName()).mid(arg.bytesBegin(), arg.bytesEnd() - arg.bytesBegin()));
+ }
+ }
+ return QString();
+}
+
+static QString quickTestName(const CPlusPlus::Document::Ptr &doc)
+{
+ static const QByteArray qtTestMacros[] = {"QUICK_TEST_MAIN", "QUICK_TEST_OPENGL_MAIN"};
const QList<CPlusPlus::Document::MacroUse> macros = doc->macroUses();
- foreach (const CPlusPlus::Document::MacroUse macro, macros) {
+ foreach (const CPlusPlus::Document::MacroUse &macro, macros) {
if (!macro.isFunctionLike())
continue;
const QByteArray name = macro.macro().name();
if (name == qtTestMacros[0] || name == qtTestMacros[1] || name == qtTestMacros[2]) {
CPlusPlus::Document::Block arg = macro.arguments().at(0);
- tC = QLatin1String(getFileContent(doc->fileName()).mid(arg.bytesBegin(),
- arg.bytesEnd() - arg.bytesBegin()));
- break;
+ return QLatin1String(getFileContent(doc->fileName()).mid(arg.bytesBegin(), arg.bytesEnd() - arg.bytesBegin()));
}
}
- return tC;
+ return QString();
+}
+
+static QList<QmlJS::Document::Ptr> scanDirectoryForQuickTestQmlFiles(const QString &srcDir)
+{
+ QStringList dirs(srcDir);
+ QmlJS::ModelManagerInterface *qmlJsMM = QmlJSTools::Internal::ModelManager::instance();
+ // make sure even files not listed in pro file are available inside the snapshot
+ QFutureInterface<void> future;
+ QmlJS::PathsAndLanguages paths;
+ paths.maybeInsert(Utils::FileName::fromString(srcDir), QmlJS::Dialect::Qml);
+ QmlJS::ModelManagerInterface::importScan(future, qmlJsMM->workingCopy(),
+ paths, qmlJsMM, false, false);
+
+ const QmlJS::Snapshot snapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
+ QDirIterator it(srcDir, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ it.next();
+ QFileInfo fi(it.fileInfo().canonicalFilePath());
+ dirs << fi.filePath();
+ }
+ QList<QmlJS::Document::Ptr> foundDocs;
+
+ foreach (const QString &path, dirs) {
+ const QList<QmlJS::Document::Ptr> docs = snapshot.documentsInDirectory(path);
+ foreach (const QmlJS::Document::Ptr &doc, docs) {
+ const QString fileName(QFileInfo(doc->fileName()).fileName());
+ if (fileName.startsWith(QLatin1String("tst_")) && fileName.endsWith(QLatin1String(".qml")))
+ foundDocs << doc;
+ }
+ }
+
+ return foundDocs;
}
/****** end of helpers ******/
@@ -145,6 +238,11 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr doc)
const QString file = doc->fileName();
const CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
+ if (includesQtQuickTest(doc, cppMM)) {
+ handleQtQuickTest(doc);
+ return;
+ }
+
if (includesQtTest(doc, cppMM) && qtTestLibDefined(cppMM, file)) {
QString tc(testClass(doc));
if (tc.isEmpty()) {
@@ -187,7 +285,7 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr doc)
TestVisitor myVisitor(tc);
myVisitor.accept(declaringDoc->globalNamespace());
const QMap<QString, TestCodeLocation> privSlots = myVisitor.privateSlots();
- foreach (const QString privS, privSlots.keys()) {
+ foreach (const QString &privS, privSlots.keys()) {
const TestCodeLocation location = privSlots.value(privS);
TestTreeItem *ttSub = new TestTreeItem(privS, location.m_fileName,
TestTreeItem::TEST_FUNCTION, ttItem);
@@ -253,7 +351,143 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr doc)
}
}
-void TestCodeParser::onDocumentUpdated(CPlusPlus::Document::Ptr doc)
+void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr doc)
+{
+ const CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
+
+ if (quickTestName(doc).isEmpty())
+ return;
+
+ const QString srcDir = quickTestSrcDir(cppMM, doc->fileName());
+ if (srcDir.isEmpty())
+ return;
+
+ const QList<QmlJS::Document::Ptr> qmlDocs = scanDirectoryForQuickTestQmlFiles(srcDir);
+ foreach (const QmlJS::Document::Ptr &d, qmlDocs) {
+ QmlJS::AST::Node *ast = d->ast();
+ if (!ast) {
+ qDebug() << "ast is zero pointer" << d->fileName(); // should not happen
+ continue;
+ }
+ TestQmlVisitor qmlVisitor(d);
+ QmlJS::AST::Node::accept(ast, &qmlVisitor);
+
+ const QString tcName = qmlVisitor.testCaseName();
+ const TestCodeLocation tcLocation = qmlVisitor.testCaseLocation();
+ const QMap<QString, TestCodeLocation> testFunctions = qmlVisitor.testFunctions();
+
+ const QModelIndex quickTestRootIndex = m_model->index(1, 0);
+ TestTreeItem *quickTestRootItem = static_cast<TestTreeItem *>(quickTestRootIndex.internalPointer());
+
+ if (tcName.isEmpty()) {
+ // if this test case was named before remove it
+ if (m_quickDocMap.contains(d->fileName())) {
+ m_model->removeQuickTestSubtreeByFilePath(d->fileName());
+ m_quickDocMap.remove(d->fileName());
+ }
+ bool hadUnnamedTestsBefore;
+ TestTreeItem *ttItem = m_model->unnamedQuickTests();
+ if (!ttItem) {
+ hadUnnamedTestsBefore = false;
+ ttItem = new TestTreeItem(QString(), QString(), TestTreeItem::TEST_CLASS,
+ quickTestRootItem);
+ foreach (const QString &func, testFunctions.keys()) {
+ const TestCodeLocation location = testFunctions.value(func);
+ TestTreeItem *ttSub = new TestTreeItem(func, location.m_fileName,
+ TestTreeItem::TEST_FUNCTION, ttItem);
+ ttSub->setLine(location.m_line);
+ ttSub->setColumn(location.m_column);
+ ttSub->setMainFile(doc->fileName());
+ ttItem->appendChild(ttSub);
+ }
+ } else {
+ hadUnnamedTestsBefore = true;
+ // remove unnamed quick tests that are already found for this qml file
+ m_model->removeUnnamedQuickTest(d->fileName());
+
+ foreach (const QString &func, testFunctions.keys()) {
+ const TestCodeLocation location = testFunctions.value(func);
+ TestTreeItem *ttSub = new TestTreeItem(func, location.m_fileName,
+ TestTreeItem::TEST_FUNCTION, ttItem);
+ ttSub->setLine(location.m_line);
+ ttSub->setColumn(location.m_column);
+ ttSub->setMainFile(doc->fileName());
+ ttItem->appendChild(ttSub);
+ }
+ }
+ TestInfo info = m_quickDocMap.contains(QLatin1String("<unnamed>"))
+ ? m_quickDocMap[QLatin1String("<unnamed>")]
+ : TestInfo(QString(), QStringList(), 666);
+ QStringList originalFunctions(info.testFunctions());
+ foreach (const QString &func, testFunctions.keys()) {
+ if (!originalFunctions.contains(func))
+ originalFunctions.append(func);
+ }
+ info.setTestFunctions(originalFunctions);
+
+ if (hadUnnamedTestsBefore)
+ m_model->modifyQuickTestSubtree(ttItem->row(), ttItem);
+ else
+ m_model->addQuickTest(ttItem);
+ m_quickDocMap.insert(QLatin1String("<unnamed>"), info);
+
+ continue;
+ } // end of handling test cases without name property
+
+ // construct new/modified TestTreeItem
+ TestTreeItem *ttItem = new TestTreeItem(tcName, tcLocation.m_fileName,
+ TestTreeItem::TEST_CLASS, quickTestRootItem);
+ ttItem->setLine(tcLocation.m_line);
+ ttItem->setColumn(tcLocation.m_column);
+ ttItem->setMainFile(doc->fileName());
+
+ foreach (const QString &func, testFunctions.keys()) {
+ const TestCodeLocation location = testFunctions.value(func);
+ TestTreeItem *ttSub = new TestTreeItem(func, location.m_fileName,
+ TestTreeItem::TEST_FUNCTION, ttItem);
+ ttSub->setLine(location.m_line);
+ ttSub->setColumn(location.m_column);
+ ttItem->appendChild(ttSub);
+ }
+
+ // update model and internal map
+ const QString fileName(tcLocation.m_fileName);
+ const QmlJS::Document::Ptr qmlDoc =
+ QmlJSTools::Internal::ModelManager::instance()->snapshot().document(fileName);
+
+ if (m_quickDocMap.contains(fileName)) {
+ for (int i = 0; i < quickTestRootItem->childCount(); ++i) {
+ if (quickTestRootItem->child(i)->filePath() == fileName) {
+ m_model->modifyQuickTestSubtree(i, ttItem);
+ TestInfo ti(tcName, testFunctions.keys(), 0, qmlDoc->editorRevision());
+ ti.setReferencingFile(doc->fileName());
+ m_quickDocMap.insert(fileName, ti);
+ break;
+ }
+ }
+ delete ttItem;
+ } else {
+ // if it was formerly unnamed remove the respective items
+ if (m_quickDocMap.contains(QLatin1String("<unnamed>"))) {
+ m_model->removeUnnamedQuickTest(d->fileName());
+ TestInfo unnamedInfo = m_quickDocMap[QLatin1String("<unnamed>")];
+ QStringList functions = unnamedInfo.testFunctions();
+ foreach (const QString &func, testFunctions.keys())
+ if (functions.contains(func))
+ functions.removeOne(func);
+ unnamedInfo.setTestFunctions(functions);
+ m_quickDocMap.insert(QLatin1String("<unnamed>"), unnamedInfo);
+ }
+
+ m_model->addQuickTest(ttItem);
+ TestInfo ti(tcName, testFunctions.keys(), 0, qmlDoc->editorRevision());
+ ti.setReferencingFile(doc->fileName());
+ m_quickDocMap.insert(tcLocation.m_fileName, ti);
+ }
+ }
+}
+
+void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &doc)
{
if (!m_currentProject)
return;
@@ -270,13 +504,51 @@ void TestCodeParser::onDocumentUpdated(CPlusPlus::Document::Ptr doc)
checkDocumentForTestCode(doc);
}
+void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &doc)
+{
+ if (!m_currentProject)
+ return;
+ const QString fileName = doc->fileName();
+ if (m_quickDocMap.contains(fileName)) {
+ if ((int)m_quickDocMap[fileName].editorRevision() == doc->editorRevision()) {
+ qDebug("Skipped due revision equality (QML)"); // added to verify this ever happens....
+ return;
+ }
+ } else if (!m_currentProject->files(ProjectExplorer::Project::AllFiles).contains(fileName)) {
+ // what if the file is not listed inside the pro file, but will be used anyway?
+ return;
+ }
+ const CPlusPlus::Snapshot snapshot = CppTools::CppModelManager::instance()->snapshot();
+ if (m_quickDocMap.contains(fileName)
+ && snapshot.contains(m_quickDocMap[fileName].referencingFile())) {
+ checkDocumentForTestCode(snapshot.document(m_quickDocMap[fileName].referencingFile()));
+ }
+ if (!m_quickDocMap.contains(QLatin1String("<unnamed>")))
+ return;
+
+ // special case of having unnamed TestCases
+ TestTreeItem *unnamed = m_model->unnamedQuickTests();
+ for (int row = 0, count = unnamed->childCount(); row < count; ++row) {
+ const TestTreeItem *child = unnamed->child(row);
+ if (fileName == child->filePath()) {
+ if (snapshot.contains(child->mainFile()))
+ checkDocumentForTestCode(snapshot.document(child->mainFile()));
+ break;
+ }
+ }
+}
+
void TestCodeParser::removeFiles(const QStringList &files)
{
- foreach (const QString file, files) {
+ foreach (const QString &file, files) {
if (m_cppDocMap.contains(file)) {
m_cppDocMap.remove(file);
m_model->removeAutoTestSubtreeByFilePath(file);
}
+ if (m_quickDocMap.contains(file)) {
+ m_quickDocMap.remove(file);
+ m_model->removeQuickTestSubtreeByFilePath(file);
+ }
}
}
@@ -286,7 +558,7 @@ void TestCodeParser::scanForTests()
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
CPlusPlus::Snapshot snapshot = cppMM->snapshot();
- foreach (const QString file, list) {
+ foreach (const QString &file, list) {
if (snapshot.contains(file)) {
CPlusPlus::Document::Ptr doc = snapshot.find(file).value();
checkDocumentForTestCode(doc);
@@ -297,6 +569,7 @@ void TestCodeParser::scanForTests()
void TestCodeParser::clearMaps()
{
m_cppDocMap.clear();
+ m_quickDocMap.clear();
}
} // namespace Internal
diff --git a/plugins/autotest/testcodeparser.h b/plugins/autotest/testcodeparser.h
index 192a9ca220..5b422ba0e6 100644
--- a/plugins/autotest/testcodeparser.h
+++ b/plugins/autotest/testcodeparser.h
@@ -21,6 +21,8 @@
#include <cplusplus/CppDocument.h>
+#include <qmljs/qmljsdocument.h>
+
#include <QObject>
#include <QMap>
@@ -46,8 +48,10 @@ signals:
public slots:
void updateTestTree();
void checkDocumentForTestCode(CPlusPlus::Document::Ptr doc);
+ void handleQtQuickTest(CPlusPlus::Document::Ptr doc);
- void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
+ void onCppDocumentUpdated(const CPlusPlus::Document::Ptr &doc);
+ void onQmlDocumentUpdated(const QmlJS::Document::Ptr &doc);
void removeFiles(const QStringList &files);
private:
@@ -56,6 +60,7 @@ private:
TestTreeModel *m_model;
QMap<QString, TestInfo> m_cppDocMap;
+ QMap<QString, TestInfo> m_quickDocMap;
ProjectExplorer::Project *m_currentProject;
};
diff --git a/plugins/autotest/testconfiguration.cpp b/plugins/autotest/testconfiguration.cpp
index 4841a86fbf..bcb90d75b5 100644
--- a/plugins/autotest/testconfiguration.cpp
+++ b/plugins/autotest/testconfiguration.cpp
@@ -41,6 +41,26 @@ TestConfiguration::~TestConfiguration()
m_testCases.clear();
}
+/**
+ * @brief sets the test cases for this test configuration.
+ *
+ * Watch out for special handling of test configurations, because this method also
+ * updates the test case count to the current size of \a testCases.
+ *
+ * @param testCases list of names of the test functions / test cases
+ */
+void TestConfiguration::setTestCases(const QStringList &testCases)
+{
+ m_testCases.clear();
+ m_testCases << testCases;
+ m_testCaseCount = m_testCases.size();
+}
+
+void TestConfiguration::setTestCaseCount(int count)
+{
+ m_testCaseCount = count;
+}
+
void TestConfiguration::setTargetFile(const QString &targetFile)
{
m_targetFile = targetFile;
diff --git a/plugins/autotest/testconfiguration.h b/plugins/autotest/testconfiguration.h
index aab9d31db6..aa82b8e2b5 100644
--- a/plugins/autotest/testconfiguration.h
+++ b/plugins/autotest/testconfiguration.h
@@ -40,6 +40,8 @@ public:
int testCaseCount = 0, QObject *parent = 0);
~TestConfiguration();
+ void setTestCases(const QStringList &testCases);
+ void setTestCaseCount(int count);
void setTargetFile(const QString &targetFile);
void setTargetName(const QString &targetName);
void setProFile(const QString &proFile);
diff --git a/plugins/autotest/testrunner.cpp b/plugins/autotest/testrunner.cpp
index e4b16ccbc4..ed2383bb3f 100644
--- a/plugins/autotest/testrunner.cpp
+++ b/plugins/autotest/testrunner.cpp
@@ -224,7 +224,7 @@ static QString which(const QString &path, const QString &cmd)
paths = path.split(QLatin1Char(':'));
#endif
- foreach (const QString p, paths) {
+ foreach (const QString &p, paths) {
const QString fName = p + QDir::separator() + cmd;
QFileInfo fi(fName);
if (fi.exists() && fi.isExecutable())
diff --git a/plugins/autotest/testtreeitem.cpp b/plugins/autotest/testtreeitem.cpp
index 7fb8b85e19..3b7c27fcde 100644
--- a/plugins/autotest/testtreeitem.cpp
+++ b/plugins/autotest/testtreeitem.cpp
@@ -94,6 +94,10 @@ bool TestTreeItem::modifyContent(const TestTreeItem *modified)
m_line = modified->m_line;
hasBeenModified = true;
}
+ if (m_mainFile != modified->m_mainFile) {
+ m_mainFile = modified->m_mainFile;
+ hasBeenModified = true;
+ }
return hasBeenModified;
}
diff --git a/plugins/autotest/testtreeitem.h b/plugins/autotest/testtreeitem.h
index edda539fd3..eaa1d68656 100644
--- a/plugins/autotest/testtreeitem.h
+++ b/plugins/autotest/testtreeitem.h
@@ -53,6 +53,8 @@ public:
unsigned line() const { return m_line; }
void setColumn(unsigned column) { m_column = column; }
unsigned column() const { return m_column; }
+ QString mainFile() const { return m_mainFile; }
+ void setMainFile(const QString &mainFile) { m_mainFile = mainFile; }
void setChecked(const Qt::CheckState checked);
Qt::CheckState checked() const { return m_checked; }
Type type() const { return m_type; }
@@ -66,6 +68,7 @@ private:
Type m_type;
unsigned m_line;
unsigned m_column;
+ QString m_mainFile;
TestTreeItem *m_parent;
QList<TestTreeItem *> m_children;
};
diff --git a/plugins/autotest/testtreemodel.cpp b/plugins/autotest/testtreemodel.cpp
index 214afb110a..38a38c51f9 100644
--- a/plugins/autotest/testtreemodel.cpp
+++ b/plugins/autotest/testtreemodel.cpp
@@ -43,11 +43,11 @@ 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_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_rootItem->appendChild(m_quickTestRootItem);
m_parser->updateTestTree();
// CppTools::CppModelManagerInterface *cppMM = CppTools::CppModelManagerInterface::instance();
@@ -148,9 +148,11 @@ QVariant TestTreeModel::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole) {
if ((item == m_autoTestRootItem && m_autoTestRootItem->childCount() == 0)
- /*|| (item == m_quickTestRootItem && m_quickTestRootItem->childCount() == 0)*/) {
+ || (item == m_quickTestRootItem && m_quickTestRootItem->childCount() == 0)) {
return QString(item->name() + tr(" (none)"));
} else {
+ if (item->name().isEmpty())
+ return tr("<unnamed>");
return item->name();
}
@@ -158,6 +160,10 @@ QVariant TestTreeModel::data(const QModelIndex &index, int role) const
}
switch(role) {
case Qt::ToolTipRole:
+ if (item->type() == TestTreeItem::TEST_CLASS && item->name().isEmpty())
+ return tr("<p>Unnamed test cases can't be (un)checked - avoid this by assigning a name."
+ "<br/>Having unnamed test cases invalidates the check state of named test "
+ "cases with the same main.cpp when executing selected tests.</p>");
return item->filePath();
case Qt::DecorationRole:
return testTreeIcon(item->type());
@@ -214,8 +220,12 @@ Qt::ItemFlags TestTreeModel::flags(const QModelIndex &index) const
TestTreeItem *item = static_cast<TestTreeItem *>(index.internalPointer());
switch(item->type()) {
case TestTreeItem::TEST_CLASS:
+ if (item->name().isEmpty())
+ return Qt::ItemIsSelectable | Qt::ItemIsTristate;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsTristate | Qt::ItemIsUserCheckable;
case TestTreeItem::TEST_FUNCTION:
+ if (item->parent()->name().isEmpty())
+ return Qt::ItemIsSelectable;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
case TestTreeItem::ROOT:
default:
@@ -245,7 +255,7 @@ bool TestTreeModel::removeRows(int row, int count, const QModelIndex &parent)
bool TestTreeModel::hasTests() const
{
- return m_autoTestRootItem->childCount() > 0 /*|| m_quickTestRootItem->childCount() > 0*/;
+ return m_autoTestRootItem->childCount() > 0 || m_quickTestRootItem->childCount() > 0;
}
static void addProjectInformation(TestConfiguration *config, const QString &filePath)
@@ -265,7 +275,7 @@ static void addProjectInformation(TestConfiguration *config, const QString &file
if (project) {
if (auto target = project->activeTarget()) {
ProjectExplorer::BuildTargetInfoList appTargets = target->applicationTargets();
- foreach (ProjectExplorer::BuildTargetInfo bti, appTargets.list) {
+ foreach (const ProjectExplorer::BuildTargetInfo &bti, appTargets.list) {
if (bti.isValid() && bti.projectFilePath.toString() == proFile) {
targetFile = bti.targetFilePath.toString();
targetName = bti.targetName;
@@ -301,15 +311,44 @@ QList<TestConfiguration *> TestTreeModel::getAllTestCases() const
{
QList<TestConfiguration *> result;
- int count = m_autoTestRootItem->childCount();
- for (int row = 0; row < count; ++row) {
- TestTreeItem *child = m_autoTestRootItem->child(row);
+ // get all Auto Tests
+ for (int row = 0, count = m_autoTestRootItem->childCount(); row < count; ++row) {
+ const TestTreeItem *child = m_autoTestRootItem->child(row);
TestConfiguration *tc = new TestConfiguration(child->name(), QStringList(),
child->childCount());
addProjectInformation(tc, child->filePath());
result << tc;
}
+
+ // get all Quick Tests
+ QMap<QString, int> foundMains;
+ for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) {
+ TestTreeItem *child = m_quickTestRootItem->child(row);
+ // unnamed Quick Tests must be handled separately
+ if (child->name().isEmpty()) {
+ for (int childRow = 0, ccount = child->childCount(); childRow < ccount; ++ childRow) {
+ const TestTreeItem *grandChild = child->child(childRow);
+ const QString mainFile = grandChild->mainFile();
+ foundMains.insert(mainFile, foundMains.contains(mainFile)
+ ? foundMains.value(mainFile) + 1 : 1);
+ }
+ continue;
+ }
+ // named Quick Test
+ const QString mainFile = child->mainFile();
+ foundMains.insert(mainFile, foundMains.contains(mainFile)
+ ? foundMains.value(mainFile) + child->childCount()
+ : child->childCount());
+ }
+ // create TestConfiguration for each main
+ foreach (const QString &mainFile, foundMains.keys()) {
+ TestConfiguration *tc = new TestConfiguration(QString(), QStringList(),
+ foundMains.value(mainFile));
+ addProjectInformation(tc, mainFile);
+ result << tc;
+ }
+
return result;
}
@@ -318,8 +357,7 @@ QList<TestConfiguration *> TestTreeModel::getSelectedTests() const
QList<TestConfiguration *> result;
TestConfiguration *tc;
- int count = m_autoTestRootItem->childCount();
- for (int row = 0; row < count; ++row) {
+ for (int row = 0, count = m_autoTestRootItem->childCount(); row < count; ++row) {
TestTreeItem *child = m_autoTestRootItem->child(row);
switch (child->checked()) {
@@ -332,7 +370,7 @@ QList<TestConfiguration *> TestTreeModel::getSelectedTests() const
continue;
case Qt::PartiallyChecked:
default:
- QString childName = child->name();
+ const QString childName = child->name();
int grandChildCount = child->childCount();
QStringList testCases;
for (int grandChildRow = 0; grandChildRow < grandChildCount; ++grandChildRow) {
@@ -346,23 +384,186 @@ QList<TestConfiguration *> TestTreeModel::getSelectedTests() const
result << tc;
}
}
+ // Quick Tests must be handled differently - need the calling cpp file to use this in
+ // addProjectInformation() - additionally this must be unique to not execute the same executable
+ // on and on and on...
+ // TODO: do this later on for Auto Tests as well to support strange setups? or redo the model
+
+ QMap<QString, TestConfiguration *> foundMains;
+
+ TestTreeItem *unnamed = unnamedQuickTests();
+ for (int childRow = 0, ccount = unnamed->childCount(); childRow < ccount; ++ childRow) {
+ const TestTreeItem *grandChild = unnamed->child(childRow);
+ const QString mainFile = grandChild->mainFile();
+ if (foundMains.contains(mainFile)) {
+ foundMains[mainFile]->setTestCaseCount(tc->testCaseCount() + 1);
+ } else {
+ TestConfiguration *tc = new TestConfiguration(QString(), QStringList());
+ tc->setTestCaseCount(1);
+ addProjectInformation(tc, mainFile);
+ foundMains.insert(mainFile, tc);
+ }
+ }
+
+ for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) {
+ TestTreeItem *child = m_quickTestRootItem->child(row);
+ // unnamed Quick Tests have been handled separately already
+ if (child->name().isEmpty())
+ continue;
+
+ // named Quick Tests
+ switch (child->checked()) {
+ case Qt::Unchecked:
+ continue;
+ case Qt::Checked:
+ case Qt::PartiallyChecked:
+ default:
+ QStringList testFunctions;
+ int grandChildCount = child->childCount();
+ for (int grandChildRow = 0; grandChildRow < grandChildCount; ++grandChildRow) {
+ const TestTreeItem *grandChild = child->child(grandChildRow);
+ if (grandChild->checked() == Qt::Checked)
+ testFunctions << child->name() + QLatin1String("::") + grandChild->name();
+ }
+ TestConfiguration *tc;
+ if (foundMains.contains(child->mainFile())) {
+ tc = foundMains[child->mainFile()];
+ QStringList oldFunctions(tc->testCases());
+ // if oldFunctions.size() is 0 this test configuration is used for at least one
+ // unnamed test case
+ if (oldFunctions.size() == 0) {
+ tc->setTestCaseCount(tc->testCaseCount() + testFunctions.size());
+ } else {
+ oldFunctions << testFunctions;
+ tc->setTestCases(oldFunctions);
+ }
+ } else {
+ tc = new TestConfiguration(QString(), testFunctions);
+ addProjectInformation(tc, child->mainFile());
+ foundMains.insert(child->mainFile(), tc);
+ }
+ break;
+ }
+ }
+
+ foreach (TestConfiguration *config, foundMains.values())
+ result << config;
+
return result;
}
+TestTreeItem *TestTreeModel::unnamedQuickTests() const
+{
+ for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) {
+ TestTreeItem *child = m_quickTestRootItem->child(row);
+ if (child->name().isEmpty())
+ return child;
+ }
+ return 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);
+ modifyTestSubtree(toBeModifiedIndex, newItem);
+}
+
+void TestTreeModel::removeAutoTestSubtreeByFilePath(const QString &file)
+{
+ const QModelIndex atRootIndex = index(0, 0);
+ const int count = rowCount(atRootIndex);
+ for (int row = 0; row < count; ++row) {
+ const QModelIndex childIndex = atRootIndex.child(row, 0);
+ TestTreeItem *childItem = static_cast<TestTreeItem *>(childIndex.internalPointer());
+ if (file == childItem->filePath()) {
+ removeRow(row, atRootIndex);
+ break;
+ }
+ }
+ emit testTreeModelChanged();
+}
+
+void TestTreeModel::addAutoTest(TestTreeItem *newItem)
+{
+ beginInsertRows(index(0, 0), m_autoTestRootItem->childCount(), m_autoTestRootItem->childCount());
+ m_autoTestRootItem->appendChild(newItem);
+ endInsertRows();
+ emit testTreeModelChanged();
+}
+
+void TestTreeModel::removeAllAutoTests()
+{
+ beginResetModel();
+ m_autoTestRootItem->removeChildren();
+ endResetModel();
+ emit testTreeModelChanged();
+}
+
+void TestTreeModel::modifyQuickTestSubtree(int row, TestTreeItem *newItem)
+{
+ QModelIndex toBeModifiedIndex = index(1, 0).child(row, 0);
+ modifyTestSubtree(toBeModifiedIndex, newItem);
+}
+
+void TestTreeModel::removeQuickTestSubtreeByFilePath(const QString &file)
+{
+ const QModelIndex qtRootIndex = index(1, 0);
+ for (int row = 0, count = rowCount(qtRootIndex); row < count; ++row) {
+ const QModelIndex childIndex = qtRootIndex.child(row, 0);
+ const TestTreeItem *childItem = static_cast<TestTreeItem *>(childIndex.internalPointer());
+ if (file == childItem->filePath()) {
+ removeRow(row, qtRootIndex);
+ break;
+ }
+ }
+ emit testTreeModelChanged();
+}
+
+void TestTreeModel::addQuickTest(TestTreeItem *newItem)
+{
+ beginInsertRows(index(1, 0), m_quickTestRootItem->childCount(), m_quickTestRootItem->childCount());
+ m_quickTestRootItem->appendChild(newItem);
+ endInsertRows();
+ emit testTreeModelChanged();
+}
+
+void TestTreeModel::removeAllQuickTests()
+{
+ beginResetModel();
+ m_quickTestRootItem->removeChildren();
+ endResetModel();
+ emit testTreeModelChanged();
+}
+
+void TestTreeModel::removeUnnamedQuickTest(const QString &filePath)
+{
+ TestTreeItem *unnamedQT = unnamedQuickTests();
+ if (!unnamedQT)
+ return;
+
+ const QModelIndex unnamedQTIndex = index(1, 0).child(unnamedQT->row(), 0);
+ for (int childRow = unnamedQT->childCount() - 1; childRow >= 0; --childRow) {
+ const TestTreeItem *child = unnamedQT->child(childRow);
+ if (filePath == child->filePath())
+ removeRow(childRow, unnamedQTIndex);
+ }
+ emit testTreeModelChanged();
+}
+
+void TestTreeModel::modifyTestSubtree(QModelIndex &toBeModifiedIndex, TestTreeItem *newItem)
+{
if (!toBeModifiedIndex.isValid())
return;
+
+ static QVector<int> modificationRoles = QVector<int>() << Qt::DisplayRole
+ << Qt::ToolTipRole << LinkRole;
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();
+ const int childCount = toBeModifiedItem->childCount();
+ const int newChildCount = newItem->childCount();
// for keeping the CheckState on modifications
QHash<QString, Qt::CheckState> originalItems;
@@ -427,36 +628,5 @@ void TestTreeModel::modifyAutoTestSubtree(int row, TestTreeItem *newItem)
emit testTreeModelChanged();
}
-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;
- }
- }
- emit testTreeModelChanged();
-}
-
-void TestTreeModel::addAutoTest(TestTreeItem *newItem)
-{
- beginInsertRows(index(0, 0), m_autoTestRootItem->childCount(), m_autoTestRootItem->childCount());
- m_autoTestRootItem->appendChild(newItem);
- endInsertRows();
- emit testTreeModelChanged();
-}
-
-void TestTreeModel::removeAllAutoTests()
-{
- beginResetModel();
- m_autoTestRootItem->removeChildren();
- endResetModel();
- emit testTreeModelChanged();
-}
-
} // namespace Internal
} // namespace Autotest
diff --git a/plugins/autotest/testtreemodel.h b/plugins/autotest/testtreemodel.h
index 0a07ef0e3a..72ecf88ff4 100644
--- a/plugins/autotest/testtreemodel.h
+++ b/plugins/autotest/testtreemodel.h
@@ -60,11 +60,19 @@ public:
bool hasTests() const;
QList<TestConfiguration *> getAllTestCases() const;
QList<TestConfiguration *> getSelectedTests() const;
+ TestTreeItem *unnamedQuickTests() const;
void modifyAutoTestSubtree(int row, TestTreeItem *newItem);
void removeAutoTestSubtreeByFilePath(const QString &file);
void addAutoTest(TestTreeItem *newItem);
void removeAllAutoTests();
+
+ void modifyQuickTestSubtree(int row, TestTreeItem *newItem);
+ void removeQuickTestSubtreeByFilePath(const QString &file);
+ void addQuickTest(TestTreeItem *newItem);
+ void removeAllQuickTests();
+ void removeUnnamedQuickTest(const QString &filePath);
+
signals:
void testTreeModelChanged();
@@ -72,10 +80,11 @@ public slots:
private:
explicit TestTreeModel(QObject *parent = 0);
+ void modifyTestSubtree(QModelIndex &toBeModifiedIndex, TestTreeItem *newItem);
TestTreeItem *m_rootItem;
TestTreeItem *m_autoTestRootItem;
-// TestTreeItem *m_quickTestRootItem;
+ TestTreeItem *m_quickTestRootItem;
TestCodeParser *m_parser;
};
diff --git a/plugins/autotest/testtreeview.cpp b/plugins/autotest/testtreeview.cpp
index 73d0fbbbd9..3773983289 100644
--- a/plugins/autotest/testtreeview.cpp
+++ b/plugins/autotest/testtreeview.cpp
@@ -32,6 +32,8 @@
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
+#include <qmljstools/qmljsmodelmanager.h>
+
#include <texteditor/texteditor.h>
#include <QToolButton>
@@ -61,11 +63,16 @@ TestTreeViewWidget::TestTreeViewWidget(QWidget *parent) :
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
connect(cppMM, &CppTools::CppModelManager::documentUpdated,
- parser, &TestCodeParser::onDocumentUpdated, Qt::QueuedConnection);
-
+ parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection);
connect(cppMM, &CppTools::CppModelManager::aboutToRemoveFiles,
parser, &TestCodeParser::removeFiles, Qt::QueuedConnection);
+ QmlJS::ModelManagerInterface *qmlJsMM = QmlJSTools::Internal::ModelManager::instance();
+ connect(qmlJsMM, &QmlJS::ModelManagerInterface::documentUpdated,
+ parser, &TestCodeParser::onQmlDocumentUpdated, Qt::QueuedConnection);
+ connect(qmlJsMM, &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
+ parser, &TestCodeParser::removeFiles, Qt::QueuedConnection);
+
connect(m_view, &TestTreeView::activated, this, &TestTreeViewWidget::onItemActivated);
}
@@ -208,28 +215,32 @@ void TestTreeView::deselectAll()
void TestTreeView::selectOrDeselectAll(const Qt::CheckState checkState)
{
const 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) {
- const 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)
+
+ // 2 == Auto Tests and Quick Tests - must be raised if there will be others
+ for (int rootRow = 0; rootRow < 2; ++rootRow) {
+ QModelIndex currentRootIndex = model->index(rootRow, 0, rootIndex());
+ if (!currentRootIndex.isValid())
+ return;
+ int count = model->rowCount(currentRootIndex);
+ QModelIndex last;
+ for (int classesRow = 0; classesRow < count; ++classesRow) {
+ const QModelIndex classesIndex = model->index(classesRow, 0, currentRootIndex);
+ int funcCount = model->rowCount(classesIndex);
+ TestTreeItem *item = static_cast<TestTreeItem *>(classesIndex.internalPointer());
+ if (item) {
item->setChecked(checkState);
+ if (!item->childCount())
+ last = classesIndex;
+ }
+ for (int functionRow = 0; functionRow < funcCount; ++functionRow) {
+ last = model->index(functionRow, 0, classesIndex);
+ TestTreeItem *item = static_cast<TestTreeItem *>(last.internalPointer());
+ if (item)
+ item->setChecked(checkState);
+ }
}
+ emit dataChanged(currentRootIndex, last);
}
- emit dataChanged(autoTestsIndex, last);
}
} // namespace Internal
diff --git a/plugins/autotest/testvisitor.cpp b/plugins/autotest/testvisitor.cpp
index e1c4fda65b..199f3236b2 100644
--- a/plugins/autotest/testvisitor.cpp
+++ b/plugins/autotest/testvisitor.cpp
@@ -26,11 +26,15 @@
#include <cpptools/cppmodelmanager.h>
+#include <qmljs/parser/qmljsast_p.h>
+
#include <QList>
namespace Autotest {
namespace Internal {
+/************************** Cpp Test Symbol Visitor ***************************/
+
TestVisitor::TestVisitor(const QString &fullQualifiedClassName)
: m_className(fullQualifiedClassName)
{
@@ -59,10 +63,11 @@ bool TestVisitor::visit(CPlusPlus::Class *symbol)
if (className != m_className)
continue;
- if (auto func = type->asFunctionType()) {
+ if (const auto func = type->asFunctionType()) {
if (func->isSlot() && member->isPrivate()) {
const QString name = o.prettyName(func->name());
if (!ignoredFunctions.contains(name) && !name.endsWith(QLatin1String("_data"))) {
+ // TODO use definition of function instead of declaration!
TestCodeLocation location;
location.m_fileName = QLatin1String(member->fileName());
location.m_line = member->line();
@@ -75,6 +80,8 @@ bool TestVisitor::visit(CPlusPlus::Class *symbol)
return true;
}
+/**************************** Cpp Test AST Visitor ****************************/
+
TestAstVisitor::TestAstVisitor(CPlusPlus::Document::Ptr doc)
: ASTVisitor(doc->translationUnit()),
m_currentDoc(doc)
@@ -90,15 +97,15 @@ bool TestAstVisitor::visit(CPlusPlus::CallAST *ast)
if (!m_currentScope || m_currentDoc.isNull())
return false;
- if (auto expressionAST = ast->base_expression) {
- if (auto idExpressionAST = expressionAST->asIdExpression()) {
- if (auto qualifiedNameAST = idExpressionAST->name->asQualifiedName()) {
+ if (const auto expressionAST = ast->base_expression) {
+ if (const auto idExpressionAST = expressionAST->asIdExpression()) {
+ if (const auto qualifiedNameAST = idExpressionAST->name->asQualifiedName()) {
const CPlusPlus::Overview o;
const QString prettyName = o.prettyName(qualifiedNameAST->name);
if (prettyName == QLatin1String("QTest::qExec")) {
- if (auto expressionListAST = ast->expression_list) {
+ if (const auto expressionListAST = ast->expression_list) {
// first argument is the one we need
- if (auto argumentExpressionAST = expressionListAST->value) {
+ if (const auto argumentExpressionAST = expressionListAST->value) {
CPlusPlus::TypeOfExpression toe;
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
toe.init(m_currentDoc, cppMM->snapshot());
@@ -106,7 +113,7 @@ bool TestAstVisitor::visit(CPlusPlus::CallAST *ast)
= toe(argumentExpressionAST, m_currentDoc, m_currentScope);
if (toeItems.size()) {
- if (auto pointerType = toeItems.first().type()->asPointerType())
+ if (const auto pointerType = toeItems.first().type()->asPointerType())
m_className = o.prettyType(pointerType->elementType());
}
}
@@ -124,5 +131,63 @@ bool TestAstVisitor::visit(CPlusPlus::CompoundStatementAST *ast)
return true;
}
+/*************************** Quick Test AST Visitor ***************************/
+
+TestQmlVisitor::TestQmlVisitor(QmlJS::Document::Ptr doc)
+ : m_currentDoc(doc)
+{
+}
+
+TestQmlVisitor::~TestQmlVisitor()
+{
+}
+
+bool TestQmlVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
+{
+ const QStringRef name = ast->qualifiedTypeNameId->name;
+ if (name != QLatin1String("TestCase"))
+ return false;
+
+ m_currentTestCaseName.clear();
+ const auto sourceLocation = ast->firstSourceLocation();
+ m_testCaseLocation.m_fileName = m_currentDoc->fileName();
+ m_testCaseLocation.m_line = sourceLocation.startLine;
+ m_testCaseLocation.m_column = sourceLocation.startColumn - 1;
+ return true;
+}
+
+bool TestQmlVisitor::visit(QmlJS::AST::ExpressionStatement *ast)
+{
+ const QmlJS::AST::ExpressionNode *expr = ast->expression;
+ return expr->kind == QmlJS::AST::Node::Kind_StringLiteral;
+}
+
+bool TestQmlVisitor::visit(QmlJS::AST::UiScriptBinding *ast)
+{
+ const QStringRef name = ast->qualifiedId->name;
+ return name == QLatin1String("name");
+}
+
+bool TestQmlVisitor::visit(QmlJS::AST::FunctionDeclaration *ast)
+{
+ const QStringRef name = ast->name;
+ if (name.startsWith(QLatin1String("test_"))) {
+ const auto sourceLocation = ast->firstSourceLocation();
+ TestCodeLocation location;
+ location.m_fileName = m_currentDoc->fileName();
+ location.m_line = sourceLocation.startLine;
+ location.m_column = sourceLocation.startColumn - 1;
+
+ m_testFunctions.insert(name.toString(), location);
+ }
+ return false;
+}
+
+bool TestQmlVisitor::visit(QmlJS::AST::StringLiteral *ast)
+{
+ m_currentTestCaseName = ast->value.toString();
+ return false;
+}
+
} // namespace Internal
} // namespace Autotest
diff --git a/plugins/autotest/testvisitor.h b/plugins/autotest/testvisitor.h
index 04076cf80c..92bfa009ae 100644
--- a/plugins/autotest/testvisitor.h
+++ b/plugins/autotest/testvisitor.h
@@ -24,6 +24,9 @@
#include <cplusplus/Scope.h>
#include <cplusplus/SymbolVisitor.h>
+#include <qmljs/parser/qmljsastvisitor_p.h>
+#include <qmljs/qmljsdocument.h>
+
#include <QMap>
#include <QString>
@@ -69,6 +72,30 @@ private:
};
+class TestQmlVisitor : public QmlJS::AST::Visitor
+{
+public:
+ TestQmlVisitor(QmlJS::Document::Ptr doc);
+ virtual ~TestQmlVisitor();
+
+ bool visit(QmlJS::AST::UiObjectDefinition *ast);
+ bool visit(QmlJS::AST::ExpressionStatement *ast);
+ bool visit(QmlJS::AST::UiScriptBinding *ast);
+ bool visit(QmlJS::AST::FunctionDeclaration *ast);
+ bool visit(QmlJS::AST::StringLiteral *ast);
+
+ QString testCaseName() const { return m_currentTestCaseName; }
+ TestCodeLocation testCaseLocation() const { return m_testCaseLocation; }
+ QMap<QString, TestCodeLocation> testFunctions() const { return m_testFunctions; }
+
+private:
+ QmlJS::Document::Ptr m_currentDoc;
+ QString m_currentTestCaseName;
+ TestCodeLocation m_testCaseLocation;
+ QMap<QString, TestCodeLocation> m_testFunctions;
+
+};
+
} // namespace Internal
} // namespace Autotest