diff --git a/src/plugins/designer/designer.pro b/src/plugins/designer/designer.pro index adf2c06229f..9ca41e110c5 100644 --- a/src/plugins/designer/designer.pro +++ b/src/plugins/designer/designer.pro @@ -62,6 +62,11 @@ SOURCES += formeditorplugin.cpp \ qtdesignerformclasscodegenerator.cpp \ designerxmleditorwidget.cpp +equals(TEST, 1) { + SOURCES += gotoslot_test.cpp + DEFINES += SRCDIR=\\\"$$PWD\\\" +} + RESOURCES += designer.qrc OTHER_FILES += README.txt diff --git a/src/plugins/designer/designer.qbs b/src/plugins/designer/designer.qbs index 9ec83588322..ee07ff32674 100644 --- a/src/plugins/designer/designer.qbs +++ b/src/plugins/designer/designer.qbs @@ -1,6 +1,8 @@ import qbs.base 1.0 +import qbs.FileInfo import "../QtcPlugin.qbs" as QtcPlugin +import "../../../qbs/defaults.js" as Defaults QtcPlugin { name: "Designer" @@ -72,4 +74,13 @@ QtcPlugin { "formclasswizardparameters.cpp", "formclasswizardparameters.h", ] } + + Group { + name: "Tests" + condition: Defaults.testsEnabled(qbs) + files: [ "gotoslot_test.cpp" ] + + cpp.defines: outer.concat(['SRCDIR="' + FileInfo.path(filePath) + '"']) + } + } diff --git a/src/plugins/designer/formeditorplugin.h b/src/plugins/designer/formeditorplugin.h index 36a900104c6..ad77112c96c 100644 --- a/src/plugins/designer/formeditorplugin.h +++ b/src/plugins/designer/formeditorplugin.h @@ -50,6 +50,11 @@ public: private: void initializeTemplates(); + +private slots: +#ifdef WITH_TESTS + void test_gotoslot_withoutProject(); +#endif }; } // namespace Internal diff --git a/src/plugins/designer/gotoslot_test.cpp b/src/plugins/designer/gotoslot_test.cpp new file mode 100644 index 00000000000..3df1b11d3f4 --- /dev/null +++ b/src/plugins/designer/gotoslot_test.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** 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 "formeditorplugin.h" + +#include "formeditorw.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace Core; +using namespace Core::Internal::Tests; +using namespace CppTools; +using namespace CPlusPlus; +using namespace Designer; +using namespace Designer::Internal; + +namespace { + +class MyTestDataDir : public Core::Internal::Tests::TestDataDir { +public: + MyTestDataDir(const QString &dir) + : TestDataDir(QLatin1String(SRCDIR "/../../../tests/designer/") + dir) + {} +}; + +bool containsSymbol(Scope *scope, const QString &functionName) +{ + Overview oo; + for (int i = 0, end = scope->memberCount(); i < end; ++i) { + Symbol *symbol = scope->memberAt(i); + const QString symbolName = oo.prettyName(symbol->name()); + if (symbolName == functionName) + return true; + } + return false; +} + +class GoToSlotTest +{ +public: + GoToSlotTest() : m_modelManager(CppModelManagerInterface::instance()) { cleanup(); } + ~GoToSlotTest() { cleanup(); } + + void run() const + { + MyTestDataDir testData(QLatin1String("gotoslot_withoutProject")); + const QString cppFile = testData.file(QLatin1String("form.cpp")); + const QString hFile = testData.file(QLatin1String("form.h")); + const QString uiFile = testData.file(QLatin1String("form.ui")); + const QStringList files = QStringList() << cppFile << hFile << uiFile; + + const QString functionName = QLatin1String("on_pushButton_clicked"); + const QString qualifiedFunctionName = QLatin1String("Form::") + functionName; + + foreach (const QString &file, files) + QVERIFY(EditorManager::openEditor(file)); + QCOMPARE(EditorManager::documentModel()->openedDocuments().size(), files.size()); + while (!m_modelManager->snapshot().contains(cppFile) + || !m_modelManager->snapshot().contains(hFile)) { + QApplication::processEvents(); + } + + // Checks before + Document::Ptr cppDocumentBefore = m_modelManager->snapshot().document(cppFile); + QCOMPARE(cppDocumentBefore->globalSymbolCount(), 2U); + QVERIFY(!containsSymbol(cppDocumentBefore->globalNamespace(), qualifiedFunctionName)); + + Document::Ptr hDocumentBefore = m_modelManager->snapshot().document(hFile); + QCOMPARE(hDocumentBefore->globalSymbolAt(1)->asScope()->memberCount(), 3U); + QVERIFY(!containsSymbol(hDocumentBefore->globalSymbolAt(1)->asScope(), functionName)); + + // Execute "Go To Slot" + FormEditorW *few = FormEditorW::instance(); + QDesignerIntegrationInterface *integration = few->designerEditor()->integration(); + QVERIFY(integration); + integration->emitNavigateToSlot(QLatin1String("pushButton"), QLatin1String("clicked()"), + QStringList()); + QApplication::processEvents(); + + // Checks after + m_modelManager->updateSourceFiles(QStringList() << cppFile << hFile).waitForFinished(); + + QCOMPARE(EditorManager::currentDocument()->filePath(), cppFile); + QVERIFY(EditorManager::currentDocument()->isModified()); + + Document::Ptr cppDocumentAfter = m_modelManager->snapshot().document(cppFile); + QCOMPARE(cppDocumentAfter->globalSymbolCount(), 3U); + QVERIFY(containsSymbol(cppDocumentAfter->globalNamespace(), qualifiedFunctionName)); + + Document::Ptr hDocumentAfter = m_modelManager->snapshot().document(hFile); + QCOMPARE(hDocumentAfter->globalSymbolAt(1)->asScope()->memberCount(), 4U); + QVERIFY(containsSymbol(hDocumentAfter->globalSymbolAt(1)->asScope(), functionName)); + } + +private: + void cleanup() + { + EditorManager::instance()->closeAllEditors(/*askAboutModifiedEditors =*/ false); + QVERIFY(EditorManager::documentModel()->openedDocuments().isEmpty()); + + m_modelManager->GC(); + QVERIFY(m_modelManager->snapshot().isEmpty()); + } + +private: + CppModelManagerInterface *m_modelManager; +}; + +} // anonymous namespace + +/// Check: Executes "Go To Slot..." on a QPushButton in a *.ui file and checks if the respective +/// header and source files are updated. +void FormEditorPlugin::test_gotoslot_withoutProject() +{ + GoToSlotTest test; + test.run(); +} diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp index 0333adf392f..7e31fcd2b45 100644 --- a/src/plugins/designer/qtcreatorintegration.cpp +++ b/src/plugins/designer/qtcreatorintegration.cpp @@ -34,6 +34,7 @@ #include "editordata.h" #include +#include #include #include #include @@ -131,15 +132,16 @@ static QList findDocumentsIncluding(const Snapshot &docTable, { QList docList; foreach (const Document::Ptr &doc, docTable) { // we go through all documents - const QStringList includes = doc->includedFiles(); - foreach (const QString &include, includes) { + const QList includes = doc->resolvedIncludes() + + doc->unresolvedIncludes(); + foreach (const Document::Include &include, includes) { if (checkFileNameOnly) { - const QFileInfo fi(include); + const QFileInfo fi(include.unresolvedFileName()); if (fi.fileName() == fileName) { // we are only interested in docs which includes fileName only docList.append(doc); } } else { - if (include == fileName) + if (include.resolvedFileName() == fileName) docList.append(doc); } } @@ -530,21 +532,29 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName, const QString uiFolder = fi.absolutePath(); const QString uicedName = QLatin1String("ui_") + fi.completeBaseName() + QLatin1String(".h"); - // Retrieve code model snapshot restricted to project of ui file. - const ProjectExplorer::Project *uiProject = ProjectExplorer::ProjectExplorerPlugin::instance()->session()->projectForFile(currentUiFile); - if (!uiProject) { - *errorMessage = tr("Internal error: No project could be found for %1.").arg(currentUiFile); - return false; - } + // Retrieve code model snapshot restricted to project of ui file or the working copy. Snapshot docTable = CppTools::CppModelManagerInterface::instance()->snapshot(); Snapshot newDocTable; - - for (Snapshot::iterator it = docTable.begin(); it != docTable.end(); ++it) { - const ProjectExplorer::Project *project = ProjectExplorer::ProjectExplorerPlugin::instance()->session()->projectForFile(it.key()); - if (project == uiProject) - newDocTable.insert(it.value()); + ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance(); + const ProjectExplorer::Project *uiProject = pe->session()->projectForFile(currentUiFile); + if (uiProject) { + Snapshot::const_iterator end = docTable.end(); + for (Snapshot::iterator it = docTable.begin(); it != end; ++it) { + const ProjectExplorer::Project *project = pe->session()->projectForFile(it.key()); + if (project == uiProject) + newDocTable.insert(it.value()); + } + } else { + const CppTools::CppModelManagerInterface::WorkingCopy workingCopy = + CppTools::CppModelManagerInterface::instance()->workingCopy(); + QHashIterator > it = workingCopy.iterator(); + while (it.hasNext()) { + it.next(); + const QString fileName = it.key(); + if (fileName != CppTools::CppModelManagerInterface::configurationFileName()) + newDocTable.insert(docTable.document(fileName)); + } } - docTable = newDocTable; // take all docs, find the ones that include the ui_xx.h. diff --git a/tests/designer/gotoslot_withoutProject/form.cpp b/tests/designer/gotoslot_withoutProject/form.cpp new file mode 100644 index 00000000000..bb22f9bce1c --- /dev/null +++ b/tests/designer/gotoslot_withoutProject/form.cpp @@ -0,0 +1,17 @@ +// Copyright header + +#include "form.h" +#include "ui_form.h" + +Form::Form(QWidget *parent) : + QWidget(parent), + ui(new Ui::Form) +{ + ui->setupUi(this); +} + +Form::~Form() +{ + delete ui; +} + diff --git a/tests/designer/gotoslot_withoutProject/form.h b/tests/designer/gotoslot_withoutProject/form.h new file mode 100644 index 00000000000..b027afa78d3 --- /dev/null +++ b/tests/designer/gotoslot_withoutProject/form.h @@ -0,0 +1,24 @@ +// Copyright header + +#ifndef FORM_H +#define FORM_H + +#include + +namespace Ui { +class Form; +} + +class Form : public QWidget +{ + Q_OBJECT + +public: + explicit Form(QWidget *parent = 0); + ~Form(); + +private: + Ui::Form *ui; +}; + +#endif // FORM_H diff --git a/tests/designer/gotoslot_withoutProject/form.ui b/tests/designer/gotoslot_withoutProject/form.ui new file mode 100644 index 00000000000..7bace8bf60d --- /dev/null +++ b/tests/designer/gotoslot_withoutProject/form.ui @@ -0,0 +1,32 @@ + + + Form + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + 50 + 60 + 87 + 27 + + + + PushButton + + + + + +