forked from qt-creator/qt-creator
		
	* Move the combox box for switching the parse configurations out of the
  "Additional Preprocessor Directives" dialog ('#'-button) to make it
  better visible/accessible. Also, decouple the extra preprocessor
  directives from the concrete parse context since this is not anymore
  in same dialog.
* The combo box appears only if multiple parse configurations are
  available for a file.
* The first time multiple parse configurations are detected, an info bar
  is shown that points the user to the combox box. A "Do Not Show Again"
  button is provided.
* Upon selecting an entry, the preferred parse configuration is saved as
  part of the session. The setting can be cleared with the context menu
  entry on the combo box.
Follow-up changes need to ensure that the display name and/or tooltip is
unambiguous, e.g. for qbs and cmake projects.
Change-Id: I9e9773704187291524ad7b605bfdddd83ef5b19d
Reviewed-by: David Schulz <david.schulz@qt.io>
		
	
		
			
				
	
	
		
			357 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/****************************************************************************
 | 
						|
**
 | 
						|
** 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 <cpptools/cppprojectpartchooser.h>
 | 
						|
#include <cpptools/cpptools_utils.h>
 | 
						|
#include <cpptools/projectpart.h>
 | 
						|
 | 
						|
using CppTools::Internal::ProjectPartChooser;
 | 
						|
using CppTools::ProjectPart;
 | 
						|
using CppTools::ProjectPartInfo;
 | 
						|
using CppTools::Language;
 | 
						|
 | 
						|
using testing::Eq;
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class ProjectPartChooser : public ::testing::Test
 | 
						|
{
 | 
						|
protected:
 | 
						|
    void SetUp() override;
 | 
						|
    const ProjectPartInfo choose() const;
 | 
						|
 | 
						|
    static QList<ProjectPart::Ptr> createProjectPartsWithDifferentProjects();
 | 
						|
    static QList<ProjectPart::Ptr> createCAndCxxProjectParts();
 | 
						|
 | 
						|
protected:
 | 
						|
    QString filePath;
 | 
						|
    ProjectPart::Ptr currentProjectPart{new ProjectPart};
 | 
						|
    ProjectPartInfo currentProjectPartInfo{currentProjectPart,
 | 
						|
                                           {currentProjectPart},
 | 
						|
                                           ProjectPartInfo::NoHint};
 | 
						|
    QString preferredProjectPartId;
 | 
						|
    const ProjectExplorer::Project *activeProject = nullptr;
 | 
						|
    Language languagePreference = Language::Cxx;
 | 
						|
    bool projectsChanged = false;
 | 
						|
    ::ProjectPartChooser chooser;
 | 
						|
 | 
						|
    QList<ProjectPart::Ptr> projectPartsForFile;
 | 
						|
    QList<ProjectPart::Ptr> projectPartsFromDependenciesForFile;
 | 
						|
    ProjectPart::Ptr fallbackProjectPart;
 | 
						|
};
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ChooseManuallySet)
 | 
						|
{
 | 
						|
    ProjectPart::Ptr p1(new ProjectPart);
 | 
						|
    ProjectPart::Ptr p2(new ProjectPart);
 | 
						|
    p2->projectFile = preferredProjectPartId = "someId";
 | 
						|
    projectPartsForFile += {p1, p2};
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(p2));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, IndicateManuallySet)
 | 
						|
{
 | 
						|
    ProjectPart::Ptr p1(new ProjectPart);
 | 
						|
    ProjectPart::Ptr p2(new ProjectPart);
 | 
						|
    p2->projectFile = preferredProjectPartId = "someId";
 | 
						|
    projectPartsForFile += {p1, p2};
 | 
						|
 | 
						|
    const ProjectPartInfo::Hints hints = choose().hints;
 | 
						|
 | 
						|
    ASSERT_TRUE(hints & ProjectPartInfo::IsPreferredMatch);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, IndicateManuallySetForFallbackToProjectPartFromDependencies)
 | 
						|
{
 | 
						|
    ProjectPart::Ptr p1(new ProjectPart);
 | 
						|
    ProjectPart::Ptr p2(new ProjectPart);
 | 
						|
    p2->projectFile = preferredProjectPartId = "someId";
 | 
						|
    projectPartsFromDependenciesForFile += {p1, p2};
 | 
						|
 | 
						|
    const ProjectPartInfo::Hints hints = choose().hints;
 | 
						|
 | 
						|
    ASSERT_TRUE(hints & ProjectPartInfo::IsPreferredMatch);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, DoNotIndicateNotManuallySet)
 | 
						|
{
 | 
						|
    const ProjectPartInfo::Hints hints = choose().hints;
 | 
						|
 | 
						|
    ASSERT_FALSE(hints & ProjectPartInfo::IsPreferredMatch);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ForMultipleChooseFromActiveProject)
 | 
						|
{
 | 
						|
    const QList<ProjectPart::Ptr> projectParts = createProjectPartsWithDifferentProjects();
 | 
						|
    const ProjectPart::Ptr secondProjectPart = projectParts.at(1);
 | 
						|
    projectPartsForFile += projectParts;
 | 
						|
    activeProject = secondProjectPart->project;
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(secondProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ForMultiplePreferSelectedForBuilding)
 | 
						|
{
 | 
						|
    const ProjectPart::Ptr firstProjectPart{new ProjectPart};
 | 
						|
    const ProjectPart::Ptr secondProjectPart{new ProjectPart};
 | 
						|
    firstProjectPart->selectedForBuilding = false;
 | 
						|
    secondProjectPart->selectedForBuilding = true;
 | 
						|
    projectPartsForFile += firstProjectPart;
 | 
						|
    projectPartsForFile += secondProjectPart;
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(secondProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ForMultipleFromDependenciesChooseFromActiveProject)
 | 
						|
{
 | 
						|
    const QList<ProjectPart::Ptr> projectParts = createProjectPartsWithDifferentProjects();
 | 
						|
    const ProjectPart::Ptr secondProjectPart = projectParts.at(1);
 | 
						|
    projectPartsFromDependenciesForFile += projectParts;
 | 
						|
    activeProject = secondProjectPart->project;
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(secondProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ForMultipleCheckIfActiveProjectChanged)
 | 
						|
{
 | 
						|
    const QList<ProjectPart::Ptr> projectParts = createProjectPartsWithDifferentProjects();
 | 
						|
    const ProjectPart::Ptr firstProjectPart = projectParts.at(0);
 | 
						|
    const ProjectPart::Ptr secondProjectPart = projectParts.at(1);
 | 
						|
    projectPartsForFile += projectParts;
 | 
						|
    currentProjectPartInfo.projectPart = firstProjectPart;
 | 
						|
    activeProject = secondProjectPart->project;
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(secondProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ForMultipleAndAmbigiousHeaderPreferCProjectPart)
 | 
						|
{
 | 
						|
    languagePreference = Language::C;
 | 
						|
    projectPartsForFile = createCAndCxxProjectParts();
 | 
						|
    const ProjectPart::Ptr cProjectPart = projectPartsForFile.at(0);
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(cProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ForMultipleAndAmbigiousHeaderPreferCxxProjectPart)
 | 
						|
{
 | 
						|
    languagePreference = Language::Cxx;
 | 
						|
    projectPartsForFile = createCAndCxxProjectParts();
 | 
						|
    const ProjectPart::Ptr cxxProjectPart = projectPartsForFile.at(1);
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(cxxProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, IndicateMultiple)
 | 
						|
{
 | 
						|
    const ProjectPart::Ptr p1{new ProjectPart};
 | 
						|
    const ProjectPart::Ptr p2{new ProjectPart};
 | 
						|
    projectPartsForFile += { p1, p2 };
 | 
						|
 | 
						|
    const ProjectPartInfo::Hints hints = choose().hints;
 | 
						|
 | 
						|
    ASSERT_TRUE(hints & ProjectPartInfo::IsAmbiguousMatch);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, IndicateMultipleForFallbackToProjectPartFromDependencies)
 | 
						|
{
 | 
						|
    const ProjectPart::Ptr p1{new ProjectPart};
 | 
						|
    const ProjectPart::Ptr p2{new ProjectPart};
 | 
						|
    projectPartsFromDependenciesForFile += { p1, p2 };
 | 
						|
 | 
						|
    const ProjectPartInfo::Hints hints = choose().hints;
 | 
						|
 | 
						|
    ASSERT_TRUE(hints & ProjectPartInfo::IsAmbiguousMatch);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ForMultipleChooseNewIfPreviousIsGone)
 | 
						|
{
 | 
						|
    const ProjectPart::Ptr newProjectPart{new ProjectPart};
 | 
						|
    projectPartsForFile += newProjectPart;
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(newProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, FallbackToProjectPartFromDependencies)
 | 
						|
{
 | 
						|
    const ProjectPart::Ptr fromDependencies{new ProjectPart};
 | 
						|
    projectPartsFromDependenciesForFile += fromDependencies;
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(fromDependencies));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, FallbackToProjectPartFromModelManager)
 | 
						|
{
 | 
						|
    fallbackProjectPart.reset(new ProjectPart);
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(fallbackProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, ContinueUsingFallbackFromModelManagerIfProjectDoesNotChange)
 | 
						|
{
 | 
						|
    // ...without re-calculating the dependency table.
 | 
						|
    fallbackProjectPart.reset(new ProjectPart);
 | 
						|
    currentProjectPartInfo.projectPart = fallbackProjectPart;
 | 
						|
    currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch;
 | 
						|
    projectPartsFromDependenciesForFile += ProjectPart::Ptr(new ProjectPart);
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(fallbackProjectPart));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, StopUsingFallbackFromModelManagerIfProjectChanges1)
 | 
						|
{
 | 
						|
    fallbackProjectPart.reset(new ProjectPart);
 | 
						|
    currentProjectPartInfo.projectPart = fallbackProjectPart;
 | 
						|
    currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch;
 | 
						|
    const ProjectPart::Ptr addedProject(new ProjectPart);
 | 
						|
    projectPartsForFile += addedProject;
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(addedProject));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, StopUsingFallbackFromModelManagerIfProjectChanges2)
 | 
						|
{
 | 
						|
    fallbackProjectPart.reset(new ProjectPart);
 | 
						|
    currentProjectPartInfo.projectPart = fallbackProjectPart;
 | 
						|
    currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch;
 | 
						|
    const ProjectPart::Ptr addedProject(new ProjectPart);
 | 
						|
    projectPartsFromDependenciesForFile += addedProject;
 | 
						|
    projectsChanged = true;
 | 
						|
 | 
						|
    const ProjectPart::Ptr chosen = choose().projectPart;
 | 
						|
 | 
						|
    ASSERT_THAT(chosen, Eq(addedProject));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, IndicateFallbacktoProjectPartFromModelManager)
 | 
						|
{
 | 
						|
    fallbackProjectPart.reset(new ProjectPart);
 | 
						|
 | 
						|
    const ProjectPartInfo::Hints hints = choose().hints;
 | 
						|
 | 
						|
    ASSERT_TRUE(hints & ProjectPartInfo::IsFallbackMatch);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, IndicateFromDependencies)
 | 
						|
{
 | 
						|
    projectPartsFromDependenciesForFile += ProjectPart::Ptr(new ProjectPart);
 | 
						|
 | 
						|
    const ProjectPartInfo::Hints hints = choose().hints;
 | 
						|
 | 
						|
    ASSERT_TRUE(hints & ProjectPartInfo::IsFromDependenciesMatch);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ProjectPartChooser, DoNotIndicateFromDependencies)
 | 
						|
{
 | 
						|
    projectPartsForFile += ProjectPart::Ptr(new ProjectPart);
 | 
						|
 | 
						|
    const ProjectPartInfo::Hints hints = choose().hints;
 | 
						|
 | 
						|
    ASSERT_FALSE(hints & ProjectPartInfo::IsFromDependenciesMatch);
 | 
						|
}
 | 
						|
 | 
						|
void ProjectPartChooser::SetUp()
 | 
						|
{
 | 
						|
    chooser.setFallbackProjectPart([&]() {
 | 
						|
        return fallbackProjectPart;
 | 
						|
    });
 | 
						|
    chooser.setProjectPartsForFile([&](const QString &) {
 | 
						|
        return projectPartsForFile;
 | 
						|
    });
 | 
						|
    chooser.setProjectPartsFromDependenciesForFile([&](const QString &) {
 | 
						|
        return projectPartsFromDependenciesForFile;
 | 
						|
    });
 | 
						|
}
 | 
						|
 | 
						|
const ProjectPartInfo ProjectPartChooser::choose() const
 | 
						|
{
 | 
						|
    return chooser.choose(filePath,
 | 
						|
                          currentProjectPartInfo,
 | 
						|
                          preferredProjectPartId,
 | 
						|
                          activeProject,
 | 
						|
                          languagePreference,
 | 
						|
                          projectsChanged);
 | 
						|
}
 | 
						|
 | 
						|
QList<ProjectPart::Ptr> ProjectPartChooser::createProjectPartsWithDifferentProjects()
 | 
						|
{
 | 
						|
    QList<ProjectPart::Ptr> projectParts;
 | 
						|
 | 
						|
    const ProjectPart::Ptr p1{new ProjectPart};
 | 
						|
    p1->project = reinterpret_cast<ProjectExplorer::Project *>(1 << 0);
 | 
						|
    projectParts.append(p1);
 | 
						|
    const ProjectPart::Ptr p2{new ProjectPart};
 | 
						|
    p2->project = reinterpret_cast<ProjectExplorer::Project *>(1 << 1);
 | 
						|
    projectParts.append(p2);
 | 
						|
 | 
						|
    return projectParts;
 | 
						|
}
 | 
						|
 | 
						|
QList<ProjectPart::Ptr> ProjectPartChooser::createCAndCxxProjectParts()
 | 
						|
{
 | 
						|
    QList<ProjectPart::Ptr> projectParts;
 | 
						|
 | 
						|
    // Create project part for C
 | 
						|
    const ProjectPart::Ptr cprojectpart{new ProjectPart};
 | 
						|
    cprojectpart->languageVersion = ProjectPart::C11;
 | 
						|
    projectParts.append(cprojectpart);
 | 
						|
 | 
						|
    // Create project part for CXX
 | 
						|
    const ProjectPart::Ptr cxxprojectpart{new ProjectPart};
 | 
						|
    cxxprojectpart->languageVersion = ProjectPart::CXX98;
 | 
						|
    projectParts.append(cxxprojectpart);
 | 
						|
 | 
						|
    return projectParts;
 | 
						|
}
 | 
						|
 | 
						|
} // anonymous namespace
 |