forked from qt-creator/qt-creator
QmlJS: Track futures to allow testing
Tests often want to wait for all async tasks to finish before progressing. Change-Id: I61738df730ca341b5c9d227569d961cd1991b296 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Philip Van Hoof <philip@codeminded.be>
This commit is contained in:
@@ -306,6 +306,7 @@ void ModelManagerInterface::updateSourceFiles(const QStringList &files,
|
|||||||
|
|
||||||
void ModelManagerInterface::cleanupFutures()
|
void ModelManagerInterface::cleanupFutures()
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&m_futuresMutex);
|
||||||
const int maxFutures = 10;
|
const int maxFutures = 10;
|
||||||
if (m_futures.size() > maxFutures) {
|
if (m_futures.size() > maxFutures) {
|
||||||
const QList<QFuture<void>> futures = m_futures;
|
const QList<QFuture<void>> futures = m_futures;
|
||||||
@@ -327,8 +328,7 @@ QFuture<void> ModelManagerInterface::refreshSourceFiles(const QStringList &sourc
|
|||||||
workingCopyInternal(), sourceFiles,
|
workingCopyInternal(), sourceFiles,
|
||||||
this, Dialect(Dialect::Qml),
|
this, Dialect(Dialect::Qml),
|
||||||
emitDocumentOnDiskChanged);
|
emitDocumentOnDiskChanged);
|
||||||
cleanupFutures();
|
addFuture(result);
|
||||||
m_futures.append(result);
|
|
||||||
|
|
||||||
if (sourceFiles.count() > 1)
|
if (sourceFiles.count() > 1)
|
||||||
addTaskInternal(result, tr("Parsing QML Files"), Constants::TASK_INDEX);
|
addTaskInternal(result, tr("Parsing QML Files"), Constants::TASK_INDEX);
|
||||||
@@ -1137,9 +1137,7 @@ void ModelManagerInterface::maybeScan(const PathsAndLanguages &importPaths)
|
|||||||
QFuture<void> result = Utils::runAsync(&ModelManagerInterface::importScan,
|
QFuture<void> result = Utils::runAsync(&ModelManagerInterface::importScan,
|
||||||
workingCopyInternal(), pathToScan,
|
workingCopyInternal(), pathToScan,
|
||||||
this, true, true, false);
|
this, true, true, false);
|
||||||
cleanupFutures();
|
addFuture(result);
|
||||||
m_futures.append(result);
|
|
||||||
|
|
||||||
addTaskInternal(result, tr("Scanning QML Imports"), Constants::TASK_IMPORT_SCAN);
|
addTaskInternal(result, tr("Scanning QML Imports"), Constants::TASK_IMPORT_SCAN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1536,13 +1534,43 @@ void ModelManagerInterface::setDefaultVContext(const ViewerContext &vContext)
|
|||||||
m_defaultVContexts[vContext.language] = vContext;
|
m_defaultVContexts[vContext.language] = vContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelManagerInterface::joinAllThreads()
|
void ModelManagerInterface::test_joinAllThreads()
|
||||||
{
|
{
|
||||||
for (QFuture<void> &future : m_futures)
|
// Loop since futures can spawn more futures as they finish.
|
||||||
future.waitForFinished();
|
while (true) {
|
||||||
|
QFuture<void> f;
|
||||||
|
// get one future
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_futuresMutex);
|
||||||
|
for (QFuture<void> &future : m_futures) {
|
||||||
|
if (!future.isFinished() && !future.isCanceled()) {
|
||||||
|
f = future;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!f.isFinished() && !f.isCanceled()) {
|
||||||
|
f.waitForFinished();
|
||||||
|
|
||||||
|
// Some futures trigger more futures from connected signals
|
||||||
|
// and in tests, we care about finishing all of these too.
|
||||||
|
QEventLoop().processEvents();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
m_futures.clear();
|
m_futures.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelManagerInterface::addFuture(const QFuture<void> &future)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_futuresMutex);
|
||||||
|
m_futures.append(future);
|
||||||
|
}
|
||||||
|
cleanupFutures();
|
||||||
|
}
|
||||||
|
|
||||||
Document::Ptr ModelManagerInterface::ensuredGetDocumentForPath(const QString &filePath)
|
Document::Ptr ModelManagerInterface::ensuredGetDocumentForPath(const QString &filePath)
|
||||||
{
|
{
|
||||||
QmlJS::Document::Ptr document = newestSnapshot().document(filePath);
|
QmlJS::Document::Ptr document = newestSnapshot().document(filePath);
|
||||||
|
|||||||
@@ -179,8 +179,9 @@ public:
|
|||||||
virtual ProjectInfo defaultProjectInfoForProject(ProjectExplorer::Project *project) const;
|
virtual ProjectInfo defaultProjectInfoForProject(ProjectExplorer::Project *project) const;
|
||||||
|
|
||||||
|
|
||||||
// Blocks until all parsing threads are done. Used for testing.
|
// Blocks until all parsing threads are done. Use for testing only!
|
||||||
void joinAllThreads();
|
void test_joinAllThreads();
|
||||||
|
void addFuture(const QFuture<void> &future);
|
||||||
|
|
||||||
QmlJS::Document::Ptr ensuredGetDocumentForPath(const QString &filePath);
|
QmlJS::Document::Ptr ensuredGetDocumentForPath(const QString &filePath);
|
||||||
static void importScan(QFutureInterface<void> &future, const WorkingCopy& workingCopyInternal,
|
static void importScan(QFutureInterface<void> &future, const WorkingCopy& workingCopyInternal,
|
||||||
@@ -271,7 +272,9 @@ private:
|
|||||||
|
|
||||||
PluginDumper *m_pluginDumper = nullptr;
|
PluginDumper *m_pluginDumper = nullptr;
|
||||||
|
|
||||||
|
mutable QMutex m_futuresMutex;
|
||||||
QList<QFuture<void>> m_futures;
|
QList<QFuture<void>> m_futures;
|
||||||
|
|
||||||
bool m_indexerDisabled = false;
|
bool m_indexerDisabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
|
|||||||
future.reportFinished(&infos);
|
future.reportFinished(&infos);
|
||||||
});
|
});
|
||||||
|
|
||||||
Utils::onFinished(future, this,
|
auto finalFuture = Utils::onFinished(future, this,
|
||||||
[this, libraryInfo, privatePlugin, libraryPath] (const QFuture<CppQmlTypesInfo>& future) {
|
[this, libraryInfo, privatePlugin, libraryPath] (const QFuture<CppQmlTypesInfo>& future) {
|
||||||
CppQmlTypesInfo infos = future.result();
|
CppQmlTypesInfo infos = future.result();
|
||||||
|
|
||||||
@@ -329,6 +329,7 @@ void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
|
|||||||
|
|
||||||
m_modelManager->updateLibraryInfo(libraryPath, libInfo);
|
m_modelManager->updateLibraryInfo(libraryPath, libInfo);
|
||||||
});
|
});
|
||||||
|
m_modelManager->addFuture(finalFuture);
|
||||||
} else {
|
} else {
|
||||||
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
|
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
|
||||||
libraryInfo.updateFingerprint();
|
libraryInfo.updateFingerprint();
|
||||||
@@ -525,12 +526,12 @@ void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
|||||||
const QString &libraryPath,
|
const QString &libraryPath,
|
||||||
QmlJS::LibraryInfo libraryInfo)
|
QmlJS::LibraryInfo libraryInfo)
|
||||||
{
|
{
|
||||||
Utils::onFinished(loadQmlTypeDescription(qmltypesFilePaths), this, [=](const QFuture<PluginDumper::QmlTypeDescription> &typesFuture)
|
auto future = Utils::onFinished(loadQmlTypeDescription(qmltypesFilePaths), this, [=](const QFuture<PluginDumper::QmlTypeDescription> &typesFuture)
|
||||||
{
|
{
|
||||||
PluginDumper::QmlTypeDescription typesResult = typesFuture.result();
|
PluginDumper::QmlTypeDescription typesResult = typesFuture.result();
|
||||||
if (!typesResult.dependencies.isEmpty())
|
if (!typesResult.dependencies.isEmpty())
|
||||||
{
|
{
|
||||||
Utils::onFinished(loadDependencies(typesResult.dependencies, QSharedPointer<QSet<QString>>()), this,
|
auto depFuture = Utils::onFinished(loadDependencies(typesResult.dependencies, QSharedPointer<QSet<QString>>()), this,
|
||||||
[typesResult, libraryInfo, libraryPath, this] (const QFuture<PluginDumper::DependencyInfo> &loadFuture)
|
[typesResult, libraryInfo, libraryPath, this] (const QFuture<PluginDumper::DependencyInfo> &loadFuture)
|
||||||
{
|
{
|
||||||
PluginDumper::DependencyInfo loadResult = loadFuture.result();
|
PluginDumper::DependencyInfo loadResult = loadFuture.result();
|
||||||
@@ -548,6 +549,7 @@ void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
|||||||
typesResult.moduleApis, objects);
|
typesResult.moduleApis, objects);
|
||||||
m_modelManager->updateLibraryInfo(libraryPath, libInfo);
|
m_modelManager->updateLibraryInfo(libraryPath, libInfo);
|
||||||
});
|
});
|
||||||
|
m_modelManager->addFuture(depFuture);
|
||||||
} else {
|
} else {
|
||||||
QmlJS::LibraryInfo libInfo = libraryInfo;
|
QmlJS::LibraryInfo libInfo = libraryInfo;
|
||||||
prepareLibraryInfo(libInfo, libraryPath, typesResult.dependencies,
|
prepareLibraryInfo(libInfo, libraryPath, typesResult.dependencies,
|
||||||
@@ -556,6 +558,7 @@ void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths,
|
|||||||
m_modelManager->updateLibraryInfo(libraryPath, libInfo);
|
m_modelManager->updateLibraryInfo(libraryPath, libInfo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
m_modelManager->addFuture(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginDumper::runQmlDump(const QmlJS::ModelManagerInterface::ProjectInfo &info,
|
void PluginDumper::runQmlDump(const QmlJS::ModelManagerInterface::ProjectInfo &info,
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ void QmlJSToolsPlugin::test_basic()
|
|||||||
|
|
||||||
const QString qmlFilePath = Core::ICore::resourcePath() + QLatin1String("/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml");
|
const QString qmlFilePath = Core::ICore::resourcePath() + QLatin1String("/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml");
|
||||||
modelManager->updateSourceFiles(QStringList(qmlFilePath), false);
|
modelManager->updateSourceFiles(QStringList(qmlFilePath), false);
|
||||||
modelManager->joinAllThreads();
|
modelManager->test_joinAllThreads();
|
||||||
|
|
||||||
Snapshot snapshot = modelManager->snapshot();
|
Snapshot snapshot = modelManager->snapshot();
|
||||||
Document::Ptr doc = snapshot.document(qmlFilePath);
|
Document::Ptr doc = snapshot.document(qmlFilePath);
|
||||||
|
|||||||
Reference in New Issue
Block a user