diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 7c3d94fc910..9700ea853a0 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -118,7 +118,8 @@ equals(TEST, 1) { symbolsearcher_test.cpp \ cpppreprocessor_test.cpp \ cpppreprocessertesthelper.cpp \ - cppheadersource_test.cpp + cppheadersource_test.cpp \ + typehierarchybuilder_test.cpp HEADERS += \ cpppreprocessertesthelper.h \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index cd8e6f228ae..e955c80e3ad 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -139,7 +139,8 @@ QtcPlugin { "cpplocatorfilter_test.cpp", "symbolsearcher_test.cpp", "cpppreprocessor_test.cpp", - "cpppreprocessertesthelper.cpp", "cpppreprocessertesthelper.h" + "cpppreprocessertesthelper.cpp", "cpppreprocessertesthelper.h", + "typehierarchybuilder_test.cpp" ] cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"']) diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index d9633708dc9..61486a96538 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -227,6 +227,9 @@ private slots: void test_headersource_data(); void test_headersource(); + void test_typehierarchy_data(); + void test_typehierarchy(); + private: void test_completion(); #endif diff --git a/src/plugins/cpptools/typehierarchybuilder_test.cpp b/src/plugins/cpptools/typehierarchybuilder_test.cpp new file mode 100644 index 00000000000..3bcfa054887 --- /dev/null +++ b/src/plugins/cpptools/typehierarchybuilder_test.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "cpptoolsplugin.h" + +#include "cppmodelmanagerinterface.h" +#include "typehierarchybuilder.h" + +#include +#include +#include + +#include +#include + +using namespace CPlusPlus; +using namespace CppTools; +using namespace CppTools::Internal; + +namespace { + +bool hierarchySorter(const TypeHierarchy &h1, const TypeHierarchy &h2) +{ + Overview oo; + return oo.prettyName(h1.symbol()->name()) < oo.prettyName(h2.symbol()->name()); +} + +QString toString(const TypeHierarchy &hierarchy, int indent = 0) +{ + Symbol *symbol = hierarchy.symbol(); + QString result = QString(indent, QLatin1Char(' ')) + + Overview().prettyName(symbol->name()) + QLatin1Char('\n'); + + QList sortedHierarchy = hierarchy.hierarchy(); + qSort(sortedHierarchy.begin(), sortedHierarchy.end(), hierarchySorter); + foreach (TypeHierarchy childHierarchy, sortedHierarchy) + result += toString(childHierarchy, indent + 2); + return result; +} + +class FindFirstClassInDocument: private SymbolVisitor +{ +public: + FindFirstClassInDocument() : m_clazz(0) {} + + Class *operator()(const Document::Ptr &document) + { + accept(document->globalNamespace()); + return m_clazz; + } + +private: + bool preVisit(Symbol *symbol) + { + if (m_clazz) + return false; + + if (Class *c = symbol->asClass()) { + m_clazz = c; + return false; + } + + return true; + } + + Class *m_clazz; +}; + +struct TestDocument +{ +public: + TestDocument(const QString &fileName, const QString &contents) + : fileName(fileName), contents(contents) {} + + QString fileName; + QString contents; +}; + +class TestCase +{ +public: + TestCase(const QList &documents, const QString &expectedHierarchy) + : m_modelManager(CppModelManagerInterface::instance()) + , m_documents(documents) + , m_expectedHierarchy(expectedHierarchy) + { + QVERIFY(m_modelManager->snapshot().isEmpty()); + } + + void run() + { + // Write files + QStringList filePaths; + foreach (const TestDocument &document, m_documents) { + const QString filePath = QDir::tempPath() + QLatin1Char('/') + document.fileName; + Utils::FileSaver documentSaver(filePath); + documentSaver.write(document.contents.toUtf8()); + documentSaver.finalize(); + filePaths << filePath; + } + + // Parse files + m_modelManager->updateSourceFiles(filePaths).waitForFinished(); + + // Get class for which to generate the hierarchy + const Snapshot snapshot = m_modelManager->snapshot(); + QVERIFY(!snapshot.isEmpty()); + const Document::Ptr firstDocument = snapshot.document(filePaths.first()); + Class *clazz = FindFirstClassInDocument()(firstDocument); + QVERIFY(clazz); + + // Generate and compare hierarchies + TypeHierarchyBuilder typeHierarchyBuilder(clazz, snapshot); + const TypeHierarchy hierarchy = typeHierarchyBuilder.buildDerivedTypeHierarchy(); + + const QString actualHierarchy = toString(hierarchy); +// Uncomment for updating/generating reference data: +// qDebug() << actualHierarchy; + QCOMPARE(actualHierarchy, m_expectedHierarchy); + } + + ~TestCase() + { + m_modelManager->GC(); + QVERIFY(m_modelManager->snapshot().isEmpty()); + } + +private: + CppModelManagerInterface *m_modelManager; + QList m_documents; + QString m_expectedHierarchy; +}; + +} // anonymous namespace + +Q_DECLARE_METATYPE(QList) + +void CppToolsPlugin::test_typehierarchy_data() +{ + QTest::addColumn >("documents"); + QTest::addColumn("expectedHierarchy"); + + typedef QLatin1String _; + + QTest::newRow("basic-single-document") + << (QList() + << TestDocument(_("a.h"), + _("class A {};\n" + "class B : public A {};\n" + "class C1 : public B {};\n" + "class C2 : public B {};\n" + "class D : public C1 {};\n"))) + << QString::fromLatin1( + "A\n" + " B\n" + " C1\n" + " D\n" + " C2\n" ); + + QTest::newRow("basic-multiple-documents") + << (QList() + << TestDocument(_("a.h"), + _("class A {};")) + << TestDocument(_("b.h"), + _("#include \"a.h\"\n" + "class B : public A {};")) + << TestDocument(_("c1.h"), + _("#include \"b.h\"\n" + "class C1 : public B {};")) + << TestDocument(_("c2.h"), + _("#include \"b.h\"\n" + "class C2 : public B {};")) + << TestDocument(_("d.h"), + _("#include \"c1.h\"\n" + "class D : public C1 {};"))) + << QString::fromLatin1( + "A\n" + " B\n" + " C1\n" + " D\n" + " C2\n" + ); +} + +void CppToolsPlugin::test_typehierarchy() +{ + QFETCH(QList, documents); + QFETCH(QString, expectedHierarchy); + + TestCase testCase(documents, expectedHierarchy); + testCase.run(); +}