From 1b8c68f61727d6d04b1375b969b5f96688e67fde Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 3 Jun 2020 14:27:01 +0200 Subject: [PATCH] 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 --- src/plugins/autotest/testcodeparser.cpp | 1 + src/plugins/autotest/testtreeitem.h | 1 + src/plugins/autotest/testtreemodel.cpp | 44 +++++++++++++++++++++++-- src/plugins/autotest/testtreemodel.h | 3 ++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index c8c74075aed..0f37ff6f5ba 100644 --- a/src/plugins/autotest/testcodeparser.cpp +++ b/src/plugins/autotest/testcodeparser.cpp @@ -334,6 +334,7 @@ void TestCodeParser::scanForTests(const QStringList &fileList, const QListupdateCheckStateCache(); if (isFullParse) { // remove qml files as they will be found automatically by the referencing cpp file list = Utils::filtered(list, [] (const QString &fn) { diff --git a/src/plugins/autotest/testtreeitem.h b/src/plugins/autotest/testtreeitem.h index 8d77ba0a6c5..ea46cb5dc18 100644 --- a/src/plugins/autotest/testtreeitem.h +++ b/src/plugins/autotest/testtreeitem.h @@ -134,6 +134,7 @@ public: virtual bool shouldBeAddedAfterFiltering() const { return true; } virtual QSet internalTargets() const; + QString cacheName() const { return m_filePath + ':' + m_name; } protected: void copyBasicDataFrom(const TestTreeItem *other); typedef std::function CompareFunction; diff --git a/src/plugins/autotest/testtreemodel.cpp b/src/plugins/autotest/testtreemodel.cpp index c4d7510b505..6fbae85cb5f 100644 --- a/src/plugins/autotest/testtreemodel.cpp +++ b/src/plugins/autotest/testtreemodel.cpp @@ -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 &frameworkIds) } } +void TestTreeModel::updateCheckStateCache() +{ + // raise generation for cached items and drop anything reaching 10th generation + const QList 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(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(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); } diff --git a/src/plugins/autotest/testtreemodel.h b/src/plugins/autotest/testtreemodel.h index 7d64db73f06..909ab3fac45 100644 --- a/src/plugins/autotest/testtreemodel.h +++ b/src/plugins/autotest/testtreemodel.h @@ -66,6 +66,7 @@ public: void synchronizeTestFrameworks(); void rebuild(const QList &frameworkIds); + void updateCheckStateCache(); #ifdef WITH_TESTS int autoTestsCount() const; int namedQuickTestsCount() const; @@ -103,6 +104,8 @@ private: QList testItemsByName(TestTreeItem *root, const QString &testName); Internal::TestCodeParser *m_parser = nullptr; + QHash m_checkStateCache; // could be enhanced to store expanded as well + QHash m_itemUseCache; }; namespace Internal {