Commit 2ce99680 authored by Christian Stenger's avatar Christian Stenger
Browse files

Decouple code parser and model



Additionally some minor refactorings to increase readability.

Change-Id: I0be120fcedcf31dbb0116d84f0d3c23cf95e7d91
Reviewed-by: default avatarAndre Poenitz <andre.poenitz@theqtcompany.com>
parent 61c50e4a
This diff is collapsed.
......@@ -19,6 +19,9 @@
#ifndef TESTCODEPARSER_H
#define TESTCODEPARSER_H
#include "testtreeitem.h"
#include "testtreemodel.h"
#include <cplusplus/CppDocument.h>
#include <qmljs/qmljsdocument.h>
......@@ -35,8 +38,6 @@ namespace Internal {
struct TestCodeLocationAndType;
class TestInfo;
class TestTreeModel;
class TestTreeItem;
class TestCodeParser : public QObject
{
......@@ -46,6 +47,14 @@ public:
virtual ~TestCodeParser();
signals:
void cacheCleared();
void testItemCreated(const TestTreeItem &item, TestTreeModel::Type type);
void testItemsCreated(const QList<TestTreeItem> &itemList, TestTreeModel::Type type);
void testItemModified(TestTreeItem tItem, TestTreeModel::Type type, const QString &file);
void testItemsRemoved(const QString &filePath, TestTreeModel::Type type);
void unnamedQuickTestsUpdated(const QString &filePath, const QString &mainFile,
const QMap<QString, TestCodeLocationAndType> &functions);
void unnamedQuickTestsRemoved(const QString &filePath);
public slots:
void emitUpdateTestTree();
......@@ -63,16 +72,15 @@ private:
void clearMaps();
void removeTestsIfNecessary(const QString &fileName);
void removeTestsIfNecessaryByProFile(const QString &proFile);
void removeUnnamedQuickTests(const QString &fileName, const QStringList &testFunctions);
void recreateUnnamedQuickTest(const QMap<QString, TestCodeLocationAndType> &testFunctions,
const QString &mainFile, TestTreeItem *rootItem);
void onTaskStarted(Core::Id type);
void onAllTasksFinished(Core::Id type);
void updateModelAndCppDocMap(CPlusPlus::Document::Ptr document, const QString &declFileName,
TestTreeItem *testItem, TestTreeItem *rootItem);
void updateModelAndQuickDocMap(QmlJS::Document::Ptr qmlDoc, const QString &currentQmlJSFile,
const QString &referencingFileName,
TestTreeItem *testItem, TestTreeItem *rootItem);
void updateUnnamedQuickTests(const QString &fileName, const QString &mainFile,
const QMap<QString, TestCodeLocationAndType> &functions);
void updateModelAndCppDocMap(CPlusPlus::Document::Ptr document,
const QString &declaringFile, TestTreeItem &testItem);
void updateModelAndQuickDocMap(QmlJS::Document::Ptr document,
const QString &referencingFile, TestTreeItem &testItem);
TestTreeModel *m_model;
QMap<QString, TestInfo> m_cppDocMap;
......
......@@ -71,7 +71,7 @@ TestTreeItem::TestTreeItem(const TestTreeItem &other)
}
}
TestTreeItem *TestTreeItem::child(int row)
TestTreeItem *TestTreeItem::child(int row) const
{
return m_children.at(row);
}
......
......@@ -41,7 +41,7 @@ public:
virtual ~TestTreeItem();
TestTreeItem(const TestTreeItem& other);
TestTreeItem *child(int row);
TestTreeItem *child(int row) const;
TestTreeItem *parent() const;
void appendChild(TestTreeItem *child);
int row() const;
......@@ -80,6 +80,13 @@ private:
QList<TestTreeItem *> m_children;
};
struct TestCodeLocationAndType {
QString m_fileName;
unsigned m_line;
unsigned m_column;
TestTreeItem::Type m_type;
};
} // namespace Internal
} // namespace Autotest
......
......@@ -50,6 +50,17 @@ TestTreeModel::TestTreeModel(QObject *parent) :
{
m_rootItem->appendChild(m_autoTestRootItem);
m_rootItem->appendChild(m_quickTestRootItem);
connect(m_parser, &TestCodeParser::cacheCleared, this, &TestTreeModel::removeAllTestItems);
connect(m_parser, &TestCodeParser::testItemCreated, this, &TestTreeModel::addTestTreeItem);
connect(m_parser, &TestCodeParser::testItemsCreated, this, &TestTreeModel::addTestTreeItems);
connect(m_parser, &TestCodeParser::testItemModified, this, &TestTreeModel::modifyTestTreeItem);
connect(m_parser, &TestCodeParser::testItemsRemoved, this, &TestTreeModel::removeTestTreeItems);
connect(m_parser, &TestCodeParser::unnamedQuickTestsUpdated,
this, &TestTreeModel::updateUnnamedQuickTest);
connect(m_parser, &TestCodeParser::unnamedQuickTestsRemoved,
this, &TestTreeModel::removeUnnamedQuickTests);
m_parser->updateTestTree();
// CppTools::CppModelManagerInterface *cppMM = CppTools::CppModelManagerInterface::instance();
......@@ -524,6 +535,62 @@ QList<TestConfiguration *> TestTreeModel::getSelectedTests() const
return result;
}
QString TestTreeModel::getMainFileForUnnamedQuickTest(const QString &qmlFile) const
{
const TestTreeItem *unnamed = unnamedQuickTests();
for (int row = 0, count = unnamed->childCount(); row < count; ++row) {
const TestTreeItem *child = unnamed->child(row);
if (qmlFile == child->filePath())
return child->mainFile();
}
return QString();
}
void TestTreeModel::qmlFilesAndFunctionNamesForMainFile(const QString &mainFile,
QSet<QString> *filePaths,
QList<QString> *functionNames) const
{
TestTreeItem *unnamed = unnamedQuickTests();
for (int i = 0; i < unnamed->childCount(); ++i) {
const TestTreeItem *child = unnamed->child(i);
if (child->mainFile() == mainFile) {
filePaths->insert(child->filePath());
functionNames->append(child->name());
}
}
}
QList<QString> TestTreeModel::getUnnamedQuickTestFunctions() const
{
TestTreeItem *unnamed = unnamedQuickTests();
if (unnamed)
return unnamed->getChildNames();
return QList<QString>();
}
QSet<QString> TestTreeModel::qmlFilesForProFile(const QString &proFile) const
{
QSet<QString> filePaths;
CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
if (TestTreeItem *unnamed = unnamedQuickTests()) {
for (int i = 0; i < unnamed->childCount(); ++i) {
const TestTreeItem *child = unnamed->child(i);
QList<CppTools::ProjectPart::Ptr> ppList = modelManager->projectPart(child->mainFile());
if (ppList.size() && ppList.at(0)->projectFile == proFile)
filePaths.insert(child->filePath());
}
}
return filePaths;
}
bool TestTreeModel::hasUnnamedQuickTests() const
{
for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row)
if (m_quickTestRootItem->child(row)->name().isEmpty())
return true;
return false;
}
TestTreeItem *TestTreeModel::unnamedQuickTests() const
{
for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) {
......@@ -534,177 +601,213 @@ TestTreeItem *TestTreeModel::unnamedQuickTests() const
return 0;
}
void TestTreeModel::modifyAutoTestSubtree(int row, TestTreeItem *newItem)
void TestTreeModel::removeUnnamedQuickTests(const QString &filePath)
{
QModelIndex toBeModifiedIndex = index(0, 0).child(row, 0);
modifyTestSubtree(toBeModifiedIndex, newItem);
}
TestTreeItem *unnamedQT = unnamedQuickTests();
if (!unnamedQT)
return;
void TestTreeModel::removeAutoTestSubtreeByFilePath(const QString &file)
{
const QModelIndex atRootIndex = index(0, 0);
const int count = rowCount(atRootIndex);
for (int row = 0; row < count; ++row) {
const QModelIndex childIndex = atRootIndex.child(row, 0);
TestTreeItem *childItem = static_cast<TestTreeItem *>(childIndex.internalPointer());
if (file == childItem->filePath()) {
removeRow(row, atRootIndex);
break;
}
const QModelIndex unnamedQTIndex = index(1, 0).child(unnamedQT->row(), 0);
for (int childRow = unnamedQT->childCount() - 1; childRow >= 0; --childRow) {
const TestTreeItem *child = unnamedQT->child(childRow);
if (filePath == child->filePath())
removeRow(childRow, unnamedQTIndex);
}
if (unnamedQT->childCount() == 0)
removeRow(unnamedQT->row(), unnamedQTIndex.parent());
emit testTreeModelChanged();
}
void TestTreeModel::addAutoTest(TestTreeItem *newItem)
void TestTreeModel::addTestTreeItem(const TestTreeItem &item, TestTreeModel::Type type)
{
beginInsertRows(index(0, 0), m_autoTestRootItem->childCount(), m_autoTestRootItem->childCount());
m_autoTestRootItem->appendChild(newItem);
TestTreeItem *parent = rootItemForType(type);
QModelIndex index = rootIndexForType(type);
TestTreeItem *toBeAdded = new TestTreeItem(item);
toBeAdded->setParent(parent);
beginInsertRows(index, parent->childCount(), parent->childCount());
parent->appendChild(toBeAdded);
endInsertRows();
emit testTreeModelChanged();
}
void TestTreeModel::removeAllAutoTests()
void TestTreeModel::addTestTreeItems(const QList<TestTreeItem> &itemList, TestTreeModel::Type type)
{
beginResetModel();
m_autoTestRootItem->removeChildren();
endResetModel();
TestTreeItem *parent = rootItemForType(type);
QModelIndex index = rootIndexForType(type);
beginInsertRows(index, parent->childCount(), parent->childCount() + itemList.size() - 1);
foreach (const TestTreeItem &item, itemList) {
TestTreeItem *toBeAdded = new TestTreeItem(item);
toBeAdded->setParent(parent);
parent->appendChild(toBeAdded);
}
endInsertRows();
emit testTreeModelChanged();
}
void TestTreeModel::modifyQuickTestSubtree(int row, TestTreeItem *newItem)
void TestTreeModel::updateUnnamedQuickTest(const QString &fileName, const QString &mainFile,
const QMap<QString, TestCodeLocationAndType> &functions)
{
QModelIndex toBeModifiedIndex = index(1, 0).child(row, 0);
modifyTestSubtree(toBeModifiedIndex, newItem);
}
removeUnnamedQuickTests(fileName);
TestTreeItem unnamed = hasUnnamedQuickTests()
? TestTreeItem(*unnamedQuickTests())
: TestTreeItem(QString(), QString(), TestTreeItem::TEST_CLASS, rootItemForType(QuickTest));
void TestTreeModel::removeQuickTestSubtreeByFilePath(const QString &file)
{
const QModelIndex qtRootIndex = index(1, 0);
for (int row = 0, count = rowCount(qtRootIndex); row < count; ++row) {
const QModelIndex childIndex = qtRootIndex.child(row, 0);
const TestTreeItem *childItem = static_cast<TestTreeItem *>(childIndex.internalPointer());
if (file == childItem->filePath()) {
removeRow(row, qtRootIndex);
break;
}
foreach (const QString &functionName, functions.keys()) {
const TestCodeLocationAndType locationAndType = functions.value(functionName);
TestTreeItem *testFunction = new TestTreeItem(functionName, locationAndType.m_fileName,
locationAndType.m_type, &unnamed);
testFunction->setLine(locationAndType.m_line);
testFunction->setColumn(locationAndType.m_column);
testFunction->setMainFile(mainFile);
unnamed.appendChild(testFunction);
}
emit testTreeModelChanged();
if (hasUnnamedQuickTests())
modifyTestTreeItem(unnamed, QuickTest, QString());
else
addTestTreeItem(unnamed, QuickTest);
}
void TestTreeModel::addQuickTest(TestTreeItem *newItem)
void TestTreeModel::modifyTestTreeItem(TestTreeItem item, TestTreeModel::Type type, const QString &file)
{
beginInsertRows(index(1, 0), m_quickTestRootItem->childCount(), m_quickTestRootItem->childCount());
m_quickTestRootItem->appendChild(newItem);
endInsertRows();
emit testTreeModelChanged();
QModelIndex index = rootIndexForType(type);
TestTreeItem *parent = rootItemForType(type);
item.setParent(parent);
if (file.isEmpty()) {
if (TestTreeItem *unnamed = unnamedQuickTests()) {
index = index.child(unnamed->row(), 0);
modifyTestSubtree(index, item);
}
} else {
for (int row = 0; row < parent->childCount(); ++row) {
if (parent->child(row)->filePath() == file) {
index = index.child(row, 0);
modifyTestSubtree(index, item);
break;
}
}
}
}
void TestTreeModel::removeAllQuickTests()
void TestTreeModel::removeAllTestItems()
{
beginResetModel();
m_autoTestRootItem->removeChildren();
m_quickTestRootItem->removeChildren();
endResetModel();
emit testTreeModelChanged();
}
bool TestTreeModel::removeUnnamedQuickTests(const QString &filePath)
void TestTreeModel::removeTestTreeItems(const QString &filePath, Type type)
{
TestTreeItem *unnamedQT = unnamedQuickTests();
if (!unnamedQT)
return false;
bool removed = false;
const QModelIndex unnamedQTIndex = index(1, 0).child(unnamedQT->row(), 0);
for (int childRow = unnamedQT->childCount() - 1; childRow >= 0; --childRow) {
const TestTreeItem *child = unnamedQT->child(childRow);
if (filePath == child->filePath())
removed |= removeRow(childRow, unnamedQTIndex);
const QModelIndex rootIndex = rootIndexForType(type);
const int count = rowCount(rootIndex);
for (int row = count - 1; row >= 0; --row) {
const QModelIndex childIndex = rootIndex.child(row, 0);
TestTreeItem *childItem = static_cast<TestTreeItem *>(childIndex.internalPointer());
if (filePath == childItem->filePath())
removed |= removeRow(row, rootIndex);
}
if (removed)
emit testTreeModelChanged();
}
if (unnamedQT->childCount() == 0)
removeRow(unnamedQT->row(), unnamedQTIndex.parent());
emit testTreeModelChanged();
return removed;
TestTreeItem *TestTreeModel::rootItemForType(TestTreeModel::Type type)
{
switch (type) {
case AutoTest:
return m_autoTestRootItem;
case QuickTest:
return m_quickTestRootItem;
}
QTC_ASSERT(false, return 0);
}
QModelIndex TestTreeModel::rootIndexForType(TestTreeModel::Type type)
{
switch (type) {
case AutoTest:
return index(0, 0);
case QuickTest:
return index(1, 0);
}
QTC_ASSERT(false, return QModelIndex());
}
void TestTreeModel::modifyTestSubtree(QModelIndex &toBeModifiedIndex, TestTreeItem *newItem)
void TestTreeModel::modifyTestSubtree(QModelIndex &toBeModifiedIndex, const TestTreeItem &newItem)
{
if (!toBeModifiedIndex.isValid())
return;
static QVector<int> modificationRoles = QVector<int>() << Qt::DisplayRole
<< Qt::ToolTipRole << LinkRole;
TestTreeItem *toBeModifiedItem = static_cast<TestTreeItem *>(toBeModifiedIndex.internalPointer());
if (toBeModifiedItem->modifyContent(newItem))
emit dataChanged(toBeModifiedIndex, toBeModifiedIndex, modificationRoles);
if (toBeModifiedItem->modifyContent(&newItem))
emit dataChanged(toBeModifiedIndex, toBeModifiedIndex,
QVector<int>() << Qt::DisplayRole << Qt::ToolTipRole << LinkRole);
// process sub-items as well...
const int childCount = toBeModifiedItem->childCount();
const int newChildCount = newItem->childCount();
const int newChildCount = newItem.childCount();
// for keeping the CheckState on modifications
// TODO might still fail for duplicate entries (e.g. unnamed Quick Tests)
QHash<QString, Qt::CheckState> originalItems;
// TODO might still fail for duplicate entries
QHash<QString, Qt::CheckState> checkStates;
for (int row = 0; row < childCount; ++row) {
const TestTreeItem *child = toBeModifiedItem->child(row);
originalItems.insert(child->name(), child->checked());
checkStates.insert(child->name(), child->checked());
}
if (childCount <= newChildCount) {
for (int row = 0; row < childCount; ++row) {
QModelIndex child = toBeModifiedIndex.child(row, 0);
TestTreeItem *toBeModifiedChild = toBeModifiedItem->child(row);
TestTreeItem *modifiedChild = newItem->child(row);
if (toBeModifiedChild->modifyContent(modifiedChild)) {
emit dataChanged(child, child, modificationRoles);
}
if (originalItems.contains(toBeModifiedChild->name())) {
Qt::CheckState state = originalItems.value(toBeModifiedChild->name());
if (state != toBeModifiedChild->checked()) {
toBeModifiedChild->setChecked(state);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
} else { // newly added (BAD: happens for renaming as well)
toBeModifiedChild->setChecked(Qt::Checked);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
}
if (childCount < newChildCount) { // add aditional items
for (int row = childCount; row < newChildCount; ++row) {
TestTreeItem *newChild = newItem->child(row);
TestTreeItem *toBeAdded = new TestTreeItem(*newChild);
toBeAdded->setParent(toBeModifiedItem);
beginInsertRows(toBeModifiedIndex, row, row);
toBeModifiedItem->appendChild(toBeAdded);
endInsertRows();
if (originalItems.contains(toBeAdded->name())
&& originalItems.value(toBeAdded->name()) != Qt::Checked)
toBeAdded->setChecked(originalItems.value(toBeAdded->name()));
}
processChildren(toBeModifiedIndex, newItem, childCount, checkStates);
// add additional items
for (int row = childCount; row < newChildCount; ++row) {
TestTreeItem *newChild = newItem.child(row);
TestTreeItem *toBeAdded = new TestTreeItem(*newChild);
toBeAdded->setParent(toBeModifiedItem);
if (checkStates.contains(toBeAdded->name())
&& checkStates.value(toBeAdded->name()) != Qt::Checked)
toBeAdded->setChecked(checkStates.value(toBeAdded->name()));
beginInsertRows(toBeModifiedIndex, row, row);
toBeModifiedItem->appendChild(toBeAdded);
endInsertRows();
}
} else {
for (int row = 0; row < newChildCount; ++row) {
QModelIndex child = toBeModifiedIndex.child(row, 0);
TestTreeItem *toBeModifiedChild = toBeModifiedItem->child(row);
TestTreeItem *modifiedChild = newItem->child(row);
if (toBeModifiedChild->modifyContent(modifiedChild))
emit dataChanged(child, child, modificationRoles);
if (originalItems.contains(toBeModifiedChild->name())) {
Qt::CheckState state = originalItems.value(toBeModifiedChild->name());
if (state != toBeModifiedChild->checked()) {
toBeModifiedChild->setChecked(state);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
} else { // newly added (BAD: happens for renaming as well)
toBeModifiedChild->setChecked(Qt::Checked);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
} // remove rest of the items
processChildren(toBeModifiedIndex, newItem, newChildCount, checkStates);
// remove rest of the items
removeRows(newChildCount, childCount - newChildCount, toBeModifiedIndex);
}
emit testTreeModelChanged();
}
void TestTreeModel::processChildren(QModelIndex &parentIndex, const TestTreeItem &newItem,
const int upperBound,
const QHash<QString, Qt::CheckState> &checkStates)
{
static QVector<int> modificationRoles = QVector<int>() << Qt::DisplayRole
<< Qt::ToolTipRole << LinkRole;
TestTreeItem *toBeModifiedItem = static_cast<TestTreeItem *>(parentIndex.internalPointer());
for (int row = 0; row < upperBound; ++row) {
QModelIndex child = parentIndex.child(row, 0);
TestTreeItem *toBeModifiedChild = toBeModifiedItem->child(row);
TestTreeItem *modifiedChild = newItem.child(row);
if (toBeModifiedChild->modifyContent(modifiedChild))
emit dataChanged(child, child, modificationRoles);
if (checkStates.contains(toBeModifiedChild->name())) {
Qt::CheckState state = checkStates.value(toBeModifiedChild->name());
if (state != toBeModifiedChild->checked()) {
toBeModifiedChild->setChecked(state);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
} else { // newly added (BAD: happens for renaming as well)
toBeModifiedChild->setChecked(Qt::Checked);
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
}
}
}
/***************************** Sort/Filter Model **********************************/
TestTreeSortFilterModel::TestTreeSortFilterModel(TestTreeModel *sourceModel, QObject *parent)
......
......@@ -37,6 +37,7 @@ namespace {
namespace Autotest {
namespace Internal {
struct TestCodeLocationAndType;
class TestCodeParser;
class TestInfo;
class TestTreeItem;
......@@ -45,6 +46,11 @@ class TestTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum Type {
AutoTest,
QuickTest
};
static TestTreeModel* instance();
~TestTreeModel();
......@@ -62,18 +68,13 @@ public:
bool hasTests() const;
QList<TestConfiguration *> getAllTestCases() const;
QList<TestConfiguration *> getSelectedTests() const;
TestTreeItem *unnamedQuickTests() const;
void modifyAutoTestSubtree(int row, TestTreeItem *newItem);
void removeAutoTestSubtreeByFilePath(const QString &file);
void addAutoTest(TestTreeItem *newItem);
void removeAllAutoTests();
QString getMainFileForUnnamedQuickTest(const QString &qmlFile) const;
void qmlFilesAndFunctionNamesForMainFile(const QString &mainFile, QSet<QString> *filePaths,
QList<QString> *functionNames) const;
QList<QString> getUnnamedQuickTestFunctions() const;
QSet<QString> qmlFilesForProFile(const QString &proFile) const;
bool hasUnnamedQuickTests() const;
void modifyQuickTestSubtree(int row, TestTreeItem *newItem);
void removeQuickTestSubtreeByFilePath(const QString &file);
void addQuickTest(TestTreeItem *newItem);
void removeAllQuickTests();
bool removeUnnamedQuickTests(const QString &filePath);
signals:
void testTreeModelChanged();
......@@ -81,8 +82,23 @@ signals:
public slots:
private:
void addTestTreeItem(const TestTreeItem &item, Type type);
void addTestTreeItems(const QList<TestTreeItem> &itemList, Type type);
void updateUnnamedQuickTest(const QString &fileName, const QString &mainFile,
const QMap<QString, TestCodeLocationAndType> &functions);
void modifyTestTreeItem(TestTreeItem item, Type type, const QString &file);
void removeAllTestItems();
void removeTestTreeItems(const QString &filePath, Type type);
void removeUnnamedQuickTests(const QString &filePath);
TestTreeItem *unnamedQuickTests() const;
TestTreeItem *rootItemForType(Type type);
QModelIndex rootIndexForType(Type type);
explicit TestTreeModel(QObject *parent = 0);
void modifyTestSubtree(QModelIndex &toBeModifiedIndex, TestTreeItem *newItem);
void modifyTestSubtree(QModelIndex &toBeModifiedIndex, const TestTreeItem &newItem);
void processChildren(QModelIndex &parentIndex, const TestTreeItem &newItem,
const int upperBound, const QHash<QString, Qt::CheckState> &checkStates);
TestTreeItem *m_rootItem;
TestTreeItem *m_autoTestRootItem;
......
......@@ -35,13 +35,6 @@
namespace Autotest {
namespace Internal {
struct TestCodeLocationAndType {
QString m_fileName;
unsigned m_line;
unsigned m_column;
TestTreeItem::Type m_type;
};
class TestVisitor : public CPlusPlus::SymbolVisitor
{
public:
......