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:
Jarek Kobus
2023-04-23 15:29:11 +02:00
parent cc14eaf570
commit e849c19a33
8 changed files with 33 additions and 46 deletions

View File

@@ -389,17 +389,19 @@ QSet<QString> internalTargets(const FilePath &proFile)
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());
const FilePath proFile = parser->projectFileForMainCppFile(filePath);
if (!proFile.isEmpty()) {
TestTreeItem *root = framework()->rootNode();
root->forAllChildItems([proFile](TestTreeItem *it) {
if (it->proFile() == proFile)
it->markForRemoval(true);
});
for (const FilePath &filePath : filePaths) {
const FilePath proFile = parser->projectFileForMainCppFile(filePath);
if (!proFile.isEmpty()) {
TestTreeItem *root = framework()->rootNode();
root->forAllChildItems([proFile](TestTreeItem *it) {
if (it->proFile() == proFile)
it->markForRemoval(true);
});
}
}
}

View File

@@ -35,7 +35,7 @@ public:
bool removeOnSweepIfEmpty() const override;
TestTreeItem *createParentGroupNode() const override;
bool isGroupable() const override;
void markForRemovalRecursively(const Utils::FilePath &filePath) override;
void markForRemovalRecursively(const QSet<Utils::FilePath> &filePaths) override;
private:
TestTreeItem *findChildByFileNameAndType(const Utils::FilePath &filePath, const QString &name,
Type tType);

View File

@@ -307,24 +307,20 @@ void TestCodeParser::scanForTests(const FilePaths &fileList, const QList<ITestPa
TestTreeModel::instance()->updateCheckStateCache();
if (isFullParse) {
// remove qml files as they will be found automatically by the referencing cpp file
list = Utils::filtered(list, [](const FilePath &fn) {
return !fn.endsWith(".qml");
});
list = Utils::filtered(list, [](const FilePath &fn) { return !fn.endsWith(".qml"); });
if (!parsers.isEmpty()) {
for (ITestParser *parser : parsers) {
for (ITestParser *parser : parsers)
parser->framework()->rootNode()->markForRemovalRecursively(true);
}
} else {
emit requestRemoveAllFrameworkItems();
}
} else if (!parsers.isEmpty()) {
const auto set = Utils::toSet(list);
for (ITestParser *parser: parsers) {
for (const FilePath &filePath : std::as_const(list))
parser->framework()->rootNode()->markForRemovalRecursively(filePath);
parser->framework()->rootNode()->markForRemovalRecursively(set);
}
} else {
for (const FilePath &filePath : std::as_const(list))
emit requestRemoval(filePath);
emit requestRemoval(Utils::toSet(list));
}
QTC_ASSERT(!(isFullParse && list.isEmpty()), onFinished(true); return);

View File

@@ -53,7 +53,7 @@ signals:
void parsingStarted();
void parsingFinished();
void parsingFailed();
void requestRemoval(const Utils::FilePath &filePath);
void requestRemoval(const QSet<Utils::FilePath> &filePaths);
void requestRemoveAllFrameworkItems();
public:

View File

@@ -224,11 +224,11 @@ void TestTreeItem::markForRemovalRecursively(bool mark)
childItem(row)->markForRemovalRecursively(mark);
}
void TestTreeItem::markForRemovalRecursively(const FilePath &filepath)
void TestTreeItem::markForRemovalRecursively(const QSet<FilePath> &filePaths)
{
bool mark = filePath() == filepath;
forFirstLevelChildItems([&mark, &filepath](TestTreeItem *child) {
child->markForRemovalRecursively(filepath);
bool mark = filePaths.contains(filePath());
forFirstLevelChildItems([&mark, &filePaths](TestTreeItem *child) {
child->markForRemovalRecursively(filePaths);
mark &= child->markedForRemoval();
});
markForRemoval(mark);

View File

@@ -115,7 +115,7 @@ public:
void setProFile(const Utils::FilePath &proFile) { m_proFile = proFile; }
void markForRemoval(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; }
bool markedForRemoval() const { return m_status == MarkedForRemoval; }
bool newlyAdded() const { return m_status == NewlyAdded; }

View File

@@ -100,8 +100,8 @@ void TestTreeModel::setupParsingConnections()
m_parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection);
connect(cppMM, &CppEditor::CppModelManager::aboutToRemoveFiles,
this, [this](const QStringList &files) {
const FilePaths filesToRemove = FileUtils::toFilePathList(files);
removeFiles(filesToRemove);
markForRemoval(transform<QSet>(files, &FilePath::fromString));
sweep();
}, Qt::QueuedConnection);
connect(cppMM, &CppEditor::CppModelManager::projectPartsUpdated,
m_parser, &TestCodeParser::onProjectPartsUpdated);
@@ -109,11 +109,11 @@ void TestTreeModel::setupParsingConnections()
QmlJS::ModelManagerInterface *qmlJsMM = QmlJS::ModelManagerInterface::instance();
connect(qmlJsMM, &QmlJS::ModelManagerInterface::documentUpdated,
m_parser, &TestCodeParser::onQmlDocumentUpdated, Qt::QueuedConnection);
connect(qmlJsMM,
&QmlJS::ModelManagerInterface::aboutToRemoveFiles,
this,
&TestTreeModel::removeFiles,
Qt::QueuedConnection);
connect(qmlJsMM, &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
this, [this](const FilePaths &filePaths) {
markForRemoval(Utils::toSet(filePaths));
sweep();
}, Qt::QueuedConnection);
connectionsInitialized = true;
}
@@ -463,13 +463,6 @@ void TestTreeModel::clearFailedMarks()
m_failedStateCache.clear();
}
void TestTreeModel::removeFiles(const FilePaths &files)
{
for (const FilePath &file : files)
markForRemoval(file);
sweep();
}
void TestTreeModel::markAllFrameworkItemsForRemoval()
{
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 (int childRow = frameworkRoot->childCount() - 1; childRow >= 0; --childRow) {
TestTreeItem *child = frameworkRoot->childItem(childRow);
child->markForRemovalRecursively(filePath);
child->markForRemovalRecursively(filePaths);
}
}
}

View File

@@ -66,7 +66,7 @@ public:
#endif
void markAllFrameworkItemsForRemoval();
void markForRemoval(const Utils::FilePath &filePath);
void markForRemoval(const QSet<Utils::FilePath> &filePaths);
void sweep();
signals:
@@ -83,7 +83,6 @@ private:
void handleParseResult(const TestParseResult *result, TestTreeItem *rootNode);
void removeAllTestItems();
void removeAllTestToolItems();
void removeFiles(const Utils::FilePaths &files);
bool sweepChildren(TestTreeItem *item);
void insertItemInParent(TestTreeItem *item, TestTreeItem *root, bool groupingEnabled);
void revalidateCheckState(ITestTreeItem *item);