Commit 992f9ed2 authored by Christian Stenger's avatar Christian Stenger
Browse files

AutoTest: Use mark and sweep for parsing



This also removes caching from parser and the respective test info
files are gone now as well.

Change-Id: Ibcea68e446dea532d6addd2f16863738e497bca4
Reviewed-by: default avatarDavid Schulz <david.schulz@theqtcompany.com>
parent fd4d1c80
......@@ -11,7 +11,6 @@ SOURCES += \
testtreemodel.cpp \
testtreeitem.cpp \
testvisitor.cpp \
testinfo.cpp \
testcodeparser.cpp \
autotestplugin.cpp \
testrunner.cpp \
......@@ -31,7 +30,6 @@ HEADERS += \
testtreemodel.h \
testtreeitem.h \
testvisitor.h \
testinfo.h \
testcodeparser.h \
autotestplugin.h \
autotest_global.h \
......
......@@ -40,8 +40,6 @@ QtcPlugin {
"testcodeparser.h",
"testconfiguration.cpp",
"testconfiguration.h",
"testinfo.cpp",
"testinfo.h",
"testnavigationwidget.cpp",
"testnavigationwidget.h",
"testresult.cpp",
......
......@@ -105,11 +105,6 @@ void AutoTestUnitTests::testCodeParser()
QCOMPARE(m_model->namedQuickTestsCount(), expectedNamedQuickTestsCount);
QCOMPARE(m_model->unnamedQuickTestsCount(), expectedUnnamedQuickTestsCount);
QCOMPARE(m_model->dataTagsCount(), expectedDataTagsCount);
QCOMPARE(m_model->parser()->autoTestsCount(), expectedAutoTestsCount);
QCOMPARE(m_model->parser()->namedQuickTestsCount(), expectedNamedQuickTestsCount);
QCOMPARE(m_model->parser()->unnamedQuickTestsCount(), expectedUnnamedQuickTestsCount);
}
void AutoTestUnitTests::testCodeParser_data()
......@@ -162,12 +157,6 @@ void AutoTestUnitTests::testCodeParserSwitchStartup()
m_isQt4 ? 0 : expectedUnnamedQuickTestsCount.at(i));
QCOMPARE(m_model->dataTagsCount(),
expectedDataTagsCount.at(i));
QCOMPARE(m_model->parser()->autoTestsCount(), expectedAutoTestsCount.at(i));
QCOMPARE(m_model->parser()->namedQuickTestsCount(),
m_isQt4 ? 0 : expectedNamedQuickTestsCount.at(i));
QCOMPARE(m_model->parser()->unnamedQuickTestsCount(),
m_isQt4 ? 0 : expectedUnnamedQuickTestsCount.at(i));
}
}
......@@ -212,8 +201,6 @@ void AutoTestUnitTests::testCodeParserGTest()
QVERIFY(parserSpy.wait(20000));
QCOMPARE(m_model->gtestNamesCount(), 6);
// 11 == 3 + 2 + 2 + 2 + 1 + 1, see below
QCOMPARE(m_model->parser()->gtestNamesAndSetsCount(), 11);
QMultiMap<QString, int> expectedNamesAndSets;
expectedNamesAndSets.insert(QStringLiteral("FactorialTest"), 3);
......@@ -233,10 +220,6 @@ void AutoTestUnitTests::testCodeParserGTest()
QCOMPARE(m_model->namedQuickTestsCount(), 0);
QCOMPARE(m_model->unnamedQuickTestsCount(), 0);
QCOMPARE(m_model->dataTagsCount(), 0);
QCOMPARE(m_model->parser()->autoTestsCount(), 0);
QCOMPARE(m_model->parser()->namedQuickTestsCount(), 0);
QCOMPARE(m_model->parser()->unnamedQuickTestsCount(), 0);
}
} // namespace Internal
......
This diff is collapsed.
......@@ -44,8 +44,6 @@ namespace Autotest {
namespace Internal {
struct TestCodeLocationAndType;
class UnnamedQuickTestInfo;
class GTestInfo;
struct GTestCaseSpec;
class TestCodeParser : public QObject
......@@ -65,29 +63,9 @@ public:
State state() const { return m_parserState; }
void setDirty() { m_dirty = true; }
#ifdef WITH_TESTS
int autoTestsCount() const;
int namedQuickTestsCount() const;
int unnamedQuickTestsCount() const;
int gtestNamesAndSetsCount() const;
#endif
// FIXME remove me again
struct ReferencingInfo
{
QString referencingFile;
TestTreeModel::Type type;
};
signals:
void cacheCleared();
void aboutToPerformFullParse();
void testItemCreated(TestTreeItem *item, TestTreeModel::Type type);
void testItemModified(TestTreeItem *tItem, TestTreeModel::Type type, const QStringList &file);
void testItemsRemoved(const QString &filePath, TestTreeModel::Type type);
void unnamedQuickTestsUpdated(const QString &mainFile,
const QMap<QString, TestCodeLocationAndType> &functions);
void unnamedQuickTestsRemoved(const QString &filePath);
void gTestsRemoved(const QString &filePath);
void parsingStarted();
void parsingFinished();
void parsingFailed();
......@@ -105,34 +83,18 @@ public slots:
void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document);
void onStartupProjectChanged(ProjectExplorer::Project *);
void onProjectPartsUpdated(ProjectExplorer::Project *project);
void removeFiles(const QStringList &files);
private:
bool postponed(const QStringList &fileList);
void scanForTests(const QStringList &fileList = QStringList());
void clearCache();
void removeTestsIfNecessary(const QString &fileName);
void onTaskStarted(Core::Id type);
void onAllTasksFinished(Core::Id type);
void onFinished();
void onPartialParsingFinished();
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);
void updateGTests(const CPlusPlus::Document::Ptr &doc,
const QMap<GTestCaseSpec, TestCodeLocationList> &tests);
void removeUnnamedQuickTestsByName(const QString &fileName);
void removeGTestsByName(const QString &fileName);
TestTreeModel *m_model;
QMap<QString, ReferencingInfo> m_referencingFiles;
QList<UnnamedQuickTestInfo> m_unnamedQuickDocList;
QList<GTestInfo> m_gtestDocList;
bool m_codeModelParsing;
bool m_fullUpdatePostponed;
bool m_partialUpdatePostponed;
......
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "testinfo.h"
namespace Autotest {
namespace Internal {
UnnamedQuickTestInfo::UnnamedQuickTestInfo(const QString &function, const QString &fileName)
: m_function(function),
m_fileName(fileName)
{
}
GTestInfo::GTestInfo(const QString &caseName, const QString &setName, const QString &file)
: m_caseName(caseName),
m_setName(setName),
m_fileName(file),
m_enabled(true)
{
}
} // namespace Internal
} // namespace Autotest
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#ifndef TESTINFO_H
#define TESTINFO_H
#include <QStringList>
namespace Autotest {
namespace Internal {
class UnnamedQuickTestInfo {
public:
explicit UnnamedQuickTestInfo(const QString &function = QString(),
const QString& fileName = QString());
~UnnamedQuickTestInfo() {}
const QString function() const { return m_function; }
void setFunction(const QString &function) { m_function = function; }
const QString fileName() const { return m_fileName; }
void setFileName(const QString &fileName) { m_fileName = fileName; }
private:
QString m_function;
QString m_fileName;
};
class GTestInfo {
public:
explicit GTestInfo(const QString &caseName, const QString &setName, const QString &file);
const QString caseName() const { return m_caseName; }
void setCaseName(const QString &caseName) { m_caseName = caseName; }
const QString setName() const { return m_setName; }
void setSetName(const QString &setName) { m_setName = setName; }
const QString fileName() const { return m_fileName; }
void setFileName(const QString &fileName) { m_fileName = fileName; }
bool isEnabled() const { return m_enabled; }
void setEnabled(bool enabled) { m_enabled = enabled; }
private:
QString m_caseName;
QString m_setName;
QString m_fileName;
bool m_enabled;
};
} // namespace Internal
} // namespace Autotest
#endif // TESTINFO_H
......@@ -86,33 +86,14 @@ TestTreeModel::TestTreeModel(QObject *parent) :
rootItem()->appendChild(m_quickTestRootItem);
rootItem()->appendChild(m_googleTestRootItem);
connect(m_parser, &TestCodeParser::cacheCleared, this,
connect(m_parser, &TestCodeParser::aboutToPerformFullParse, this,
&TestTreeModel::removeAllTestItems, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::testItemCreated,
this, &TestTreeModel::addTestTreeItem, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::testItemModified,
this, &TestTreeModel::modifyTestTreeItem, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::testItemsRemoved,
this, &TestTreeModel::removeTestTreeItems, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::unnamedQuickTestsUpdated,
this, &TestTreeModel::updateUnnamedQuickTest, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::unnamedQuickTestsRemoved,
this, &TestTreeModel::removeUnnamedQuickTests, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::gTestsRemoved,
this, &TestTreeModel::removeGTests, Qt::QueuedConnection);
// CppTools::CppModelManagerInterface *cppMM = CppTools::CppModelManagerInterface::instance();
// if (cppMM) {
// // replace later on by
// // cppMM->registerAstProcessor([this](const CplusPlus::Document::Ptr &doc,
// // const CPlusPlus::Snapshot &snapshot) {
// // checkForQtTestStuff(doc, snapshot);
// // });
// connect(cppMM, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
// this, SLOT(checkForQtTestStuff(CPlusPlus::Document::Ptr)),
// Qt::DirectConnection);
// }
connect(m_parser, &TestCodeParser::parsingFinished,
this, &TestTreeModel::sweep, Qt::QueuedConnection);
connect(m_parser, &TestCodeParser::parsingFailed,
this, &TestTreeModel::sweep, Qt::QueuedConnection);
}
static TestTreeModel *m_instance = 0;
......@@ -148,7 +129,7 @@ void TestTreeModel::enableParsing()
connect(cppMM, &CppTools::CppModelManager::documentUpdated,
m_parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection);
connect(cppMM, &CppTools::CppModelManager::aboutToRemoveFiles,
m_parser, &TestCodeParser::removeFiles, Qt::QueuedConnection);
this, &TestTreeModel::removeFiles, Qt::QueuedConnection);
connect(cppMM, &CppTools::CppModelManager::projectPartsUpdated,
m_parser, &TestCodeParser::onProjectPartsUpdated);
......@@ -156,7 +137,7 @@ void TestTreeModel::enableParsing()
connect(qmlJsMM, &QmlJS::ModelManagerInterface::documentUpdated,
m_parser, &TestCodeParser::onQmlDocumentUpdated, Qt::QueuedConnection);
connect(qmlJsMM, &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
m_parser, &TestCodeParser::removeFiles, Qt::QueuedConnection);
this, &TestTreeModel::removeFiles, Qt::QueuedConnection);
m_connectionsInitialized = true;
}
......@@ -541,38 +522,6 @@ TestConfiguration *TestTreeModel::getTestConfiguration(const TestTreeItem *item)
return config;
}
QString TestTreeModel::getMainFileForUnnamedQuickTest(const QString &qmlFile) const
{
const TestTreeItem *unnamed = unnamedQuickTests();
const int count = unnamed ? unnamed->childCount() : 0;
for (int row = 0; row < count; ++row) {
const TestTreeItem *child = unnamed->childItem(row);
if (qmlFile == child->filePath())
return child->mainFile();
}
return QString();
}
void TestTreeModel::qmlFilesForMainFile(const QString &mainFile, QSet<QString> *filePaths) const
{
const TestTreeItem *unnamed = unnamedQuickTests();
if (!unnamed)
return;
for (int row = 0, count = unnamed->childCount(); row < count; ++row) {
const TestTreeItem *child = unnamed->childItem(row);
if (child->mainFile() == mainFile)
filePaths->insert(child->filePath());
}
}
QList<QString> TestTreeModel::getUnnamedQuickTestFunctions() const
{
const TestTreeItem *unnamed = unnamedQuickTests();
if (unnamed)
return unnamed->getChildNames();
return QList<QString>();
}
bool TestTreeModel::hasUnnamedQuickTests() const
{
for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row)
......@@ -591,36 +540,11 @@ TestTreeItem *TestTreeModel::unnamedQuickTests() const
return 0;
}
void TestTreeModel::removeUnnamedQuickTests(const QString &filePath)
{
TestTreeItem *unnamedQT = unnamedQuickTests();
if (!unnamedQT)
return;
for (int childRow = unnamedQT->childCount() - 1; childRow >= 0; --childRow) {
TestTreeItem *child = unnamedQT->childItem(childRow);
if (filePath == child->filePath())
delete takeItem(child);
}
if (unnamedQT->childCount() == 0)
delete takeItem(unnamedQT);
emit testTreeModelChanged();
}
void TestTreeModel::removeGTests(const QString &filePath)
void TestTreeModel::removeFiles(const QStringList &files)
{
for (int childRow = m_googleTestRootItem->childCount() - 1; childRow >= 0; --childRow) {
TestTreeItem *child = m_googleTestRootItem->childItem(childRow);
for (int grandChildRow = child->childCount() - 1; grandChildRow >= 0; --grandChildRow) {
TestTreeItem *grandChild = child->childItem(grandChildRow);
if (filePath == grandChild->filePath())
delete takeItem(grandChild);
}
if (child->childCount() == 0)
delete takeItem(child);
}
emit testTreeModelChanged();
foreach (const QString &file, files)
markForRemoval(file);
sweep();
}
void TestTreeModel::markAllForRemoval()
......@@ -711,78 +635,59 @@ QMap<QString, QString> TestTreeModel::referencingFiles() const
return finder.referencingFiles();
}
void TestTreeModel::addTestTreeItem(TestTreeItem *item, TestTreeModel::Type type)
TestTreeItem *TestTreeModel::findTestTreeItemByContent(TestTreeItem *item, TestTreeItem *parent,
Type type)
{
TestTreeItem *parent = rootItemForType(type);
if (type == TestTreeModel::GoogleTest) {
// check if there's already an item with the same test name...
TestTreeItem *toBeUpdated = 0;
for (int row = 0, count = parent->childCount(); row < count; ++row) {
TestTreeItem *current = parent->childItem(row);
if (current->name() == item->name() && current->type() == item->type()) {
toBeUpdated = current;
if (current->name() != item->name())
continue;
switch (type) {
case AutoTest:
if (current->filePath() == item->filePath())
return current;
break;
case QuickTest:
if (current->filePath() == item->filePath() && current->mainFile() == item->mainFile())
return current;
break;
case GoogleTest:
if (current->type() == item->type())
return current;
break;
}
}
// ...if so we have, to update this one instead of adding a new item
if (toBeUpdated) {
for (int row = 0, count = item->childCount(); row < count; ++row)
toBeUpdated->appendChild(new TestTreeItem(*item->childItem(row)));
delete item;
} else {
parent->appendChild(item);
}
} else {
parent->appendChild(item);
}
emit testTreeModelChanged();
}
void TestTreeModel::updateUnnamedQuickTest(const QString &mainFile,
const QMap<QString, TestCodeLocationAndType> &functions)
{
if (functions.isEmpty())
return;
if (!hasUnnamedQuickTests())
addTestTreeItem(new TestTreeItem(QString(), QString(), TestTreeItem::TestClass), QuickTest);
TestTreeItem *unnamed = unnamedQuickTests();
foreach (const QString &functionName, functions.keys()) {
const TestCodeLocationAndType locationAndType = functions.value(functionName);
TestTreeItem *testFunction = new TestTreeItem(functionName, locationAndType.m_name,
locationAndType.m_type);
testFunction->setLine(locationAndType.m_line);
testFunction->setColumn(locationAndType.m_column);
testFunction->setMainFile(mainFile);
unnamed->appendChild(testFunction);
}
return 0;
}
void TestTreeModel::modifyTestTreeItem(TestTreeItem *item, TestTreeModel::Type type, const QStringList &files)
void TestTreeModel::addTestTreeItem(TestTreeItem *item, Type type)
{
QModelIndex index = rootIndexForType(type);
TestTreeItem *parent = rootItemForType(type);
if (files.isEmpty()) {
if (TestTreeItem *unnamed = unnamedQuickTests()) {
if (unnamed == item) // no need to update or delete
return;
index = indexForItem(unnamed);
modifyTestSubtree(index, item);
}
TestTreeItem *toBeUpdated = findTestTreeItemByContent(item, parent, type);
const int count = item->childCount();
if (toBeUpdated) {
if (!toBeUpdated->markedForRemoval()) {
for (int row = 0; row < count; ++row)
toBeUpdated->appendChild(new TestTreeItem(*item->childItem(row)));
} else {
for (int row = 0; row < parent->childCount(); ++row) {
if (files.contains(parent->childItem(row)->filePath())) {
index = index.child(row, 0);
modifyTestSubtree(index, item);
break;
for (int childRow = count - 1; childRow >= 0; --childRow) {
TestTreeItem *childItem = item->childItem(childRow);
TestTreeItem *origChild = findTestTreeItemByContent(childItem, toBeUpdated, type);
if (origChild) {
QModelIndex toBeModifiedIndex = indexForItem(origChild);
modifyTestSubtree(toBeModifiedIndex, childItem);
} else {
toBeUpdated->insertChild(qMin(count, toBeUpdated->childCount()),
new TestTreeItem(*childItem));
}
}
}
// item was created as temporary, destroy it if it won't get destroyed by its parent
if (!item->parent())
delete item;
} else {
parent->appendChild(item);
}
emit testTreeModelChanged();
}
void TestTreeModel::removeAllTestItems()
......@@ -793,21 +698,6 @@ void TestTreeModel::removeAllTestItems()
emit testTreeModelChanged();
}
void TestTreeModel::removeTestTreeItems(const QString &filePath, Type type)
{
bool removed = false;
const TestTreeItem *rootItem = rootItemForType(type);
for (int row = rootItem->childCount() - 1; row >= 0; --row) {
TestTreeItem *childItem = rootItem->childItem(row);
if (filePath == childItem->filePath()) {
delete takeItem(childItem);
removed = true;
}
}
if (removed)
emit testTreeModelChanged();
}
TestTreeItem *TestTreeModel::rootItemForType(TestTreeModel::Type type)
{
switch (type) {
......@@ -821,19 +711,6 @@ TestTreeItem *TestTreeModel::rootItemForType(TestTreeModel::Type type)
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);
case GoogleTest:
return index(2, 0);
}
QTC_ASSERT(false, return QModelIndex());
}
void TestTreeModel::modifyTestSubtree(QModelIndex &toBeModifiedIndex, const TestTreeItem *newItem)
{
if (!toBeModifiedIndex.isValid())
......
......@@ -64,9 +64,6 @@ public:
QList<TestConfiguration *> getAllTestCases() const;
QList<TestConfiguration *> getSelectedTests() const;
TestConfiguration *getTestConfiguration(const TestTreeItem *item) const;
QString getMainFileForUnnamedQuickTest(const QString &qmlFile) const;
void qmlFilesForMainFile(const QString &mainFile, QSet<QString> *filePaths) const;
QList<QString> getUnnamedQuickTestFunctions() const;
bool hasUnnamedQuickTests() const;
#ifdef WITH_TESTS
......@@ -90,18 +87,14 @@ public slots:
private:
void addTestTreeItem(TestTreeItem *item, Type type);
void updateUnnamedQuickTest(const QString &mainFile,
const QMap<QString, TestCodeLocationAndType> &functions);
void modifyTestTreeItem(TestTreeItem *item, Type type, const QStringList &file);
void removeAllTestItems();
void removeTestTreeItems(const QString &filePath, Type type);
void removeUnnamedQuickTests(const QString &filePath);
void removeGTests(const QString &filePath);
bool sweepChildren(TestTreeItem *item);
void removeFiles(const QStringList &files);
void markForRemoval(const QString &filePath, Type type);
void sweepChildren(TestTreeItem *item);
TestTreeItem *findTestTreeItemByContent(TestTreeItem *item, TestTreeItem *parent, Type type);
TestTreeItem *unnamedQuickTests() const;
TestTreeItem *rootItemForType(Type type);
QModelIndex rootIndexForType(Type type);
explicit TestTreeModel(QObject *parent = 0);
void modifyTestSubtree(QModelIndex &toBeModifiedIndex, const TestTreeItem *newItem);
......