diff options
author | Christian Stenger <christian.stenger@theqtcompany.com> | 2015-02-05 16:07:45 +0100 |
---|---|---|
committer | Christian Stenger <christian.stenger@theqtcompany.com> | 2015-02-16 15:43:02 +0200 |
commit | 2ce99680b20802a370e6d20a1467a1d41fbee690 (patch) | |
tree | 66d68ac3cb3231b121e90cb3be683da1b2e8b63a /plugins | |
parent | 61c50e4aa343387b56192a34db6384d1dfcfa3c5 (diff) | |
download | qt-creator-2ce99680b20802a370e6d20a1467a1d41fbee690.tar.gz |
Decouple code parser and model
Additionally some minor refactorings to increase readability.
Change-Id: I0be120fcedcf31dbb0116d84f0d3c23cf95e7d91
Reviewed-by: Andre Poenitz <andre.poenitz@theqtcompany.com>
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/autotest/testcodeparser.cpp | 297 | ||||
-rw-r--r-- | plugins/autotest/testcodeparser.h | 28 | ||||
-rw-r--r-- | plugins/autotest/testtreeitem.cpp | 2 | ||||
-rw-r--r-- | plugins/autotest/testtreeitem.h | 9 | ||||
-rw-r--r-- | plugins/autotest/testtreemodel.cpp | 329 | ||||
-rw-r--r-- | plugins/autotest/testtreemodel.h | 40 | ||||
-rw-r--r-- | plugins/autotest/testvisitor.h | 7 |
7 files changed, 366 insertions, 346 deletions
diff --git a/plugins/autotest/testcodeparser.cpp b/plugins/autotest/testcodeparser.cpp index 179698294f..510ce09de2 100644 --- a/plugins/autotest/testcodeparser.cpp +++ b/plugins/autotest/testcodeparser.cpp @@ -19,8 +19,6 @@ #include "autotestconstants.h" #include "testcodeparser.h" #include "testinfo.h" -#include "testtreeitem.h" -#include "testtreemodel.h" #include "testvisitor.h" #include <coreplugin/progressmanager/progressmanager.h> @@ -41,6 +39,7 @@ #include <qmljs/qmljsdialect.h> #include <qmljstools/qmljsmodelmanager.h> +#include <utils/qtcassert.h> #include <utils/textfileformat.h> namespace Autotest { @@ -89,8 +88,7 @@ void TestCodeParser::updateTestTree() qDebug("updating TestTreeModel"); clearMaps(); - m_model->removeAllAutoTests(); - m_model->removeAllQuickTests(); + emit cacheCleared(); if (ProjectExplorer::Project *project = currentProject()) { if (auto qmakeProject = qobject_cast<QmakeProjectManager::QmakeProject *>(project)) { @@ -316,25 +314,24 @@ static CPlusPlus::Document::Ptr declaringDocument(CPlusPlus::Document::Ptr doc, return declaringDoc; } -static TestTreeItem *constructTestTreeItem(const QString &fileName, - const QString &mainFile, // used for Quick Tests only - const QString &testCaseName, - int line, int column, - const QMap<QString, TestCodeLocationAndType> functions, - TestTreeItem *rootItem) +static TestTreeItem constructTestTreeItem(const QString &fileName, + const QString &mainFile, // used for Quick Tests only + const QString &testCaseName, + int line, int column, + const QMap<QString, TestCodeLocationAndType> functions) { - TestTreeItem *treeItem = new TestTreeItem(testCaseName, fileName, TestTreeItem::TEST_CLASS, rootItem); - treeItem->setMainFile(mainFile); // used for Quick Tests only - treeItem->setLine(line); - treeItem->setColumn(column); + TestTreeItem treeItem(testCaseName, fileName, TestTreeItem::TEST_CLASS); + treeItem.setMainFile(mainFile); // used for Quick Tests only + treeItem.setLine(line); + treeItem.setColumn(column); foreach (const QString &functionName, functions.keys()) { const TestCodeLocationAndType locationAndType = functions.value(functionName); TestTreeItem *treeItemChild = new TestTreeItem(functionName, locationAndType.m_fileName, - locationAndType.m_type, treeItem); + locationAndType.m_type, &treeItem); treeItemChild->setLine(locationAndType.m_line); treeItemChild->setColumn(locationAndType.m_column); - treeItem->appendChild(treeItemChild); + treeItem.appendChild(treeItemChild); } return treeItem; } @@ -372,13 +369,9 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr document) visitor.accept(declaringDoc->globalNamespace()); const QMap<QString, TestCodeLocationAndType> testFunctions = visitor.privateSlots(); - const QModelIndex autoTestRootIndex = m_model->index(0, 0); - TestTreeItem *autoTestRootItem = static_cast<TestTreeItem *>(autoTestRootIndex.internalPointer()); - - TestTreeItem *ttItem = constructTestTreeItem(declaringDoc->fileName(), QString(), - testCaseName, line, column, testFunctions, - autoTestRootItem); - updateModelAndCppDocMap(document, declaringDoc->fileName(), ttItem, autoTestRootItem); + TestTreeItem item = constructTestTreeItem(declaringDoc->fileName(), QString(), + testCaseName, line, column, testFunctions); + updateModelAndCppDocMap(document, declaringDoc->fileName(), item); } else { // could not find the class to test, but QTest is included and QT_TESTLIB_LIB defined // maybe file is only a referenced file @@ -388,8 +381,8 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr document) if (snapshot.contains(info.referencingFile())) { checkDocumentForTestCode(snapshot.find(info.referencingFile()).value()); } else { // no referencing file too, so this test case is no more a test case - m_model->removeAutoTestSubtreeByFilePath(fileName); m_cppDocMap.remove(fileName); + emit testItemsRemoved(fileName, TestTreeModel::AutoTest); } } } @@ -403,21 +396,15 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document) if (quickTestName(document).isEmpty()) return; - const QString fileName = document->fileName(); - const QString srcDir = quickTestSrcDir(modelManager, fileName); + const QString cppFileName = document->fileName(); + const QString srcDir = quickTestSrcDir(modelManager, cppFileName); if (srcDir.isEmpty()) return; - const QModelIndex quickTestRootIndex = m_model->index(1, 0); - TestTreeItem *quickTestRootItem = static_cast<TestTreeItem *>(quickTestRootIndex.internalPointer()); - const QList<QmlJS::Document::Ptr> qmlDocs = scanDirectoryForQuickTestQmlFiles(srcDir); foreach (const QmlJS::Document::Ptr &qmlJSDoc, qmlDocs) { QmlJS::AST::Node *ast = qmlJSDoc->ast(); - if (!ast) { - qDebug() << "ast is zero pointer" << qmlJSDoc->fileName(); // should not happen - continue; - } + QTC_ASSERT(ast, continue); TestQmlVisitor qmlVisitor(qmlJSDoc); QmlJS::AST::Node::accept(ast, &qmlVisitor); @@ -426,24 +413,18 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document) const QMap<QString, TestCodeLocationAndType> testFunctions = qmlVisitor.testFunctions(); if (testCaseName.isEmpty()) { - // remove found test functions before re-adding them - removeUnnamedQuickTests(qmlJSDoc->fileName(), testFunctions.keys()); - // re-create TestTreeItem for unnamed Quick Tests - recreateUnnamedQuickTest(testFunctions, fileName, quickTestRootItem); + updateUnnamedQuickTests(qmlJSDoc->fileName(), cppFileName, testFunctions); continue; } // end of handling test cases without name property // construct new/modified TestTreeItem - TestTreeItem *testTreeItem - = constructTestTreeItem(tcLocationAndType.m_fileName, fileName, testCaseName, + TestTreeItem testTreeItem + = constructTestTreeItem(tcLocationAndType.m_fileName, cppFileName, testCaseName, tcLocationAndType.m_line, tcLocationAndType.m_column, - testFunctions, quickTestRootItem); + testFunctions); // update model and internal map - const QmlJS::Document::Ptr qmlDoc = - QmlJSTools::Internal::ModelManager::instance()->snapshot().document(tcLocationAndType.m_fileName); - updateModelAndQuickDocMap(qmlDoc, qmlJSDoc->fileName(), fileName, testTreeItem, - quickTestRootItem); + updateModelAndQuickDocMap(qmlJSDoc, cppFileName, testTreeItem); } } @@ -489,15 +470,9 @@ void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document) 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; - } - } + const QString &mainFile = m_model->getMainFileForUnnamedQuickTest(fileName); + if (!mainFile.isEmpty() && snapshot.contains(mainFile)) + checkDocumentForTestCode(snapshot.document(mainFile)); } void TestCodeParser::removeFiles(const QStringList &files) @@ -537,7 +512,7 @@ void TestCodeParser::removeTestsIfNecessary(const QString &fileName) // check if this file was listed before and remove if necessary (switched config,...) if (m_cppDocMap.contains(fileName)) { m_cppDocMap.remove(fileName); - m_model->removeAutoTestSubtreeByFilePath(fileName); + emit testItemsRemoved(fileName, TestTreeModel::AutoTest); } else { // handle Qt Quick Tests QList<QString> toBeRemoved; foreach (const QString &file, m_quickDocMap.keys()) { @@ -551,21 +526,15 @@ void TestCodeParser::removeTestsIfNecessary(const QString &fileName) } foreach (const QString &file, toBeRemoved) { m_quickDocMap.remove(file); - m_model->removeQuickTestSubtreeByFilePath(file); + emit testItemsRemoved(file, TestTreeModel::QuickTest); } // unnamed Quick Tests must be handled separately QSet<QString> filePaths; QList<QString> functionNames; - if (TestTreeItem *unnamedQT = m_model->unnamedQuickTests()) { - for (int i = 0; i < unnamedQT->childCount(); ++i) { - const TestTreeItem *child = unnamedQT->child(i); - if (child->mainFile() == fileName) { - filePaths.insert(child->filePath()); - functionNames.append(child->name()); - } - } + if (m_model->hasUnnamedQuickTests()) { + m_model->qmlFilesAndFunctionNamesForMainFile(fileName, &filePaths, &functionNames); foreach (const QString &file, filePaths) - m_model->removeUnnamedQuickTests(file); + emit testItemsRemoved(file, TestTreeModel::QuickTest); // update info map TestInfo unnamedInfo = m_quickDocMap[tr(Constants::UNNAMED_QUICKTESTS)]; QStringList functions = unnamedInfo.testFunctions(); @@ -576,6 +545,8 @@ void TestCodeParser::removeTestsIfNecessary(const QString &fileName) m_quickDocMap.remove(tr(Constants::UNNAMED_QUICKTESTS)); else m_quickDocMap.insert(tr(Constants::UNNAMED_QUICKTESTS), unnamedInfo); + } else { + m_quickDocMap.remove(tr(Constants::UNNAMED_QUICKTESTS)); } } } @@ -589,7 +560,7 @@ void TestCodeParser::removeTestsIfNecessaryByProFile(const QString &proFile) } foreach (const QString &fileName, fList) { m_cppDocMap.remove(fileName); - m_model->removeAutoTestSubtreeByFilePath(fileName); + emit testItemsRemoved(fileName, TestTreeModel::AutoTest); } fList.clear(); foreach (const QString &fileName, m_quickDocMap.keys()) { @@ -598,105 +569,23 @@ void TestCodeParser::removeTestsIfNecessaryByProFile(const QString &proFile) } foreach (const QString &fileName, fList) { m_quickDocMap.remove(fileName); - m_model->removeQuickTestSubtreeByFilePath(fileName); + emit testItemsRemoved(fileName, TestTreeModel::QuickTest); } // handle unnamed Quick Tests - fList.clear(); // will now be re-used as function names storage - QSet<QString> filePaths; - CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); - if (TestTreeItem *unnamedQT = m_model->unnamedQuickTests()) { - for (int i = 0; i < unnamedQT->childCount(); ++i) { - const TestTreeItem *child = unnamedQT->child(i); - QList<CppTools::ProjectPart::Ptr> ppList = cppMM->projectPart(child->mainFile()); - if (ppList.size() && ppList.at(0)->projectFile == proFile) { - filePaths.insert(child->filePath()); - fList.append(child->name()); - } - } - } - foreach (const QString &filePath, filePaths) { - m_model->removeUnnamedQuickTests(filePath); - } + const QSet<QString> &filePaths = m_model->qmlFilesForProFile(proFile); + foreach (const QString &fileName, filePaths) + emit unnamedQuickTestsRemoved(fileName); // update info map - TestInfo unnamedInfo = m_quickDocMap[tr(Constants::UNNAMED_QUICKTESTS)]; - QStringList functions = unnamedInfo.testFunctions(); - foreach (const QString &function, fList) - functions.removeOne(function); - unnamedInfo.setTestFunctions(functions); - if (functions.size() == 0) + TestInfo unnamedInfo = m_quickDocMap.value(tr(Constants::UNNAMED_QUICKTESTS), + TestInfo(QString(), QStringList(), 666)); + + unnamedInfo.setTestFunctions(m_model->getUnnamedQuickTestFunctions()); + if (unnamedInfo.testFunctions().isEmpty()) m_quickDocMap.remove(tr(Constants::UNNAMED_QUICKTESTS)); else m_quickDocMap.insert(tr(Constants::UNNAMED_QUICKTESTS), unnamedInfo); } -void TestCodeParser::removeUnnamedQuickTests(const QString &fileName, - const QStringList &testFunctions) -{ - // if this test case was named before remove it - if (m_quickDocMap.contains(fileName)) { - m_model->removeQuickTestSubtreeByFilePath(fileName); - m_quickDocMap.remove(fileName); - } - - if (m_model->unnamedQuickTests()) { - // remove unnamed quick tests that are already found for this qml file - if (m_model->removeUnnamedQuickTests(fileName)) { - // make sure m_quickDocMap does not have a inconsistent state now - TestInfo testInfo = m_quickDocMap[tr(Constants::UNNAMED_QUICKTESTS)]; - QStringList testFunctionNames = testInfo.testFunctions(); - foreach (const QString &func, testFunctions) - testFunctionNames.removeOne(func); - testInfo.setTestFunctions(testFunctionNames); - if (testFunctionNames.size() == 0) - m_quickDocMap.remove(tr(Constants::UNNAMED_QUICKTESTS)); - else - m_quickDocMap.insert(tr(Constants::UNNAMED_QUICKTESTS), testInfo); - } - } -} - -void TestCodeParser::recreateUnnamedQuickTest(const QMap<QString, TestCodeLocationAndType> &testFunctions, - const QString &mainFile, TestTreeItem *rootItem) -{ - TestTreeItem *testTreeItem = new TestTreeItem(QString(), QString(), TestTreeItem::TEST_CLASS, - rootItem); - TestTreeItem *unnamedQTItem = m_model->unnamedQuickTests(); - // if there are still other unnamed Quick Tests re-parent them to the new - if (unnamedQTItem) { - for (int i = 0, count = unnamedQTItem->childCount(); i < count; ++i) { - TestTreeItem *child = new TestTreeItem(*unnamedQTItem->child(i)); - child->setParent(testTreeItem); - testTreeItem->appendChild(child); - } - } - // add test functions of the current - foreach (const QString &function, testFunctions.keys()) { - const TestCodeLocationAndType locationAndType = testFunctions.value(function); - TestTreeItem *testTreeFunction = new TestTreeItem(function, locationAndType.m_fileName, - locationAndType.m_type, testTreeItem); - testTreeFunction->setLine(locationAndType.m_line); - testTreeFunction->setColumn(locationAndType.m_column); - testTreeFunction->setMainFile(mainFile); - testTreeItem->appendChild(testTreeFunction); - } - - TestInfo info = m_quickDocMap.contains(tr(Constants::UNNAMED_QUICKTESTS)) - ? m_quickDocMap[tr(Constants::UNNAMED_QUICKTESTS)] - : TestInfo(QString(), QStringList(), 666); - QStringList originalFunctions(info.testFunctions()); - foreach (const QString &function, testFunctions.keys()) - originalFunctions.append(function); - info.setTestFunctions(originalFunctions); - - if (unnamedQTItem) { - m_model->modifyQuickTestSubtree(unnamedQTItem->row(), testTreeItem); - delete testTreeItem; - } else { - m_model->addQuickTest(testTreeItem); - } - m_quickDocMap.insert(tr(Constants::UNNAMED_QUICKTESTS), info); -} - void TestCodeParser::onTaskStarted(Core::Id type) { if (type != CppTools::Constants::TASK_INDEX @@ -715,13 +604,31 @@ void TestCodeParser::onAllTasksFinished(Core::Id type) updateTestTree(); } +void TestCodeParser::updateUnnamedQuickTests(const QString &fileName, const QString &mainFile, + const QMap<QString, TestCodeLocationAndType> &functions) +{ + // if this test case was named before remove it + m_quickDocMap.remove(fileName); + emit testItemsRemoved(fileName, TestTreeModel::QuickTest); + + emit unnamedQuickTestsUpdated(fileName, mainFile, functions); + + if (m_model->hasUnnamedQuickTests()) { + TestInfo info = m_quickDocMap.value(tr(Constants::UNNAMED_QUICKTESTS), + TestInfo(QString(), QStringList(), 666)); + info.setTestFunctions(m_model->getUnnamedQuickTestFunctions()); + m_quickDocMap.insert(tr(Constants::UNNAMED_QUICKTESTS), info); + } else { + m_quickDocMap.remove(tr(Constants::UNNAMED_QUICKTESTS)); + } +} + void TestCodeParser::updateModelAndCppDocMap(CPlusPlus::Document::Ptr document, - const QString &declFileName, - TestTreeItem *testItem, TestTreeItem *rootItem) + const QString &declaringFile, TestTreeItem &testItem) { const CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); const QString fileName = document->fileName(); - const QString testCaseName = testItem->name(); + const QString testCaseName = testItem.name(); QString proFile; const QList<CppTools::ProjectPart::Ptr> ppList = cppMM->projectPart(fileName); if (ppList.size()) @@ -729,71 +636,57 @@ void TestCodeParser::updateModelAndCppDocMap(CPlusPlus::Document::Ptr document, if (m_cppDocMap.contains(fileName)) { QStringList files = QStringList() << fileName; - if (fileName != declFileName) - files << declFileName; + if (fileName != declaringFile) + files << declaringFile; foreach (const QString &file, files) { - const bool setReferencingFile = (files.size() == 2 && file == declFileName); - const int count = rootItem->childCount(); - for (int i = 0; i < count; ++i) { - TestTreeItem *currentItem = rootItem->child(i); - if (currentItem->filePath() == file) { - m_model->modifyAutoTestSubtree(i, testItem); - TestInfo testInfo(testCaseName, testItem->getChildNames(), - document->revision(), document->editorRevision()); - testInfo.setProfile(proFile); - if (setReferencingFile) - testInfo.setReferencingFile(fileName); - m_cppDocMap.insert(file, testInfo); - break; - } - } + const bool setReferencingFile = (files.size() == 2 && file == declaringFile); + emit testItemModified(testItem, TestTreeModel::AutoTest, file); + TestInfo testInfo(testCaseName, testItem.getChildNames(), + document->revision(), document->editorRevision()); + testInfo.setProfile(proFile); + if (setReferencingFile) + testInfo.setReferencingFile(fileName); + m_cppDocMap.insert(file, testInfo); } - delete testItem; // this item is no more needed as model updates the original with its content } else { - m_model->addAutoTest(testItem); - TestInfo ti(testCaseName, testItem->getChildNames(), + emit testItemCreated(testItem, TestTreeModel::AutoTest); + TestInfo ti(testCaseName, testItem.getChildNames(), document->revision(), document->editorRevision()); ti.setProfile(proFile); m_cppDocMap.insert(fileName, ti); - if (declFileName != fileName) { + if (declaringFile != fileName) { ti.setReferencingFile(fileName); - m_cppDocMap.insert(declFileName, ti); + m_cppDocMap.insert(declaringFile, ti); } } } -void TestCodeParser::updateModelAndQuickDocMap(QmlJS::Document::Ptr qmlDoc, const QString ¤tQmlJSFile, - const QString &referencingFileName, - TestTreeItem *testItem, TestTreeItem *rootItem) +void TestCodeParser::updateModelAndQuickDocMap(QmlJS::Document::Ptr document, + const QString &referencingFile, + TestTreeItem &testItem) { const CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); - const QString fileName = qmlDoc->fileName(); + const QString fileName = document->fileName(); QString proFile; - QList<CppTools::ProjectPart::Ptr> ppList = cppMM->projectPart(referencingFileName); + QList<CppTools::ProjectPart::Ptr> ppList = cppMM->projectPart(referencingFile); if (ppList.size()) proFile = ppList.at(0)->projectFile; if (m_quickDocMap.contains(fileName)) { - for (int i = 0; i < rootItem->childCount(); ++i) { - if (rootItem->child(i)->filePath() == fileName) { - m_model->modifyQuickTestSubtree(i, testItem); - TestInfo testInfo(testItem->name(), testItem->getChildNames(), 0, qmlDoc->editorRevision()); - testInfo.setReferencingFile(referencingFileName); - testInfo.setProfile(proFile); - m_quickDocMap.insert(fileName, testInfo); - break; - } - } - delete testItem; // this item is no more needed as model updates the original with its content + emit testItemModified(testItem, TestTreeModel::QuickTest, fileName); + TestInfo testInfo(testItem.name(), testItem.getChildNames(), 0, document->editorRevision()); + testInfo.setReferencingFile(referencingFile); + testInfo.setProfile(proFile); + m_quickDocMap.insert(fileName, testInfo); } else { // if it was formerly unnamed remove the respective items - removeUnnamedQuickTests(currentQmlJSFile, testItem->getChildNames()); + emit unnamedQuickTestsRemoved(fileName); - m_model->addQuickTest(testItem); - TestInfo testInfo(testItem->name(), testItem->getChildNames(), 0, qmlDoc->editorRevision()); - testInfo.setReferencingFile(referencingFileName); + emit testItemCreated(testItem, TestTreeModel::QuickTest); + TestInfo testInfo(testItem.name(), testItem.getChildNames(), 0, document->editorRevision()); + testInfo.setReferencingFile(referencingFile); testInfo.setProfile(proFile); - m_quickDocMap.insert(testItem->filePath(), testInfo); + m_quickDocMap.insert(testItem.filePath(), testInfo); } } diff --git a/plugins/autotest/testcodeparser.h b/plugins/autotest/testcodeparser.h index 2c83433f0a..e2f590cabf 100644 --- a/plugins/autotest/testcodeparser.h +++ b/plugins/autotest/testcodeparser.h @@ -19,6 +19,9 @@ #ifndef TESTCODEPARSER_H #define TESTCODEPARSER_H +#include "testtreeitem.h" +#include "testtreemodel.h" + #include <cplusplus/CppDocument.h> #include <qmljs/qmljsdocument.h> @@ -35,8 +38,6 @@ namespace Internal { struct TestCodeLocationAndType; class TestInfo; -class TestTreeModel; -class TestTreeItem; class TestCodeParser : public QObject { @@ -46,6 +47,14 @@ public: virtual ~TestCodeParser(); signals: + void cacheCleared(); + void testItemCreated(const TestTreeItem &item, TestTreeModel::Type type); + void testItemsCreated(const QList<TestTreeItem> &itemList, TestTreeModel::Type type); + void testItemModified(TestTreeItem tItem, TestTreeModel::Type type, const QString &file); + void testItemsRemoved(const QString &filePath, TestTreeModel::Type type); + void unnamedQuickTestsUpdated(const QString &filePath, const QString &mainFile, + const QMap<QString, TestCodeLocationAndType> &functions); + void unnamedQuickTestsRemoved(const QString &filePath); public slots: void emitUpdateTestTree(); @@ -63,16 +72,15 @@ private: void clearMaps(); void removeTestsIfNecessary(const QString &fileName); void removeTestsIfNecessaryByProFile(const QString &proFile); - void removeUnnamedQuickTests(const QString &fileName, const QStringList &testFunctions); - void recreateUnnamedQuickTest(const QMap<QString, TestCodeLocationAndType> &testFunctions, - const QString &mainFile, TestTreeItem *rootItem); + void onTaskStarted(Core::Id type); void onAllTasksFinished(Core::Id type); - void updateModelAndCppDocMap(CPlusPlus::Document::Ptr document, const QString &declFileName, - TestTreeItem *testItem, TestTreeItem *rootItem); - void updateModelAndQuickDocMap(QmlJS::Document::Ptr qmlDoc, const QString ¤tQmlJSFile, - const QString &referencingFileName, - TestTreeItem *testItem, TestTreeItem *rootItem); + void updateUnnamedQuickTests(const QString &fileName, const QString &mainFile, + const QMap<QString, TestCodeLocationAndType> &functions); + void updateModelAndCppDocMap(CPlusPlus::Document::Ptr document, + const QString &declaringFile, TestTreeItem &testItem); + void updateModelAndQuickDocMap(QmlJS::Document::Ptr document, + const QString &referencingFile, TestTreeItem &testItem); TestTreeModel *m_model; QMap<QString, TestInfo> m_cppDocMap; diff --git a/plugins/autotest/testtreeitem.cpp b/plugins/autotest/testtreeitem.cpp index ae6ab2e671..f377118313 100644 --- a/plugins/autotest/testtreeitem.cpp +++ b/plugins/autotest/testtreeitem.cpp @@ -71,7 +71,7 @@ TestTreeItem::TestTreeItem(const TestTreeItem &other) } } -TestTreeItem *TestTreeItem::child(int row) +TestTreeItem *TestTreeItem::child(int row) const { return m_children.at(row); } diff --git a/plugins/autotest/testtreeitem.h b/plugins/autotest/testtreeitem.h index 411839a9bb..25a49f3edf 100644 --- a/plugins/autotest/testtreeitem.h +++ b/plugins/autotest/testtreeitem.h @@ -41,7 +41,7 @@ public: virtual ~TestTreeItem(); TestTreeItem(const TestTreeItem& other); - TestTreeItem *child(int row); + TestTreeItem *child(int row) const; TestTreeItem *parent() const; void appendChild(TestTreeItem *child); int row() const; @@ -80,6 +80,13 @@ private: QList<TestTreeItem *> m_children; }; +struct TestCodeLocationAndType { + QString m_fileName; + unsigned m_line; + unsigned m_column; + TestTreeItem::Type m_type; +}; + } // namespace Internal } // namespace Autotest diff --git a/plugins/autotest/testtreemodel.cpp b/plugins/autotest/testtreemodel.cpp index 9c8d235a04..f2ea94d626 100644 --- a/plugins/autotest/testtreemodel.cpp +++ b/plugins/autotest/testtreemodel.cpp @@ -50,6 +50,17 @@ TestTreeModel::TestTreeModel(QObject *parent) : { m_rootItem->appendChild(m_autoTestRootItem); m_rootItem->appendChild(m_quickTestRootItem); + + connect(m_parser, &TestCodeParser::cacheCleared, this, &TestTreeModel::removeAllTestItems); + connect(m_parser, &TestCodeParser::testItemCreated, this, &TestTreeModel::addTestTreeItem); + connect(m_parser, &TestCodeParser::testItemsCreated, this, &TestTreeModel::addTestTreeItems); + connect(m_parser, &TestCodeParser::testItemModified, this, &TestTreeModel::modifyTestTreeItem); + connect(m_parser, &TestCodeParser::testItemsRemoved, this, &TestTreeModel::removeTestTreeItems); + connect(m_parser, &TestCodeParser::unnamedQuickTestsUpdated, + this, &TestTreeModel::updateUnnamedQuickTest); + connect(m_parser, &TestCodeParser::unnamedQuickTestsRemoved, + this, &TestTreeModel::removeUnnamedQuickTests); + m_parser->updateTestTree(); // CppTools::CppModelManagerInterface *cppMM = CppTools::CppModelManagerInterface::instance(); @@ -524,6 +535,62 @@ QList<TestConfiguration *> TestTreeModel::getSelectedTests() const return result; } +QString TestTreeModel::getMainFileForUnnamedQuickTest(const QString &qmlFile) const +{ + const TestTreeItem *unnamed = unnamedQuickTests(); + for (int row = 0, count = unnamed->childCount(); row < count; ++row) { + const TestTreeItem *child = unnamed->child(row); + if (qmlFile == child->filePath()) + return child->mainFile(); + } + return QString(); +} + +void TestTreeModel::qmlFilesAndFunctionNamesForMainFile(const QString &mainFile, + QSet<QString> *filePaths, + QList<QString> *functionNames) const +{ + TestTreeItem *unnamed = unnamedQuickTests(); + for (int i = 0; i < unnamed->childCount(); ++i) { + const TestTreeItem *child = unnamed->child(i); + if (child->mainFile() == mainFile) { + filePaths->insert(child->filePath()); + functionNames->append(child->name()); + } + } +} + +QList<QString> TestTreeModel::getUnnamedQuickTestFunctions() const +{ + TestTreeItem *unnamed = unnamedQuickTests(); + if (unnamed) + return unnamed->getChildNames(); + return QList<QString>(); +} + +QSet<QString> TestTreeModel::qmlFilesForProFile(const QString &proFile) const +{ + QSet<QString> filePaths; + CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); + if (TestTreeItem *unnamed = unnamedQuickTests()) { + for (int i = 0; i < unnamed->childCount(); ++i) { + const TestTreeItem *child = unnamed->child(i); + QList<CppTools::ProjectPart::Ptr> ppList = modelManager->projectPart(child->mainFile()); + if (ppList.size() && ppList.at(0)->projectFile == proFile) + filePaths.insert(child->filePath()); + } + } + return filePaths; +} + +bool TestTreeModel::hasUnnamedQuickTests() const +{ + for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) + if (m_quickTestRootItem->child(row)->name().isEmpty()) + return true; + return false; +} + TestTreeItem *TestTreeModel::unnamedQuickTests() const { for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) { @@ -534,177 +601,213 @@ TestTreeItem *TestTreeModel::unnamedQuickTests() const return 0; } -void TestTreeModel::modifyAutoTestSubtree(int row, TestTreeItem *newItem) +void TestTreeModel::removeUnnamedQuickTests(const QString &filePath) { - QModelIndex toBeModifiedIndex = index(0, 0).child(row, 0); - modifyTestSubtree(toBeModifiedIndex, newItem); -} + TestTreeItem *unnamedQT = unnamedQuickTests(); + if (!unnamedQT) + return; -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; - } + 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); } + + if (unnamedQT->childCount() == 0) + removeRow(unnamedQT->row(), unnamedQTIndex.parent()); emit testTreeModelChanged(); } -void TestTreeModel::addAutoTest(TestTreeItem *newItem) +void TestTreeModel::addTestTreeItem(const TestTreeItem &item, TestTreeModel::Type type) { - beginInsertRows(index(0, 0), m_autoTestRootItem->childCount(), m_autoTestRootItem->childCount()); - m_autoTestRootItem->appendChild(newItem); + TestTreeItem *parent = rootItemForType(type); + QModelIndex index = rootIndexForType(type); + TestTreeItem *toBeAdded = new TestTreeItem(item); + toBeAdded->setParent(parent); + + beginInsertRows(index, parent->childCount(), parent->childCount()); + parent->appendChild(toBeAdded); endInsertRows(); emit testTreeModelChanged(); } -void TestTreeModel::removeAllAutoTests() +void TestTreeModel::addTestTreeItems(const QList<TestTreeItem> &itemList, TestTreeModel::Type type) { - beginResetModel(); - m_autoTestRootItem->removeChildren(); - endResetModel(); + TestTreeItem *parent = rootItemForType(type); + QModelIndex index = rootIndexForType(type); + + beginInsertRows(index, parent->childCount(), parent->childCount() + itemList.size() - 1); + foreach (const TestTreeItem &item, itemList) { + TestTreeItem *toBeAdded = new TestTreeItem(item); + toBeAdded->setParent(parent); + parent->appendChild(toBeAdded); + } + endInsertRows(); emit testTreeModelChanged(); } -void TestTreeModel::modifyQuickTestSubtree(int row, TestTreeItem *newItem) +void TestTreeModel::updateUnnamedQuickTest(const QString &fileName, const QString &mainFile, + const QMap<QString, TestCodeLocationAndType> &functions) { - QModelIndex toBeModifiedIndex = index(1, 0).child(row, 0); - modifyTestSubtree(toBeModifiedIndex, newItem); -} + removeUnnamedQuickTests(fileName); + TestTreeItem unnamed = hasUnnamedQuickTests() + ? TestTreeItem(*unnamedQuickTests()) + : TestTreeItem(QString(), QString(), TestTreeItem::TEST_CLASS, rootItemForType(QuickTest)); -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; - } + foreach (const QString &functionName, functions.keys()) { + const TestCodeLocationAndType locationAndType = functions.value(functionName); + TestTreeItem *testFunction = new TestTreeItem(functionName, locationAndType.m_fileName, + locationAndType.m_type, &unnamed); + testFunction->setLine(locationAndType.m_line); + testFunction->setColumn(locationAndType.m_column); + testFunction->setMainFile(mainFile); + unnamed.appendChild(testFunction); } - emit testTreeModelChanged(); + if (hasUnnamedQuickTests()) + modifyTestTreeItem(unnamed, QuickTest, QString()); + else + addTestTreeItem(unnamed, QuickTest); } -void TestTreeModel::addQuickTest(TestTreeItem *newItem) +void TestTreeModel::modifyTestTreeItem(TestTreeItem item, TestTreeModel::Type type, const QString &file) { - beginInsertRows(index(1, 0), m_quickTestRootItem->childCount(), m_quickTestRootItem->childCount()); - m_quickTestRootItem->appendChild(newItem); - endInsertRows(); - emit testTreeModelChanged(); + QModelIndex index = rootIndexForType(type); + TestTreeItem *parent = rootItemForType(type); + item.setParent(parent); + if (file.isEmpty()) { + if (TestTreeItem *unnamed = unnamedQuickTests()) { + index = index.child(unnamed->row(), 0); + modifyTestSubtree(index, item); + } + } else { + for (int row = 0; row < parent->childCount(); ++row) { + if (parent->child(row)->filePath() == file) { + index = index.child(row, 0); + modifyTestSubtree(index, item); + break; + } + } + } } -void TestTreeModel::removeAllQuickTests() +void TestTreeModel::removeAllTestItems() { beginResetModel(); + m_autoTestRootItem->removeChildren(); m_quickTestRootItem->removeChildren(); endResetModel(); emit testTreeModelChanged(); } -bool TestTreeModel::removeUnnamedQuickTests(const QString &filePath) +void TestTreeModel::removeTestTreeItems(const QString &filePath, Type type) { - TestTreeItem *unnamedQT = unnamedQuickTests(); - if (!unnamedQT) - return false; - bool removed = false; - 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()) - removed |= removeRow(childRow, unnamedQTIndex); + const QModelIndex rootIndex = rootIndexForType(type); + const int count = rowCount(rootIndex); + for (int row = count - 1; row >= 0; --row) { + const QModelIndex childIndex = rootIndex.child(row, 0); + TestTreeItem *childItem = static_cast<TestTreeItem *>(childIndex.internalPointer()); + if (filePath == childItem->filePath()) + removed |= removeRow(row, rootIndex); } + if (removed) + emit testTreeModelChanged(); +} - if (unnamedQT->childCount() == 0) - removeRow(unnamedQT->row(), unnamedQTIndex.parent()); - emit testTreeModelChanged(); - return removed; +TestTreeItem *TestTreeModel::rootItemForType(TestTreeModel::Type type) +{ + switch (type) { + case AutoTest: + return m_autoTestRootItem; + case QuickTest: + return m_quickTestRootItem; + } + QTC_ASSERT(false, return 0); +} + +QModelIndex TestTreeModel::rootIndexForType(TestTreeModel::Type type) +{ + switch (type) { + case AutoTest: + return index(0, 0); + case QuickTest: + return index(1, 0); + } + QTC_ASSERT(false, return QModelIndex()); } -void TestTreeModel::modifyTestSubtree(QModelIndex &toBeModifiedIndex, TestTreeItem *newItem) +void TestTreeModel::modifyTestSubtree(QModelIndex &toBeModifiedIndex, const 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); + if (toBeModifiedItem->modifyContent(&newItem)) + emit dataChanged(toBeModifiedIndex, toBeModifiedIndex, + QVector<int>() << Qt::DisplayRole << Qt::ToolTipRole << LinkRole); // process sub-items as well... const int childCount = toBeModifiedItem->childCount(); - const int newChildCount = newItem->childCount(); + const int newChildCount = newItem.childCount(); // for keeping the CheckState on modifications - // TODO might still fail for duplicate entries (e.g. unnamed Quick Tests) - QHash<QString, Qt::CheckState> originalItems; + // TODO might still fail for duplicate entries + QHash<QString, Qt::CheckState> checkStates; for (int row = 0; row < childCount; ++row) { const TestTreeItem *child = toBeModifiedItem->child(row); - originalItems.insert(child->name(), child->checked()); + checkStates.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); - toBeAdded->setParent(toBeModifiedItem); - 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())); - } + processChildren(toBeModifiedIndex, newItem, childCount, checkStates); + // add additional items + for (int row = childCount; row < newChildCount; ++row) { + TestTreeItem *newChild = newItem.child(row); + TestTreeItem *toBeAdded = new TestTreeItem(*newChild); + toBeAdded->setParent(toBeModifiedItem); + if (checkStates.contains(toBeAdded->name()) + && checkStates.value(toBeAdded->name()) != Qt::Checked) + toBeAdded->setChecked(checkStates.value(toBeAdded->name())); + beginInsertRows(toBeModifiedIndex, row, row); + toBeModifiedItem->appendChild(toBeAdded); + endInsertRows(); } } 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 + processChildren(toBeModifiedIndex, newItem, newChildCount, checkStates); + // remove rest of the items removeRows(newChildCount, childCount - newChildCount, toBeModifiedIndex); } emit testTreeModelChanged(); } +void TestTreeModel::processChildren(QModelIndex &parentIndex, const TestTreeItem &newItem, + const int upperBound, + const QHash<QString, Qt::CheckState> &checkStates) +{ + static QVector<int> modificationRoles = QVector<int>() << Qt::DisplayRole + << Qt::ToolTipRole << LinkRole; + TestTreeItem *toBeModifiedItem = static_cast<TestTreeItem *>(parentIndex.internalPointer()); + for (int row = 0; row < upperBound; ++row) { + QModelIndex child = parentIndex.child(row, 0); + TestTreeItem *toBeModifiedChild = toBeModifiedItem->child(row); + TestTreeItem *modifiedChild = newItem.child(row); + if (toBeModifiedChild->modifyContent(modifiedChild)) + emit dataChanged(child, child, modificationRoles); + if (checkStates.contains(toBeModifiedChild->name())) { + Qt::CheckState state = checkStates.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); + } + } +} + /***************************** Sort/Filter Model **********************************/ TestTreeSortFilterModel::TestTreeSortFilterModel(TestTreeModel *sourceModel, QObject *parent) diff --git a/plugins/autotest/testtreemodel.h b/plugins/autotest/testtreemodel.h index 5ae83f0df7..81ad9418d9 100644 --- a/plugins/autotest/testtreemodel.h +++ b/plugins/autotest/testtreemodel.h @@ -37,6 +37,7 @@ namespace { namespace Autotest { namespace Internal { +struct TestCodeLocationAndType; class TestCodeParser; class TestInfo; class TestTreeItem; @@ -45,6 +46,11 @@ class TestTreeModel : public QAbstractItemModel { Q_OBJECT public: + enum Type { + AutoTest, + QuickTest + }; + static TestTreeModel* instance(); ~TestTreeModel(); @@ -62,18 +68,13 @@ 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(); + QString getMainFileForUnnamedQuickTest(const QString &qmlFile) const; + void qmlFilesAndFunctionNamesForMainFile(const QString &mainFile, QSet<QString> *filePaths, + QList<QString> *functionNames) const; + QList<QString> getUnnamedQuickTestFunctions() const; + QSet<QString> qmlFilesForProFile(const QString &proFile) const; + bool hasUnnamedQuickTests() const; - void modifyQuickTestSubtree(int row, TestTreeItem *newItem); - void removeQuickTestSubtreeByFilePath(const QString &file); - void addQuickTest(TestTreeItem *newItem); - void removeAllQuickTests(); - bool removeUnnamedQuickTests(const QString &filePath); signals: void testTreeModelChanged(); @@ -81,8 +82,23 @@ signals: public slots: private: + void addTestTreeItem(const TestTreeItem &item, Type type); + void addTestTreeItems(const QList<TestTreeItem> &itemList, Type type); + void updateUnnamedQuickTest(const QString &fileName, const QString &mainFile, + const QMap<QString, TestCodeLocationAndType> &functions); + void modifyTestTreeItem(TestTreeItem item, Type type, const QString &file); + void removeAllTestItems(); + void removeTestTreeItems(const QString &filePath, Type type); + void removeUnnamedQuickTests(const QString &filePath); + + TestTreeItem *unnamedQuickTests() const; + TestTreeItem *rootItemForType(Type type); + QModelIndex rootIndexForType(Type type); + explicit TestTreeModel(QObject *parent = 0); - void modifyTestSubtree(QModelIndex &toBeModifiedIndex, TestTreeItem *newItem); + void modifyTestSubtree(QModelIndex &toBeModifiedIndex, const TestTreeItem &newItem); + void processChildren(QModelIndex &parentIndex, const TestTreeItem &newItem, + const int upperBound, const QHash<QString, Qt::CheckState> &checkStates); TestTreeItem *m_rootItem; TestTreeItem *m_autoTestRootItem; diff --git a/plugins/autotest/testvisitor.h b/plugins/autotest/testvisitor.h index bba9f0ebda..ae6b844ca2 100644 --- a/plugins/autotest/testvisitor.h +++ b/plugins/autotest/testvisitor.h @@ -35,13 +35,6 @@ namespace Autotest { namespace Internal { -struct TestCodeLocationAndType { - QString m_fileName; - unsigned m_line; - unsigned m_column; - TestTreeItem::Type m_type; -}; - class TestVisitor : public CPlusPlus::SymbolVisitor { public: |