AutoTest: Report parse results through QFutureInterface

Change-Id: Ib99e9ae5efa26f01dd9c0b0bf5516e2e9dab73ce
Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
This commit is contained in:
Christian Stenger
2016-01-27 13:52:33 +01:00
parent fa3a35e346
commit 30b1e70c42
3 changed files with 129 additions and 132 deletions

View File

@@ -79,6 +79,13 @@ TestCodeParser::TestCodeParser(TestTreeModel *parent)
this, &TestCodeParser::onAllTasksFinished); this, &TestCodeParser::onAllTasksFinished);
connect(this, &TestCodeParser::partialParsingFinished, connect(this, &TestCodeParser::partialParsingFinished,
this, &TestCodeParser::onPartialParsingFinished); this, &TestCodeParser::onPartialParsingFinished);
connect(&m_futureWatcher, &QFutureWatcher<TestParseResult>::finished,
this, &TestCodeParser::onFinished);
connect(&m_futureWatcher, &QFutureWatcher<TestParseResult>::resultReadyAt,
this, [this] (int index) {
const TestParseResult result = m_futureWatcher.resultAt(index);
emit testItemCreated(result.item, result.type);
});
} }
TestCodeParser::~TestCodeParser() TestCodeParser::~TestCodeParser()
@@ -481,102 +488,8 @@ static TestTreeItem *constructGTestTreeItem(const QString &filePath, const GTest
/****** end of helpers ******/ /****** end of helpers ******/
// used internally to indicate a parse that failed due to having triggered a parse for a file that static void handleQtQuickTest(QFutureInterface<TestParseResult> futureInterface,
// is not (yet) part of the CppModelManager's snapshot CPlusPlus::Document::Ptr document)
static bool parsingHasFailed;
void performParse(QFutureInterface<void> &futureInterface, const QStringList &list,
const QMap<QString, QString> &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<CppTools::ProjectPart::Ptr> 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<QString, TestCodeLocationAndType> testFunctions = visitor.privateSlots();
QMap<QString, TestCodeLocationList> 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)
{ {
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
@@ -620,11 +533,12 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document)
tcLocationAndType.m_column, testFunctions); tcLocationAndType.m_column, testFunctions);
testTreeItem->setReferencingFile(cppFileName); testTreeItem->setReferencingFile(cppFileName);
} }
emit testItemCreated(testTreeItem, TestTreeModel::QuickTest);
futureInterface.reportResult(TestParseResult(testTreeItem, TestTreeModel::QuickTest));
} }
} }
void TestCodeParser::handleGTest(const QString &filePath) static void handleGTest(QFutureInterface<TestParseResult> futureInterface, const QString &filePath)
{ {
const QByteArray &fileContent = getFileContent(filePath); const QByteArray &fileContent = getFileContent(filePath);
const CPlusPlus::Snapshot snapshot = CPlusPlus::CppModelManagerBase::instance()->snapshot(); const CPlusPlus::Snapshot snapshot = CPlusPlus::CppModelManagerBase::instance()->snapshot();
@@ -644,10 +558,107 @@ void TestCodeParser::handleGTest(const QString &filePath)
foreach (const GTestCaseSpec &testSpec, result.keys()) { foreach (const GTestCaseSpec &testSpec, result.keys()) {
TestTreeItem *item = constructGTestTreeItem(filePath, testSpec, proFile, TestTreeItem *item = constructGTestTreeItem(filePath, testSpec, proFile,
result.value(testSpec)); result.value(testSpec));
emit testItemCreated(item, TestTreeModel::GoogleTest);
futureInterface.reportResult(TestParseResult(item, TestTreeModel::GoogleTest));
} }
} }
static void checkDocumentForTestCode(QFutureInterface<TestParseResult> futureInterface,
CPlusPlus::Document::Ptr document,
const QString &referencingFile = QString())
{
const QString fileName = document->fileName();
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
QList<CppTools::ProjectPart::Ptr> 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<QString, TestCodeLocationAndType> testFunctions = visitor.privateSlots();
QMap<QString, TestCodeLocationList> 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<TestParseResult> &futureInterface,
const QStringList &list, const QMap<QString, QString> &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) void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document)
{ {
if (m_codeModelParsing) { if (m_codeModelParsing) {
@@ -770,7 +781,6 @@ void TestCodeParser::scanForTests(const QStringList &fileList)
m_postponedFiles.clear(); m_postponedFiles.clear();
bool isFullParse = fileList.isEmpty(); bool isFullParse = fileList.isEmpty();
bool isSmallChange = !isFullParse && fileList.size() < 6;
QStringList list; QStringList list;
if (isFullParse) { if (isFullParse) {
list = ProjectExplorer::SessionManager::startupProject()->files(ProjectExplorer::Project::AllFiles); list = ProjectExplorer::SessionManager::startupProject()->files(ProjectExplorer::Project::AllFiles);
@@ -787,37 +797,20 @@ void TestCodeParser::scanForTests(const QStringList &fileList)
parsingHasFailed = false; parsingHasFailed = false;
QMap<QString, QString> referencingFiles = m_model->referencingFiles(); QMap<QString, QString> 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) foreach (const QString &filePath, list)
m_model->markForRemoval(filePath); 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<TestParseResult> future
= Utils::runAsync<TestParseResult>(&performParse, list, referencingFiles);
QFuture<void> future = Utils::runAsync<void>(&performParse, list, referencingFiles, this); m_futureWatcher.setFuture(future);
Core::FutureProgress *progress if (list.size() > 5) {
= Core::ProgressManager::addTask(future, isFullParse ? tr("Scanning for Tests") Core::ProgressManager::addTask(future, tr("Scanning for Tests"),
: tr("Refreshing Tests List"), Autotest::Constants::TASK_PARSE);
Autotest::Constants::TASK_PARSE); }
connect(progress, &Core::FutureProgress::finished,
this, &TestCodeParser::onFinished);
emit parsingStarted(); emit parsingStarted();
} }

View File

@@ -35,6 +35,7 @@
#include <QObject> #include <QObject>
#include <QMap> #include <QMap>
#include <QFutureWatcher>
namespace Core { namespace Core {
class Id; class Id;
@@ -74,11 +75,6 @@ signals:
public slots: public slots:
void emitUpdateTestTree(); void emitUpdateTestTree();
void updateTestTree(); 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 onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document);
void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document); void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document);
void onStartupProjectChanged(ProjectExplorer::Project *); void onStartupProjectChanged(ProjectExplorer::Project *);
@@ -102,6 +98,7 @@ private:
bool m_singleShotScheduled; bool m_singleShotScheduled;
QSet<QString> m_postponedFiles; QSet<QString> m_postponedFiles;
State m_parserState; State m_parserState;
QFutureWatcher<TestParseResult> m_futureWatcher;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -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 Internal
} // namespace Autotest } // namespace Autotest