forked from qt-creator/qt-creator
AutoTest: Report parse results through QFutureInterface
Change-Id: Ib99e9ae5efa26f01dd9c0b0bf5516e2e9dab73ce Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
This commit is contained in:
@@ -79,6 +79,13 @@ TestCodeParser::TestCodeParser(TestTreeModel *parent)
|
||||
this, &TestCodeParser::onAllTasksFinished);
|
||||
connect(this, &TestCodeParser::partialParsingFinished,
|
||||
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()
|
||||
@@ -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<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)
|
||||
static void handleQtQuickTest(QFutureInterface<TestParseResult> 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<TestParseResult> 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<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)
|
||||
{
|
||||
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<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)
|
||||
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<void> future = Utils::runAsync<void>(&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<TestParseResult> future
|
||||
= Utils::runAsync<TestParseResult>(&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();
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
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<QString> m_postponedFiles;
|
||||
State m_parserState;
|
||||
QFutureWatcher<TestParseResult> m_futureWatcher;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user