From ce1c6b2258aead2e50d15aa5f72148023b6220a7 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Fri, 16 Dec 2016 13:10:43 +0100 Subject: [PATCH] CppTools: Extract ProjectPartChooser ...and put it under tests. Change-Id: Id4bd2391abd1dfdc23640e871453558566cb0693 Reviewed-by: David Schulz --- .../cpptools/baseeditordocumentparser.cpp | 42 ++--- .../cpptools/cppprojectpartchooser.cpp | 87 +++++++++++ src/plugins/cpptools/cppprojectpartchooser.h | 60 ++++++++ src/plugins/cpptools/cpptools.pro | 4 +- src/plugins/cpptools/cpptools.qbs | 1 + .../cpptools/cpptoolsunittestfiles.pri | 3 +- .../unittest/cppprojectpartchooser-test.cpp | 145 ++++++++++++++++++ tests/unit/unittest/unittest.pro | 1 + 8 files changed, 315 insertions(+), 28 deletions(-) create mode 100644 src/plugins/cpptools/cppprojectpartchooser.cpp create mode 100644 src/plugins/cpptools/cppprojectpartchooser.h create mode 100644 tests/unit/unittest/cppprojectpartchooser-test.cpp diff --git a/src/plugins/cpptools/baseeditordocumentparser.cpp b/src/plugins/cpptools/baseeditordocumentparser.cpp index ee3b8bec5c4..ce44c312ff0 100644 --- a/src/plugins/cpptools/baseeditordocumentparser.cpp +++ b/src/plugins/cpptools/baseeditordocumentparser.cpp @@ -27,6 +27,7 @@ #include "baseeditordocumentprocessor.h" #include "cppmodelmanager.h" +#include "cppprojectpartchooser.h" #include "editordocumenthandle.h" namespace CppTools { @@ -120,33 +121,22 @@ ProjectPart::Ptr BaseEditorDocumentParser::determineProjectPart(const QString &f const Configuration &config, const State &state) { - if (config.manuallySetProjectPart) - return config.manuallySetProjectPart; + Internal::ProjectPartChooser chooser; + chooser.setFallbackProjectPart([](){ + return CppModelManager::instance()->fallbackProjectPart(); + }); + chooser.setProjectPartsForFile([](const QString &filePath) { + return CppModelManager::instance()->projectPart(filePath); + }); + chooser.setProjectPartsFromDependenciesForFile([&](const QString &filePath) { + const auto fileName = Utils::FileName::fromString(filePath); + return CppModelManager::instance()->projectPartFromDependencies(fileName); + }); - ProjectPart::Ptr projectPart = state.projectPart; - - CppModelManager *cmm = CppModelManager::instance(); - QList projectParts = cmm->projectPart(filePath); - if (projectParts.isEmpty()) { - if (projectPart && config.stickToPreviousProjectPart) - // File is not directly part of any project, but we got one before. We will re-use it, - // because re-calculating this can be expensive when the dependency table is big. - return projectPart; - - // Fall-back step 1: Get some parts through the dependency table: - projectParts = cmm->projectPartFromDependencies(Utils::FileName::fromString(filePath)); - if (projectParts.isEmpty()) - // Fall-back step 2: Use fall-back part from the model manager: - projectPart = cmm->fallbackProjectPart(); - else - projectPart = projectParts.first(); - } else { - if (!projectParts.contains(projectPart)) - // Apparently the project file changed, so update our project part. - projectPart = projectParts.first(); - } - - return projectPart; + return chooser.choose(filePath, + state.projectPart, + config.manuallySetProjectPart, + config.stickToPreviousProjectPart); } } // namespace CppTools diff --git a/src/plugins/cpptools/cppprojectpartchooser.cpp b/src/plugins/cpptools/cppprojectpartchooser.cpp new file mode 100644 index 00000000000..19074ff1845 --- /dev/null +++ b/src/plugins/cpptools/cppprojectpartchooser.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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 "cppprojectpartchooser.h" + +#include + +namespace CppTools { +namespace Internal { + +ProjectPart::Ptr ProjectPartChooser::choose(const QString &filePath, + const ProjectPart::Ptr ¤tProjectPart, + const ProjectPart::Ptr &manuallySetProjectPart, + bool stickToPreviousProjectPart) const +{ + QTC_CHECK(m_projectPartsForFile); + QTC_CHECK(m_projectPartsFromDependenciesForFile); + QTC_CHECK(m_fallbackProjectPart); + + if (manuallySetProjectPart) + return manuallySetProjectPart; + + ProjectPart::Ptr projectPart = currentProjectPart; + + QList projectParts = m_projectPartsForFile(filePath); + if (projectParts.isEmpty()) { + if (projectPart && stickToPreviousProjectPart) + // File is not directly part of any project, but we got one before. We will re-use it, + // because re-calculating this can be expensive when the dependency table is big. + return projectPart; + + // Fall-back step 1: Get some parts through the dependency table: + projectParts = m_projectPartsFromDependenciesForFile(filePath); + if (projectParts.isEmpty()) + // Fall-back step 2: Use fall-back part from the model manager: + projectPart = m_fallbackProjectPart(); + else + projectPart = projectParts.first(); + } else { + if (!projectParts.contains(projectPart)) + // Apparently the project file changed, so update our project part. + projectPart = projectParts.first(); + } + + return projectPart; +} + +void ProjectPartChooser::setFallbackProjectPart(const FallBackProjectPart &getter) +{ + m_fallbackProjectPart = getter; +} + +void ProjectPartChooser::setProjectPartsForFile(const ProjectPartsForFile &getter) +{ + m_projectPartsForFile = getter; +} + +void ProjectPartChooser::setProjectPartsFromDependenciesForFile( + const ProjectPartsFromDependenciesForFile &getter) +{ + m_projectPartsFromDependenciesForFile = getter; +} + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/cppprojectpartchooser.h b/src/plugins/cpptools/cppprojectpartchooser.h new file mode 100644 index 00000000000..0b9095bfdaa --- /dev/null +++ b/src/plugins/cpptools/cppprojectpartchooser.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include "projectpart.h" + +#include + +namespace CppTools { +namespace Internal { + +class ProjectPartChooser +{ +public: + using FallBackProjectPart = std::function; + using ProjectPartsForFile = std::function(const QString &filePath)>; + using ProjectPartsFromDependenciesForFile + = std::function(const QString &filePath)>; + +public: + void setFallbackProjectPart(const FallBackProjectPart &getter); + void setProjectPartsForFile(const ProjectPartsForFile &getter); + void setProjectPartsFromDependenciesForFile(const ProjectPartsFromDependenciesForFile &getter); + + ProjectPart::Ptr choose(const QString &filePath, + const ProjectPart::Ptr ¤tProjectPart, + const ProjectPart::Ptr &manuallySetProjectPart, + bool stickToPreviousProjectPart) const; + +private: + FallBackProjectPart m_fallbackProjectPart; + ProjectPartsForFile m_projectPartsForFile; + ProjectPartsFromDependenciesForFile m_projectPartsFromDependenciesForFile; +}; + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index bb0f6149ed2..00611dbca11 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -82,6 +82,7 @@ HEADERS += \ clangcompileroptionsbuilder.h \ cppprojectinterface.h \ cppbaseprojectpartbuilder.h \ + cppprojectpartchooser.h \ SOURCES += \ abstracteditorsupport.cpp \ @@ -156,7 +157,8 @@ SOURCES += \ compileroptionsbuilder.cpp \ cppprojectfilecategorizer.cpp \ cppbaseprojectpartbuilder.cpp \ - clangcompileroptionsbuilder.cpp + clangcompileroptionsbuilder.cpp \ + cppprojectpartchooser.cpp \ FORMS += \ clangdiagnosticconfigswidget.ui \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index 1fae89e9ec9..53ff39b5e9d 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -108,6 +108,7 @@ Project { "cppprojectinterface.h", "cppbaseprojectpartbuilder.cpp", "cppbaseprojectpartbuilder.h", "clangcompileroptionsbuilder.cpp", "clangcompileroptionsbuilder.h", + "cppprojectpartchooser.cpp", "cppprojectpartchooser.h", ] Group { diff --git a/src/plugins/cpptools/cpptoolsunittestfiles.pri b/src/plugins/cpptools/cpptoolsunittestfiles.pri index 4a2b7902b41..1ed85c6c6ac 100644 --- a/src/plugins/cpptools/cpptoolsunittestfiles.pri +++ b/src/plugins/cpptools/cpptoolsunittestfiles.pri @@ -18,7 +18,7 @@ HEADERS += \ $$PWD/cppbaseprojectpartbuilder.h \ $$PWD/projectinfo.h \ $$PWD/cppprojectinterface.h \ - + $$PWD/cppprojectpartchooser.h \ SOURCES += \ $$PWD/cppprojectfile.cpp \ @@ -29,3 +29,4 @@ SOURCES += \ $$PWD/clangcompileroptionsbuilder.cpp \ $$PWD/cppbaseprojectpartbuilder.cpp \ $$PWD/projectinfo.cpp \ + $$PWD/cppprojectpartchooser.cpp \ diff --git a/tests/unit/unittest/cppprojectpartchooser-test.cpp b/tests/unit/unittest/cppprojectpartchooser-test.cpp new file mode 100644 index 00000000000..2a6e85946de --- /dev/null +++ b/tests/unit/unittest/cppprojectpartchooser-test.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** 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 "googletest.h" + +#include +#include + +using CppTools::Internal::ProjectPartChooser; +using CppTools::ProjectPart; + +using testing::Eq; + +namespace { + +class ProjectPartChooser : public ::testing::Test +{ +protected: + void SetUp() override; + const ProjectPart::Ptr choose() const; + +protected: + QString filePath; + ProjectPart::Ptr currentProjectPart{new ProjectPart}; + ProjectPart::Ptr manuallySetProjectPart; + bool stickToPreviousProjectPart = false; + ::ProjectPartChooser chooser; + + QList projectPartsForFile; + QList projectPartsFromDependenciesForFile; + ProjectPart::Ptr fallbackProjectPart; +}; + +TEST_F(ProjectPartChooser, ChooseManuallySet) +{ + manuallySetProjectPart.reset(new ProjectPart); + + const ProjectPart::Ptr chosen = choose(); + + ASSERT_THAT(chosen, Eq(manuallySetProjectPart)); +} + +TEST_F(ProjectPartChooser, ForMultipleChoosePrevious) +{ + const ProjectPart::Ptr otherProjectPart; + projectPartsForFile += otherProjectPart; + projectPartsForFile += currentProjectPart; + + const ProjectPart::Ptr chosen = choose(); + + ASSERT_THAT(chosen, Eq(currentProjectPart)); +} + +TEST_F(ProjectPartChooser, IfProjectIsGoneStickToPrevious) // Built-in Code Model +{ + stickToPreviousProjectPart = true; + + const ProjectPart::Ptr chosen = choose(); + + ASSERT_THAT(chosen, Eq(currentProjectPart)); +} + +TEST_F(ProjectPartChooser, IfProjectIsGoneDoNotStickToPrevious) // Clang Code Model +{ + currentProjectPart.clear(); + stickToPreviousProjectPart = true; + + const ProjectPart::Ptr chosen = choose(); + + ASSERT_THAT(chosen, Eq(ProjectPart::Ptr())); +} + +TEST_F(ProjectPartChooser, ForMultipleChooseNewIfPreviousIsGone) +{ + const ProjectPart::Ptr newProjectPart; + projectPartsForFile += newProjectPart; + + const ProjectPart::Ptr chosen = choose(); + + ASSERT_THAT(chosen, Eq(newProjectPart)); +} + +TEST_F(ProjectPartChooser, FallbackToProjectPartFromDependencies) +{ + const ProjectPart::Ptr fromDependencies{new ProjectPart}; + projectPartsFromDependenciesForFile += fromDependencies; + + const ProjectPart::Ptr chosen = choose(); + + ASSERT_THAT(chosen, Eq(fromDependencies)); +} + +TEST_F(ProjectPartChooser, FallbackToProjectPartFromModelManager) +{ + fallbackProjectPart.reset(new ProjectPart); + + const ProjectPart::Ptr chosen = choose(); + + ASSERT_THAT(chosen, Eq(fallbackProjectPart)); +} + +void ProjectPartChooser::SetUp() +{ + chooser.setFallbackProjectPart([&](){ + return fallbackProjectPart; + }); + chooser.setProjectPartsForFile([&](const QString &) { + return projectPartsForFile; + }); + chooser.setProjectPartsFromDependenciesForFile([&](const QString &) { + return projectPartsFromDependenciesForFile; + }); +} + +const ProjectPart::Ptr ProjectPartChooser::choose() const +{ + return chooser.choose(filePath, + currentProjectPart, + manuallySetProjectPart, + stickToPreviousProjectPart); +} + +} // anonymous namespace diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 48131fe2ac5..3fd0c618753 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -30,6 +30,7 @@ SOURCES += \ lineprefixer-test.cpp \ cppprojectfilecategorizer-test.cpp \ cppbaseprojectpartbuilder-test.cpp \ + cppprojectpartchooser-test.cpp \ processevents-utilities.cpp \ mimedatabase-utilities.cpp \ readandwritemessageblock-test.cpp \