forked from qt-creator/qt-creator
AutoTest: Optimize TestCodeParser::scanForTests
In case of loading a Creator project, after the Scanning For Tests finished, the scanForTests() called by TestCodeParser::onFinished() freezed the main thread for about 1 second. In this case requestRemoval() signal was emitted nearly 1000 times. Optimize the internals: 1. Don't emit requestRemoval() for every single file but emit it just once passing a QSet<FilePath> instead. 2. Adapt some other callees and callers to work on QSet<FilePath> instead on a single FilePath. This change constraints the freeze to about 2 ms. Change-Id: If23b85b495c125d82eb3c8b5a6912349df122745 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -389,17 +389,19 @@ QSet<QString> internalTargets(const FilePath &proFile)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickTestTreeItem::markForRemovalRecursively(const FilePath &filePath)
|
void QuickTestTreeItem::markForRemovalRecursively(const QSet<FilePath> &filePaths)
|
||||||
{
|
{
|
||||||
TestTreeItem::markForRemovalRecursively(filePath);
|
TestTreeItem::markForRemovalRecursively(filePaths);
|
||||||
auto parser = static_cast<QuickTestParser *>(framework()->testParser());
|
auto parser = static_cast<QuickTestParser *>(framework()->testParser());
|
||||||
const FilePath proFile = parser->projectFileForMainCppFile(filePath);
|
for (const FilePath &filePath : filePaths) {
|
||||||
if (!proFile.isEmpty()) {
|
const FilePath proFile = parser->projectFileForMainCppFile(filePath);
|
||||||
TestTreeItem *root = framework()->rootNode();
|
if (!proFile.isEmpty()) {
|
||||||
root->forAllChildItems([proFile](TestTreeItem *it) {
|
TestTreeItem *root = framework()->rootNode();
|
||||||
if (it->proFile() == proFile)
|
root->forAllChildItems([proFile](TestTreeItem *it) {
|
||||||
it->markForRemoval(true);
|
if (it->proFile() == proFile)
|
||||||
});
|
it->markForRemoval(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public:
|
|||||||
bool removeOnSweepIfEmpty() const override;
|
bool removeOnSweepIfEmpty() const override;
|
||||||
TestTreeItem *createParentGroupNode() const override;
|
TestTreeItem *createParentGroupNode() const override;
|
||||||
bool isGroupable() const override;
|
bool isGroupable() const override;
|
||||||
void markForRemovalRecursively(const Utils::FilePath &filePath) override;
|
void markForRemovalRecursively(const QSet<Utils::FilePath> &filePaths) override;
|
||||||
private:
|
private:
|
||||||
TestTreeItem *findChildByFileNameAndType(const Utils::FilePath &filePath, const QString &name,
|
TestTreeItem *findChildByFileNameAndType(const Utils::FilePath &filePath, const QString &name,
|
||||||
Type tType);
|
Type tType);
|
||||||
|
|||||||
@@ -307,24 +307,20 @@ void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestPa
|
|||||||
TestTreeModel::instance()->updateCheckStateCache();
|
TestTreeModel::instance()->updateCheckStateCache();
|
||||||
if (isFullParse) {
|
if (isFullParse) {
|
||||||
// remove qml files as they will be found automatically by the referencing cpp file
|
// remove qml files as they will be found automatically by the referencing cpp file
|
||||||
list = Utils::filtered(list, [](const FilePath &fn) {
|
list = Utils::filtered(list, [](const FilePath &fn) { return !fn.endsWith(".qml"); });
|
||||||
return !fn.endsWith(".qml");
|
|
||||||
});
|
|
||||||
if (!parsers.isEmpty()) {
|
if (!parsers.isEmpty()) {
|
||||||
for (ITestParser *parser : parsers) {
|
for (ITestParser *parser : parsers)
|
||||||
parser->framework()->rootNode()->markForRemovalRecursively(true);
|
parser->framework()->rootNode()->markForRemovalRecursively(true);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
emit requestRemoveAllFrameworkItems();
|
emit requestRemoveAllFrameworkItems();
|
||||||
}
|
}
|
||||||
} else if (!parsers.isEmpty()) {
|
} else if (!parsers.isEmpty()) {
|
||||||
|
const auto set = Utils::toSet(list);
|
||||||
for (ITestParser *parser: parsers) {
|
for (ITestParser *parser: parsers) {
|
||||||
for (const FilePath &filePath : std::as_const(list))
|
parser->framework()->rootNode()->markForRemovalRecursively(set);
|
||||||
parser->framework()->rootNode()->markForRemovalRecursively(filePath);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const FilePath &filePath : std::as_const(list))
|
emit requestRemoval(Utils::toSet(list));
|
||||||
emit requestRemoval(filePath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QTC_ASSERT(!(isFullParse && list.isEmpty()), onFinished(true); return);
|
QTC_ASSERT(!(isFullParse && list.isEmpty()), onFinished(true); return);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ signals:
|
|||||||
void parsingStarted();
|
void parsingStarted();
|
||||||
void parsingFinished();
|
void parsingFinished();
|
||||||
void parsingFailed();
|
void parsingFailed();
|
||||||
void requestRemoval(const Utils::FilePath &filePath);
|
void requestRemoval(const QSet<Utils::FilePath> &filePaths);
|
||||||
void requestRemoveAllFrameworkItems();
|
void requestRemoveAllFrameworkItems();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -224,11 +224,11 @@ void TestTreeItem::markForRemovalRecursively(bool mark)
|
|||||||
childItem(row)->markForRemovalRecursively(mark);
|
childItem(row)->markForRemovalRecursively(mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestTreeItem::markForRemovalRecursively(const FilePath &filepath)
|
void TestTreeItem::markForRemovalRecursively(const QSet<FilePath> &filePaths)
|
||||||
{
|
{
|
||||||
bool mark = filePath() == filepath;
|
bool mark = filePaths.contains(filePath());
|
||||||
forFirstLevelChildItems([&mark, &filepath](TestTreeItem *child) {
|
forFirstLevelChildItems([&mark, &filePaths](TestTreeItem *child) {
|
||||||
child->markForRemovalRecursively(filepath);
|
child->markForRemovalRecursively(filePaths);
|
||||||
mark &= child->markedForRemoval();
|
mark &= child->markedForRemoval();
|
||||||
});
|
});
|
||||||
markForRemoval(mark);
|
markForRemoval(mark);
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public:
|
|||||||
void setProFile(const Utils::FilePath &proFile) { m_proFile = proFile; }
|
void setProFile(const Utils::FilePath &proFile) { m_proFile = proFile; }
|
||||||
void markForRemoval(bool mark);
|
void markForRemoval(bool mark);
|
||||||
void markForRemovalRecursively(bool mark);
|
void markForRemovalRecursively(bool mark);
|
||||||
virtual void markForRemovalRecursively(const Utils::FilePath &filepath);
|
virtual void markForRemovalRecursively(const QSet<Utils::FilePath> &filePaths);
|
||||||
virtual bool removeOnSweepIfEmpty() const { return type() == GroupNode; }
|
virtual bool removeOnSweepIfEmpty() const { return type() == GroupNode; }
|
||||||
bool markedForRemoval() const { return m_status == MarkedForRemoval; }
|
bool markedForRemoval() const { return m_status == MarkedForRemoval; }
|
||||||
bool newlyAdded() const { return m_status == NewlyAdded; }
|
bool newlyAdded() const { return m_status == NewlyAdded; }
|
||||||
|
|||||||
@@ -100,8 +100,8 @@ void TestTreeModel::setupParsingConnections()
|
|||||||
m_parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection);
|
m_parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection);
|
||||||
connect(cppMM, &CppEditor::CppModelManager::aboutToRemoveFiles,
|
connect(cppMM, &CppEditor::CppModelManager::aboutToRemoveFiles,
|
||||||
this, [this](const QStringList &files) {
|
this, [this](const QStringList &files) {
|
||||||
const FilePaths filesToRemove = FileUtils::toFilePathList(files);
|
markForRemoval(transform<QSet>(files, &FilePath::fromString));
|
||||||
removeFiles(filesToRemove);
|
sweep();
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
connect(cppMM, &CppEditor::CppModelManager::projectPartsUpdated,
|
connect(cppMM, &CppEditor::CppModelManager::projectPartsUpdated,
|
||||||
m_parser, &TestCodeParser::onProjectPartsUpdated);
|
m_parser, &TestCodeParser::onProjectPartsUpdated);
|
||||||
@@ -109,11 +109,11 @@ void TestTreeModel::setupParsingConnections()
|
|||||||
QmlJS::ModelManagerInterface *qmlJsMM = QmlJS::ModelManagerInterface::instance();
|
QmlJS::ModelManagerInterface *qmlJsMM = QmlJS::ModelManagerInterface::instance();
|
||||||
connect(qmlJsMM, &QmlJS::ModelManagerInterface::documentUpdated,
|
connect(qmlJsMM, &QmlJS::ModelManagerInterface::documentUpdated,
|
||||||
m_parser, &TestCodeParser::onQmlDocumentUpdated, Qt::QueuedConnection);
|
m_parser, &TestCodeParser::onQmlDocumentUpdated, Qt::QueuedConnection);
|
||||||
connect(qmlJsMM,
|
connect(qmlJsMM, &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
|
||||||
&QmlJS::ModelManagerInterface::aboutToRemoveFiles,
|
this, [this](const FilePaths &filePaths) {
|
||||||
this,
|
markForRemoval(Utils::toSet(filePaths));
|
||||||
&TestTreeModel::removeFiles,
|
sweep();
|
||||||
Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
connectionsInitialized = true;
|
connectionsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,13 +463,6 @@ void TestTreeModel::clearFailedMarks()
|
|||||||
m_failedStateCache.clear();
|
m_failedStateCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestTreeModel::removeFiles(const FilePaths &files)
|
|
||||||
{
|
|
||||||
for (const FilePath &file : files)
|
|
||||||
markForRemoval(file);
|
|
||||||
sweep();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestTreeModel::markAllFrameworkItemsForRemoval()
|
void TestTreeModel::markAllFrameworkItemsForRemoval()
|
||||||
{
|
{
|
||||||
for (TestTreeItem *frameworkRoot : frameworkRootNodes()) {
|
for (TestTreeItem *frameworkRoot : frameworkRootNodes()) {
|
||||||
@@ -479,15 +472,12 @@ void TestTreeModel::markAllFrameworkItemsForRemoval()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestTreeModel::markForRemoval(const FilePath &filePath)
|
void TestTreeModel::markForRemoval(const QSet<Utils::FilePath> &filePaths)
|
||||||
{
|
{
|
||||||
if (filePath.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (TestTreeItem *frameworkRoot : frameworkRootNodes()) {
|
for (TestTreeItem *frameworkRoot : frameworkRootNodes()) {
|
||||||
for (int childRow = frameworkRoot->childCount() - 1; childRow >= 0; --childRow) {
|
for (int childRow = frameworkRoot->childCount() - 1; childRow >= 0; --childRow) {
|
||||||
TestTreeItem *child = frameworkRoot->childItem(childRow);
|
TestTreeItem *child = frameworkRoot->childItem(childRow);
|
||||||
child->markForRemovalRecursively(filePath);
|
child->markForRemovalRecursively(filePaths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void markAllFrameworkItemsForRemoval();
|
void markAllFrameworkItemsForRemoval();
|
||||||
void markForRemoval(const Utils::FilePath &filePath);
|
void markForRemoval(const QSet<Utils::FilePath> &filePaths);
|
||||||
void sweep();
|
void sweep();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -83,7 +83,6 @@ private:
|
|||||||
void handleParseResult(const TestParseResult *result, TestTreeItem *rootNode);
|
void handleParseResult(const TestParseResult *result, TestTreeItem *rootNode);
|
||||||
void removeAllTestItems();
|
void removeAllTestItems();
|
||||||
void removeAllTestToolItems();
|
void removeAllTestToolItems();
|
||||||
void removeFiles(const Utils::FilePaths &files);
|
|
||||||
bool sweepChildren(TestTreeItem *item);
|
bool sweepChildren(TestTreeItem *item);
|
||||||
void insertItemInParent(TestTreeItem *item, TestTreeItem *root, bool groupingEnabled);
|
void insertItemInParent(TestTreeItem *item, TestTreeItem *root, bool groupingEnabled);
|
||||||
void revalidateCheckState(ITestTreeItem *item);
|
void revalidateCheckState(ITestTreeItem *item);
|
||||||
|
|||||||
Reference in New Issue
Block a user