forked from qt-creator/qt-creator
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:
@@ -334,6 +334,7 @@ void TestCodeParser::scanForTests(const QStringList &fileList, const QList<ITest
|
|||||||
}
|
}
|
||||||
|
|
||||||
parsingHasFailed = false;
|
parsingHasFailed = false;
|
||||||
|
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 QString &fn) {
|
list = Utils::filtered(list, [] (const QString &fn) {
|
||||||
|
@@ -134,6 +134,7 @@ public:
|
|||||||
virtual bool shouldBeAddedAfterFiltering() const { return true; }
|
virtual bool shouldBeAddedAfterFiltering() const { return true; }
|
||||||
virtual QSet<QString> internalTargets() const;
|
virtual QSet<QString> internalTargets() const;
|
||||||
|
|
||||||
|
QString cacheName() const { return m_filePath + ':' + m_name; }
|
||||||
protected:
|
protected:
|
||||||
void copyBasicDataFrom(const TestTreeItem *other);
|
void copyBasicDataFrom(const TestTreeItem *other);
|
||||||
typedef std::function<bool(const TestTreeItem *)> CompareFunction;
|
typedef std::function<bool(const TestTreeItem *)> CompareFunction;
|
||||||
|
@@ -91,6 +91,8 @@ void TestTreeModel::setupParsingConnections()
|
|||||||
connect(sm, &SessionManager::startupProjectChanged, [this](Project *project) {
|
connect(sm, &SessionManager::startupProjectChanged, [this](Project *project) {
|
||||||
synchronizeTestFrameworks(); // we might have project settings
|
synchronizeTestFrameworks(); // we might have project settings
|
||||||
m_parser->onStartupProjectChanged(project);
|
m_parser->onStartupProjectChanged(project);
|
||||||
|
m_checkStateCache.clear(); // TODO persist to project settings?
|
||||||
|
m_itemUseCache.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
|
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)
|
void TestTreeModel::removeFiles(const QStringList &files)
|
||||||
{
|
{
|
||||||
for (const QString &file : 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
|
// only handle item's children and add them to the already present one
|
||||||
for (int row = 0, count = item->childCount(); row < count; ++row) {
|
for (int row = 0, count = item->childCount(); row < count; ++row) {
|
||||||
TestTreeItem *child = fullCopyOf(item->childAt(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);
|
otherItem->appendChild(child);
|
||||||
|
revalidateCheckState(child);
|
||||||
}
|
}
|
||||||
delete item;
|
delete item;
|
||||||
} else {
|
} else {
|
||||||
// we could try to add a non-checked item to a checked group or vice versa
|
// 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);
|
applyParentCheckState(parentNode, item);
|
||||||
parentNode->appendChild(item);
|
parentNode->appendChild(item);
|
||||||
|
revalidateCheckState(parentNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,6 +523,13 @@ void TestTreeModel::handleParseResult(const TestParseResult *result, TestTreeIte
|
|||||||
TestTreeItem *newItem = result->createTestTreeItem();
|
TestTreeItem *newItem = result->createTestTreeItem();
|
||||||
QTC_ASSERT(newItem, return);
|
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
|
// it might be necessary to "split" created item
|
||||||
filterAndInsert(newItem, parentNode, groupingEnabled);
|
filterAndInsert(newItem, parentNode, groupingEnabled);
|
||||||
}
|
}
|
||||||
|
@@ -66,6 +66,7 @@ public:
|
|||||||
void synchronizeTestFrameworks();
|
void synchronizeTestFrameworks();
|
||||||
void rebuild(const QList<Core::Id> &frameworkIds);
|
void rebuild(const QList<Core::Id> &frameworkIds);
|
||||||
|
|
||||||
|
void updateCheckStateCache();
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
int autoTestsCount() const;
|
int autoTestsCount() const;
|
||||||
int namedQuickTestsCount() const;
|
int namedQuickTestsCount() const;
|
||||||
@@ -103,6 +104,8 @@ private:
|
|||||||
QList<TestTreeItem *> testItemsByName(TestTreeItem *root, const QString &testName);
|
QList<TestTreeItem *> testItemsByName(TestTreeItem *root, const QString &testName);
|
||||||
|
|
||||||
Internal::TestCodeParser *m_parser = nullptr;
|
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 {
|
namespace Internal {
|
||||||
|
Reference in New Issue
Block a user