diff --git a/plugins/autotest/autotestconstants.h b/plugins/autotest/autotestconstants.h index fc254e42283f2007b7c699514005bec11489d9f5..c82c5efbca2fc15d17434a4b7c05aeeb47904b3d 100644 --- a/plugins/autotest/autotestconstants.h +++ b/plugins/autotest/autotestconstants.h @@ -29,6 +29,7 @@ const char MENU_ID[] = "AutoTest.Menu"; const char AUTOTEST_ID[] = "AutoTest.ATP"; const char AUTOTEST_CONTEXT[] = "Auto Tests"; const char TASK_INDEX[] = "AutoTest.Task.Index"; +const char TASK_PARSE[] = "AutoTest.Task.Parse"; const char UNNAMED_QUICKTESTS[] = QT_TR_NOOP("<unnamed>"); const char AUTOTEST_SETTINGS_CATEGORY[] = "ZY.Tests"; diff --git a/plugins/autotest/autotestplugin.cpp b/plugins/autotest/autotestplugin.cpp index 46e2a311d9a35dbb10e35ffbc5c9b52b94b1c567..0d6f7be04ffc128ceb4eca46fe0fb73e3d10a73e 100644 --- a/plugins/autotest/autotestplugin.cpp +++ b/plugins/autotest/autotestplugin.cpp @@ -21,6 +21,7 @@ #include "testrunner.h" #include "testsettings.h" #include "testsettingspage.h" +#include "testtreeitem.h" #include "testtreeview.h" #include "testtreemodel.h" #include "testresultspane.h" @@ -57,7 +58,10 @@ AutotestPlugin::AutotestPlugin() { // needed to be used in QueuedConnection connects qRegisterMetaType<TestResult>(); - // Create your members + qRegisterMetaType<TestTreeItem>(); + qRegisterMetaType<TestCodeLocationAndType>(); + qRegisterMetaType<TestTreeModel::Type>(); + m_instance = this; } diff --git a/plugins/autotest/testcodeparser.cpp b/plugins/autotest/testcodeparser.cpp index 2fc0fff3ca68d3001e2749eb36ee107cb7132d72..0a9b8b6893a168a60e411611d90b2a7433e8fa22 100644 --- a/plugins/autotest/testcodeparser.cpp +++ b/plugins/autotest/testcodeparser.cpp @@ -21,6 +21,7 @@ #include "testinfo.h" #include "testvisitor.h" +#include <coreplugin/progressmanager/futureprogress.h> #include <coreplugin/progressmanager/progressmanager.h> #include <cplusplus/LookupContext.h> @@ -39,9 +40,13 @@ #include <qmljs/qmljsdialect.h> #include <qmljstools/qmljsmodelmanager.h> +#include <utils/multitask.h> #include <utils/qtcassert.h> #include <utils/textfileformat.h> +#include <QFuture> +#include <QFutureInterface> + namespace Autotest { namespace Internal { @@ -66,7 +71,7 @@ TestCodeParser::TestCodeParser(TestTreeModel *parent) TestCodeParser::~TestCodeParser() { - clearMaps(); + clearCache(); } void TestCodeParser::setState(State state) @@ -112,7 +117,7 @@ void TestCodeParser::updateTestTree() m_pendingUpdate = false; - clearMaps(); + clearCache(); emit cacheCleared(); scanForTests(); } @@ -353,6 +358,26 @@ static TestTreeItem constructTestTreeItem(const QString &fileName, /****** end of helpers ******/ +void performParse(QFutureInterface<void> &futureInterface, QStringList list, + TestCodeParser *testCodeParser) +{ + int progressValue = 0; + futureInterface.setProgressRange(0, list.size()); + futureInterface.setProgressValue(progressValue); + CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); + CPlusPlus::Snapshot snapshot = cppMM->snapshot(); + + foreach (const QString &file, list) { + if (snapshot.contains(file)) { + CPlusPlus::Document::Ptr doc = snapshot.find(file).value(); + futureInterface.setProgressValue(++progressValue); + testCodeParser->checkDocumentForTestCode(doc); + } + } + futureInterface.setProgressValue(list.size()); +} + +/****** threaded parsing stuff *******/ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr document) { const QString fileName = document->fileName(); @@ -445,6 +470,14 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document) void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document) { + if (!m_parserEnabled) { + if (!m_pendingUpdate) { + m_partialUpdatePostPoned = true; + m_postPonedFiles.insert(document->fileName()); + } + return; + } + ProjectExplorer::Project *project = currentProject(); if (!project) return; @@ -463,6 +496,14 @@ void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &docume void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document) { + if (!m_parserEnabled) { + if (!m_pendingUpdate) { + m_partialUpdatePostPoned = true; + m_postPonedFiles.insert(document->fileName()); + } + return; + } + ProjectExplorer::Project *project = currentProject(); if (!project) return; @@ -482,7 +523,7 @@ void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document) if (!m_quickDocMap[fileName].referencingFile().isEmpty()) scanForTests(QStringList(m_quickDocMap[fileName].referencingFile())); } - if (!m_quickDocMap.contains(tr(Constants::UNNAMED_QUICKTESTS))) + if (m_unnamedQuickDocList.size() == 0) return; // special case of having unnamed TestCases @@ -548,8 +589,10 @@ void TestCodeParser::scanForTests(const QStringList &fileList) if (postponed(fileList)) return; + bool isFullParse = fileList.isEmpty(); + bool isSmallChange = !isFullParse && fileList.size() < 6; QStringList list; - if (fileList.isEmpty()) { + if (isFullParse) { list = currentProject()->files(ProjectExplorer::Project::AllFiles); if (list.isEmpty()) return; @@ -559,34 +602,33 @@ void TestCodeParser::scanForTests(const QStringList &fileList) m_parserState = PartialParse; } - CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); - CPlusPlus::Snapshot snapshot = cppMM->snapshot(); - - foreach (const QString &file, list) { - if (snapshot.contains(file)) { - CPlusPlus::Document::Ptr doc = snapshot.find(file).value(); - checkDocumentForTestCode(doc); + if (isSmallChange) { // no need to do this async or should we do this always async? + CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); + CPlusPlus::Snapshot snapshot = cppMM->snapshot(); + foreach (const QString &file, list) { + if (snapshot.contains(file)) { + CPlusPlus::Document::Ptr doc = snapshot.find(file).value(); + checkDocumentForTestCode(doc); + } } + emit onFinished(); + return; } - switch (m_parserState) { - case PartialParse: - m_parserState = Idle; - emit partialParsingFinished(); - break; - case FullParse: - m_parserState = Idle; - emit parsingFinished(); - break; - default: - qWarning("I should not be here..."); - break; - } + + QFuture<void> future = QtConcurrent::run(&performParse, list, this); + Core::FutureProgress *progress + = Core::ProgressManager::addTask(future, isFullParse ? tr("Scanning for Tests") + : tr("Updating Tests"), + Autotest::Constants::TASK_PARSE); + connect(progress, &Core::FutureProgress::finished, + this, &TestCodeParser::onFinished); } -void TestCodeParser::clearMaps() +void TestCodeParser::clearCache() { m_cppDocMap.clear(); m_quickDocMap.clear(); + m_unnamedQuickDocList.clear(); } void TestCodeParser::removeTestsIfNecessary(const QString &fileName) @@ -611,24 +653,14 @@ void TestCodeParser::removeTestsIfNecessary(const QString &fileName) emit testItemsRemoved(file, TestTreeModel::QuickTest); } // unnamed Quick Tests must be handled separately + removeUnnamedQuickTestsByName(fileName); + QSet<QString> filePaths; QList<QString> functionNames; if (m_model->hasUnnamedQuickTests()) { m_model->qmlFilesAndFunctionNamesForMainFile(fileName, &filePaths, &functionNames); foreach (const QString &file, filePaths) emit testItemsRemoved(file, TestTreeModel::QuickTest); - // update info map - TestInfo unnamedInfo = m_quickDocMap[tr(Constants::UNNAMED_QUICKTESTS)]; - QStringList functions = unnamedInfo.testFunctions(); - foreach (const QString &func, functionNames) - functions.removeOne(func); - unnamedInfo.setTestFunctions(functions); - if (functions.size() == 0) - m_quickDocMap.remove(tr(Constants::UNNAMED_QUICKTESTS)); - else - m_quickDocMap.insert(tr(Constants::UNNAMED_QUICKTESTS), unnamedInfo); - } else { - m_quickDocMap.remove(tr(Constants::UNNAMED_QUICKTESTS)); } } } @@ -655,17 +687,10 @@ void TestCodeParser::removeTestsIfNecessaryByProFile(const QString &proFile) } // handle unnamed Quick Tests const QSet<QString> &filePaths = m_model->qmlFilesForProFile(proFile); - foreach (const QString &fileName, filePaths) + foreach (const QString &fileName, filePaths) { + removeUnnamedQuickTestsByName(fileName); emit unnamedQuickTestsRemoved(fileName); - // update info map - 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::onTaskStarted(Core::Id type) @@ -683,6 +708,31 @@ void TestCodeParser::onAllTasksFinished(Core::Id type) m_parserEnabled = true; if (m_pendingUpdate) updateTestTree(); + else if (m_partialUpdatePostPoned) { + m_partialUpdatePostPoned = false; + QStringList tmp; + foreach (const QString &file, m_postPonedFiles) + tmp << file; + m_postPonedFiles.clear(); + scanForTests(tmp); + } +} + +void TestCodeParser::onFinished() +{ + switch (m_parserState) { + case PartialParse: + m_parserState = Idle; + emit partialParsingFinished(); + break; + case FullParse: + m_parserState = Idle; + emit parsingFinished(); + break; + default: + qWarning("I should not be here..."); + break; + } } void TestCodeParser::onPartialParsingFinished() @@ -710,16 +760,13 @@ void TestCodeParser::updateUnnamedQuickTests(const QString &fileName, const QStr 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)); + removeUnnamedQuickTestsByName(fileName); + foreach (const QString &functionName, functions.keys()) { + UnnamedQuickTestInfo info(functionName, fileName); + m_unnamedQuickDocList.append(info); } + + emit unnamedQuickTestsUpdated(fileName, mainFile, functions); } void TestCodeParser::updateModelAndCppDocMap(CPlusPlus::Document::Ptr document, @@ -779,6 +826,7 @@ void TestCodeParser::updateModelAndQuickDocMap(QmlJS::Document::Ptr document, m_quickDocMap.insert(fileName, testInfo); } else { // if it was formerly unnamed remove the respective items + removeUnnamedQuickTestsByName(fileName); emit unnamedQuickTestsRemoved(fileName); emit testItemCreated(testItem, TestTreeModel::QuickTest); @@ -789,6 +837,14 @@ void TestCodeParser::updateModelAndQuickDocMap(QmlJS::Document::Ptr document, } } +void TestCodeParser::removeUnnamedQuickTestsByName(const QString &fileName) +{ + for (int i = m_unnamedQuickDocList.size() - 1; i >= 0; --i) { + if (m_unnamedQuickDocList.at(i).fileName() == fileName) + m_unnamedQuickDocList.removeAt(i); + } +} + void TestCodeParser::onProFileEvaluated() { ProjectExplorer::Project *project = currentProject(); @@ -822,16 +878,12 @@ int TestCodeParser::autoTestsCount() const int TestCodeParser::namedQuickTestsCount() const { - if (m_quickDocMap.contains(tr(Constants::UNNAMED_QUICKTESTS))) - return m_quickDocMap.size() - 1; return m_quickDocMap.size(); } int TestCodeParser::unnamedQuickTestsCount() const { - if (m_quickDocMap.contains(tr(Constants::UNNAMED_QUICKTESTS))) - return m_quickDocMap.value(tr(Constants::UNNAMED_QUICKTESTS)).testFunctions().size(); - return 0; + return m_unnamedQuickDocList.size(); } #endif diff --git a/plugins/autotest/testcodeparser.h b/plugins/autotest/testcodeparser.h index 9b6670c320f7b0e9d52ca3d4f1c1598d660e2a5b..f2bb6de13ef844073d14aa6a817922373eadb6fb 100644 --- a/plugins/autotest/testcodeparser.h +++ b/plugins/autotest/testcodeparser.h @@ -38,6 +38,7 @@ namespace Internal { struct TestCodeLocationAndType; class TestInfo; +class UnnamedQuickTestInfo; class TestCodeParser : public QObject { @@ -86,12 +87,13 @@ public slots: private: bool postponed(const QStringList &fileList); void scanForTests(const QStringList &fileList = QStringList()); - void clearMaps(); + void clearCache(); void removeTestsIfNecessary(const QString &fileName); void removeTestsIfNecessaryByProFile(const QString &proFile); void onTaskStarted(Core::Id type); void onAllTasksFinished(Core::Id type); + void onFinished(); void onPartialParsingFinished(); void updateUnnamedQuickTests(const QString &fileName, const QString &mainFile, const QMap<QString, TestCodeLocationAndType> &functions); @@ -99,10 +101,12 @@ private: const QString &declaringFile, TestTreeItem &testItem); void updateModelAndQuickDocMap(QmlJS::Document::Ptr document, const QString &referencingFile, TestTreeItem &testItem); + void removeUnnamedQuickTestsByName(const QString &fileName); TestTreeModel *m_model; QMap<QString, TestInfo> m_cppDocMap; QMap<QString, TestInfo> m_quickDocMap; + QList<UnnamedQuickTestInfo> m_unnamedQuickDocList; bool m_parserEnabled; bool m_pendingUpdate; bool m_fullUpdatePostPoned; diff --git a/plugins/autotest/testinfo.cpp b/plugins/autotest/testinfo.cpp index ad904e9a0f1a4d79964e48879b859755c4ae819f..e3e13274f7c1dd496e5df64505c095d1be499cc2 100644 --- a/plugins/autotest/testinfo.cpp +++ b/plugins/autotest/testinfo.cpp @@ -35,5 +35,11 @@ TestInfo::~TestInfo() m_functions.clear(); } +UnnamedQuickTestInfo::UnnamedQuickTestInfo(const QString &function, const QString &fileName) + : m_function(function), + m_fileName(fileName) +{ +} + } // namespace Internal } // namespace Autotest diff --git a/plugins/autotest/testinfo.h b/plugins/autotest/testinfo.h index b974824f9e0763e56cb2969debe741fc1f1821b0..cadd57212f9a9a4bf92b475403b51c97f71a790f 100644 --- a/plugins/autotest/testinfo.h +++ b/plugins/autotest/testinfo.h @@ -54,6 +54,22 @@ private: QString m_proFile; }; +class UnnamedQuickTestInfo { +public: + explicit UnnamedQuickTestInfo(const QString &function = QString(), + const QString& fileName = QString()); + ~UnnamedQuickTestInfo() {} + + const QString function() const { return m_function; } + void setFunction(const QString &function) { m_function = function; } + const QString fileName() const { return m_fileName; } + void setFileName(const QString &fileName) { m_fileName = fileName; } + +private: + QString m_function; + QString m_fileName; +}; + } // namespace Internal } // namespace Autotest diff --git a/plugins/autotest/testtreeitem.h b/plugins/autotest/testtreeitem.h index 25a49f3edf7677e9d797861da643e3b74172b9e0..e1814db356f59a0b6c83d46c986fb8f3c9fb0184 100644 --- a/plugins/autotest/testtreeitem.h +++ b/plugins/autotest/testtreeitem.h @@ -21,6 +21,7 @@ #include <QList> #include <QString> +#include <QMetaType> namespace Autotest { namespace Internal { @@ -37,7 +38,8 @@ public: TEST_SPECIALFUNCTION }; - TestTreeItem(const QString &name, const QString &filePath, Type type, TestTreeItem *parent = 0); + TestTreeItem(const QString &name = QString(), const QString &filePath = QString(), + Type type = ROOT, TestTreeItem *parent = 0); virtual ~TestTreeItem(); TestTreeItem(const TestTreeItem& other); @@ -90,4 +92,7 @@ struct TestCodeLocationAndType { } // namespace Internal } // namespace Autotest +Q_DECLARE_METATYPE(Autotest::Internal::TestTreeItem) +Q_DECLARE_METATYPE(Autotest::Internal::TestCodeLocationAndType) + #endif // TESTTREEITEM_H diff --git a/plugins/autotest/testtreemodel.cpp b/plugins/autotest/testtreemodel.cpp index cc54b6969ae9ff67910254d04b9f5162f7fd7c8c..8b9de455ade49fed66ee2c5f1d0012503419911e 100644 --- a/plugins/autotest/testtreemodel.cpp +++ b/plugins/autotest/testtreemodel.cpp @@ -55,15 +55,20 @@ 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::cacheCleared, this, + &TestTreeModel::removeAllTestItems, Qt::QueuedConnection); + connect(m_parser, &TestCodeParser::testItemCreated, + this, &TestTreeModel::addTestTreeItem, Qt::QueuedConnection); + connect(m_parser, &TestCodeParser::testItemsCreated, + this, &TestTreeModel::addTestTreeItems, Qt::QueuedConnection); + connect(m_parser, &TestCodeParser::testItemModified, + this, &TestTreeModel::modifyTestTreeItem, Qt::QueuedConnection); + connect(m_parser, &TestCodeParser::testItemsRemoved, + this, &TestTreeModel::removeTestTreeItems, Qt::QueuedConnection); connect(m_parser, &TestCodeParser::unnamedQuickTestsUpdated, - this, &TestTreeModel::updateUnnamedQuickTest); + this, &TestTreeModel::updateUnnamedQuickTest, Qt::QueuedConnection); connect(m_parser, &TestCodeParser::unnamedQuickTestsRemoved, - this, &TestTreeModel::removeUnnamedQuickTests); + this, &TestTreeModel::removeUnnamedQuickTests, Qt::QueuedConnection); // CppTools::CppModelManagerInterface *cppMM = CppTools::CppModelManagerInterface::instance(); // if (cppMM) { diff --git a/plugins/autotest/testtreemodel.h b/plugins/autotest/testtreemodel.h index 46a7b28a95686cf5c5265c215dd3c936f03fac1e..44186285a9fbd53480a07726150105ce0e1a6a86 100644 --- a/plugins/autotest/testtreemodel.h +++ b/plugins/autotest/testtreemodel.h @@ -152,4 +152,6 @@ private: } // namespace Internal } // namespace Autotest +Q_DECLARE_METATYPE(Autotest::Internal::TestTreeModel::Type) + #endif // TESTTREEMODEL_H