summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Stenger <christian.stenger@theqtcompany.com>2015-02-13 15:44:18 +0100
committerChristian Stenger <christian.stenger@theqtcompany.com>2015-02-17 10:26:49 +0200
commit4caba7a3dda9a22a505d96799233dc35de29dba3 (patch)
treefc7597077e76c3c905cf32c47d566bb8a9ac521e
parent223b43e9e0d978e1c6c2afc5002489eef09ec9e4 (diff)
downloadqt-creator-4caba7a3dda9a22a505d96799233dc35de29dba3.tar.gz
Perform parsing asynchronously...
...to avoid blocking the ui thread. Parsing will now performed in a separate thread, except for small changes where this would create too much overhead. Change-Id: I1db441594f1684f969bb86c9423c0fb0bcb1a53a Reviewed-by: Andre Poenitz <andre.poenitz@theqtcompany.com>
-rw-r--r--plugins/autotest/autotestconstants.h1
-rw-r--r--plugins/autotest/autotestplugin.cpp6
-rw-r--r--plugins/autotest/testcodeparser.cpp174
-rw-r--r--plugins/autotest/testcodeparser.h6
-rw-r--r--plugins/autotest/testinfo.cpp6
-rw-r--r--plugins/autotest/testinfo.h16
-rw-r--r--plugins/autotest/testtreeitem.h7
-rw-r--r--plugins/autotest/testtreemodel.cpp19
-rw-r--r--plugins/autotest/testtreemodel.h2
9 files changed, 166 insertions, 71 deletions
diff --git a/plugins/autotest/autotestconstants.h b/plugins/autotest/autotestconstants.h
index fc254e4228..c82c5efbca 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 46e2a311d9..0d6f7be04f 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 2fc0fff3ca..0a9b8b6893 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 9b6670c320..f2bb6de13e 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 ad904e9a0f..e3e13274f7 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 b974824f9e..cadd57212f 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 25a49f3edf..e1814db356 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 cc54b6969a..8b9de455ad 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 46a7b28a95..44186285a9 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