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);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user