diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index e3708082b78..dd504aba6ad 100644 --- a/src/plugins/autotest/testcodeparser.cpp +++ b/src/plugins/autotest/testcodeparser.cpp @@ -79,6 +79,13 @@ TestCodeParser::TestCodeParser(TestTreeModel *parent) this, &TestCodeParser::onAllTasksFinished); connect(this, &TestCodeParser::partialParsingFinished, this, &TestCodeParser::onPartialParsingFinished); + connect(&m_futureWatcher, &QFutureWatcher::finished, + this, &TestCodeParser::onFinished); + connect(&m_futureWatcher, &QFutureWatcher::resultReadyAt, + this, [this] (int index) { + const TestParseResult result = m_futureWatcher.resultAt(index); + emit testItemCreated(result.item, result.type); + }); } TestCodeParser::~TestCodeParser() @@ -481,102 +488,8 @@ static TestTreeItem *constructGTestTreeItem(const QString &filePath, const GTest /****** end of helpers ******/ -// used internally to indicate a parse that failed due to having triggered a parse for a file that -// is not (yet) part of the CppModelManager's snapshot -static bool parsingHasFailed; - -void performParse(QFutureInterface &futureInterface, const QStringList &list, - const QMap &referencingFiles, - 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); - const QString &referencingFile = referencingFiles.value(file); - testCodeParser->checkDocumentForTestCode(doc, - list.contains(referencingFile) ? QString() : referencingFile); - } else { - parsingHasFailed |= (CppTools::ProjectFile::classify(file) - != CppTools::ProjectFile::Unclassified); - } - } - futureInterface.setProgressValue(list.size()); -} - -/****** threaded parsing stuff *******/ -void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr document, - const QString &referencingFile) -{ - const QString fileName = document->fileName(); - const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); - - QList projParts = modelManager->projectPart(fileName); - if (projParts.size()) - if (!projParts.at(0)->selectedForBuilding) - return; - - if (includesQtQuickTest(document, modelManager)) { - handleQtQuickTest(document); - return; - } - - if (includesQtTest(document, modelManager) && qtTestLibDefined(modelManager, fileName)) { - QString testCaseName(testClass(modelManager, document)); - if (!testCaseName.isEmpty()) { - unsigned line = 0; - unsigned column = 0; - CPlusPlus::Document::Ptr declaringDoc = declaringDocument(document, testCaseName, - &line, &column); - if (declaringDoc.isNull()) - return; - - const bool hasReferencingFile = declaringDoc->fileName() != document->fileName(); - - TestVisitor visitor(testCaseName); - visitor.accept(declaringDoc->globalNamespace()); - const QMap testFunctions = visitor.privateSlots(); - - QMap dataTags = - checkForDataTags(declaringDoc->fileName(), testFunctions); - - if (hasReferencingFile) - dataTags.unite(checkForDataTags(document->fileName(), testFunctions)); - - TestTreeItem *item = constructTestTreeItem(declaringDoc->fileName(), QString(), - testCaseName, line, column, testFunctions, - dataTags); - if (hasReferencingFile) - item->setReferencingFile(fileName); - - emit testItemCreated(item, TestTreeModel::AutoTest); - return; - } - } - - if (includesGTest(document, modelManager)) { - if (hasGTestNames(document)) { - handleGTest(document->fileName()); - return; - } - } - - // could not find the class to test, or QTest is not included and QT_TESTLIB_LIB defined - // maybe file is only a referenced file - if (!referencingFile.isEmpty()) { - CPlusPlus::Snapshot snapshot = modelManager->snapshot(); - if (snapshot.contains(referencingFile)) - checkDocumentForTestCode(snapshot.find(referencingFile).value()); - } -} - -void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document) +static void handleQtQuickTest(QFutureInterface futureInterface, + CPlusPlus::Document::Ptr document) { const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); @@ -620,11 +533,12 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document) tcLocationAndType.m_column, testFunctions); testTreeItem->setReferencingFile(cppFileName); } - emit testItemCreated(testTreeItem, TestTreeModel::QuickTest); + + futureInterface.reportResult(TestParseResult(testTreeItem, TestTreeModel::QuickTest)); } } -void TestCodeParser::handleGTest(const QString &filePath) +static void handleGTest(QFutureInterface futureInterface, const QString &filePath) { const QByteArray &fileContent = getFileContent(filePath); const CPlusPlus::Snapshot snapshot = CPlusPlus::CppModelManagerBase::instance()->snapshot(); @@ -644,10 +558,107 @@ void TestCodeParser::handleGTest(const QString &filePath) foreach (const GTestCaseSpec &testSpec, result.keys()) { TestTreeItem *item = constructGTestTreeItem(filePath, testSpec, proFile, result.value(testSpec)); - emit testItemCreated(item, TestTreeModel::GoogleTest); + + futureInterface.reportResult(TestParseResult(item, TestTreeModel::GoogleTest)); } } +static void checkDocumentForTestCode(QFutureInterface futureInterface, + CPlusPlus::Document::Ptr document, + const QString &referencingFile = QString()) +{ + const QString fileName = document->fileName(); + const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); + + QList projParts = modelManager->projectPart(fileName); + if (projParts.size()) + if (!projParts.at(0)->selectedForBuilding) + return; + + if (includesQtQuickTest(document, modelManager)) { + handleQtQuickTest(futureInterface, document); + return; + } + + if (includesQtTest(document, modelManager) && qtTestLibDefined(modelManager, fileName)) { + QString testCaseName(testClass(modelManager, document)); + if (!testCaseName.isEmpty()) { + unsigned line = 0; + unsigned column = 0; + CPlusPlus::Document::Ptr declaringDoc = declaringDocument(document, testCaseName, + &line, &column); + if (declaringDoc.isNull()) + return; + + const bool hasReferencingFile = declaringDoc->fileName() != document->fileName(); + + TestVisitor visitor(testCaseName); + visitor.accept(declaringDoc->globalNamespace()); + const QMap testFunctions = visitor.privateSlots(); + + QMap dataTags = + checkForDataTags(declaringDoc->fileName(), testFunctions); + + if (hasReferencingFile) + dataTags.unite(checkForDataTags(document->fileName(), testFunctions)); + + TestTreeItem *item = constructTestTreeItem(declaringDoc->fileName(), QString(), + testCaseName, line, column, testFunctions, + dataTags); + if (hasReferencingFile) + item->setReferencingFile(fileName); + + futureInterface.reportResult(TestParseResult(item, TestTreeModel::AutoTest)); + return; + } + } + + if (includesGTest(document, modelManager)) { + if (hasGTestNames(document)) { + handleGTest(futureInterface, document->fileName()); + return; + } + } + + // could not find the class to test, or QTest is not included and QT_TESTLIB_LIB defined + // maybe file is only a referenced file + if (!referencingFile.isEmpty()) { + CPlusPlus::Snapshot snapshot = modelManager->snapshot(); + if (snapshot.contains(referencingFile)) + checkDocumentForTestCode(futureInterface, snapshot.find(referencingFile).value()); + } +} + +// used internally to indicate a parse that failed due to having triggered a parse for a file that +// is not (yet) part of the CppModelManager's snapshot +static bool parsingHasFailed; + +static void performParse(QFutureInterface &futureInterface, + const QStringList &list, const QMap &referencingFiles) +{ + 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); + const QString &referencingFile = referencingFiles.value(file); + checkDocumentForTestCode(futureInterface, doc, + list.contains(referencingFile) ? QString() : referencingFile); + } else { + parsingHasFailed |= (CppTools::ProjectFile::classify(file) + != CppTools::ProjectFile::Unclassified); + } + } + futureInterface.setProgressValue(list.size()); +} + +/****** threaded parsing stuff *******/ + void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document) { if (m_codeModelParsing) { @@ -770,7 +781,6 @@ void TestCodeParser::scanForTests(const QStringList &fileList) m_postponedFiles.clear(); bool isFullParse = fileList.isEmpty(); - bool isSmallChange = !isFullParse && fileList.size() < 6; QStringList list; if (isFullParse) { list = ProjectExplorer::SessionManager::startupProject()->files(ProjectExplorer::Project::AllFiles); @@ -787,37 +797,20 @@ void TestCodeParser::scanForTests(const QStringList &fileList) parsingHasFailed = false; QMap referencingFiles = m_model->referencingFiles(); - if (isSmallChange) { // no need to do this async or should we do this always async? + if (isFullParse) { + m_model->markAllForRemoval(); + } else { foreach (const QString &filePath, list) m_model->markForRemoval(filePath); - - 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(); - const QString &referencingFile = referencingFiles.value(file); - checkDocumentForTestCode(doc, list.contains(referencingFile) ? QString() - : referencingFile); - } else { - parsingHasFailed |= (CppTools::ProjectFile::classify(file) - != CppTools::ProjectFile::Unclassified); - } - } - onFinished(); - return; } - m_model->markAllForRemoval(); - - QFuture future = Utils::runAsync(&performParse, list, referencingFiles, this); - Core::FutureProgress *progress - = Core::ProgressManager::addTask(future, isFullParse ? tr("Scanning for Tests") - : tr("Refreshing Tests List"), - Autotest::Constants::TASK_PARSE); - connect(progress, &Core::FutureProgress::finished, - this, &TestCodeParser::onFinished); - + QFuture future + = Utils::runAsync(&performParse, list, referencingFiles); + m_futureWatcher.setFuture(future); + if (list.size() > 5) { + Core::ProgressManager::addTask(future, tr("Scanning for Tests"), + Autotest::Constants::TASK_PARSE); + } emit parsingStarted(); } diff --git a/src/plugins/autotest/testcodeparser.h b/src/plugins/autotest/testcodeparser.h index e3ab0bdeb80..64a0af22b5d 100644 --- a/src/plugins/autotest/testcodeparser.h +++ b/src/plugins/autotest/testcodeparser.h @@ -35,6 +35,7 @@ #include #include +#include namespace Core { class Id; @@ -74,11 +75,6 @@ signals: public slots: void emitUpdateTestTree(); void updateTestTree(); - void checkDocumentForTestCode(CPlusPlus::Document::Ptr document, - const QString &referencingFile = QString()); - void handleQtQuickTest(CPlusPlus::Document::Ptr document); - void handleGTest(const QString &filePath); - void onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document); void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document); void onStartupProjectChanged(ProjectExplorer::Project *); @@ -102,6 +98,7 @@ private: bool m_singleShotScheduled; QSet m_postponedFiles; State m_parserState; + QFutureWatcher m_futureWatcher; }; } // namespace Internal diff --git a/src/plugins/autotest/testtreemodel.h b/src/plugins/autotest/testtreemodel.h index ee04bdac983..8af0fd7bec1 100644 --- a/src/plugins/autotest/testtreemodel.h +++ b/src/plugins/autotest/testtreemodel.h @@ -145,6 +145,13 @@ private: }; +struct TestParseResult +{ + TestParseResult(TestTreeItem *it, TestTreeModel::Type t) : item(it), type(t) {} + TestTreeItem *item; + TestTreeModel::Type type; +}; + } // namespace Internal } // namespace Autotest