AutoTest: Restore former check state on reparse

When reparsing while modifying a file it can happen that
the parse failed for some reason or did not provide full
information due to syntax errors the code model cannot cope
with.
This in turn can purge items from the test tree. Re-adding
the items in a later reparse had just added the item and
did not take care of (former) check states.
Add simple caching mechanism to keep track of check states
and use them if available.

Task-number: QTCREATORBUG-24099
Change-Id: I3ca04f5fd58810df71582972e6fe96a00cfc48f1
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Stenger
2020-06-03 14:27:01 +02:00
parent 1c9194d2eb
commit 1b8c68f617
4 changed files with 46 additions and 3 deletions

View File

@@ -334,6 +334,7 @@ void TestCodeParser::scanForTests(const QStringList &fileList, const QList<ITest
}
parsingHasFailed = false;
TestTreeModel::instance()->updateCheckStateCache();
if (isFullParse) {
// remove qml files as they will be found automatically by the referencing cpp file
list = Utils::filtered(list, [] (const QString &fn) {

View File

@@ -134,6 +134,7 @@ public:
virtual bool shouldBeAddedAfterFiltering() const { return true; }
virtual QSet<QString> internalTargets() const;
QString cacheName() const { return m_filePath + ':' + m_name; }
protected:
void copyBasicDataFrom(const TestTreeItem *other);
typedef std::function<bool(const TestTreeItem *)> CompareFunction;

View File

@@ -91,6 +91,8 @@ void TestTreeModel::setupParsingConnections()
connect(sm, &SessionManager::startupProjectChanged, [this](Project *project) {
synchronizeTestFrameworks(); // we might have project settings
m_parser->onStartupProjectChanged(project);
m_checkStateCache.clear(); // TODO persist to project settings?
m_itemUseCache.clear();
});
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
@@ -291,6 +293,28 @@ void TestTreeModel::rebuild(const QList<Core::Id> &frameworkIds)
}
}
void TestTreeModel::updateCheckStateCache()
{
// raise generation for cached items and drop anything reaching 10th generation
const QList<QString> cachedNames = m_itemUseCache.keys();
for (const QString &cachedName : cachedNames) {
auto it = m_itemUseCache.find(cachedName);
if (it.value()++ >= 10) {
m_itemUseCache.erase(it);
m_checkStateCache.remove(cachedName);
}
}
for (Utils::TreeItem *rootNode : *rootItem()) {
rootNode->forAllChildren([this](Utils::TreeItem *child) {
auto childItem = static_cast<TestTreeItem *>(child);
const QString cacheName = childItem->cacheName();
m_checkStateCache.insert(cacheName, childItem->checked());
m_itemUseCache[cacheName] = 0; // explicitly mark as 0-generation
});
}
}
void TestTreeModel::removeFiles(const QStringList &files)
{
for (const QString &file : files)
@@ -404,14 +428,21 @@ void TestTreeModel::insertItemInParent(TestTreeItem *item, TestTreeItem *root, b
// only handle item's children and add them to the already present one
for (int row = 0, count = item->childCount(); row < count; ++row) {
TestTreeItem *child = fullCopyOf(item->childAt(row));
applyParentCheckState(otherItem, child);
// use check state of the original
child->setData(0, item->childAt(row)->checked(), Qt::CheckStateRole);
otherItem->appendChild(child);
revalidateCheckState(child);
}
delete item;
} else {
// we could try to add a non-checked item to a checked group or vice versa
applyParentCheckState(parentNode, item);
// restore former check state if available
auto cached = m_checkStateCache.find(item->cacheName());
if (cached != m_checkStateCache.end())
item->setData(0, cached.value(), Qt::CheckStateRole);
else
applyParentCheckState(parentNode, item);
parentNode->appendChild(item);
revalidateCheckState(parentNode);
}
}
@@ -492,6 +523,13 @@ void TestTreeModel::handleParseResult(const TestParseResult *result, TestTreeIte
TestTreeItem *newItem = result->createTestTreeItem();
QTC_ASSERT(newItem, return);
// restore former check state if available
newItem->forAllChildren([this](Utils::TreeItem *child) {
auto childItem = static_cast<TestTreeItem *>(child);
auto cached = m_checkStateCache.find(childItem->cacheName());
if (cached != m_checkStateCache.end())
childItem->setData(0, cached.value(), Qt::CheckStateRole);
});
// it might be necessary to "split" created item
filterAndInsert(newItem, parentNode, groupingEnabled);
}

View File

@@ -66,6 +66,7 @@ public:
void synchronizeTestFrameworks();
void rebuild(const QList<Core::Id> &frameworkIds);
void updateCheckStateCache();
#ifdef WITH_TESTS
int autoTestsCount() const;
int namedQuickTestsCount() const;
@@ -103,6 +104,8 @@ private:
QList<TestTreeItem *> testItemsByName(TestTreeItem *root, const QString &testName);
Internal::TestCodeParser *m_parser = nullptr;
QHash<QString, Qt::CheckState> m_checkStateCache; // could be enhanced to store expanded as well
QHash<QString, int> m_itemUseCache;
};
namespace Internal {