Skip to content

Commit

Permalink
SLVS-1671 Refactor IFileConfigProvider (#5862)
Browse files Browse the repository at this point in the history
  • Loading branch information
georgii-borovinskikh-sonarsource committed Dec 9, 2024
1 parent 9af16e6 commit b1ddcbd
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 687 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,140 +18,138 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.VCProjectEngine;
using Moq;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell.Interop;
using NSubstitute.ExceptionExtensions;
using NSubstitute.ReturnsExtensions;
using SonarLint.VisualStudio.Core;
using SonarLint.VisualStudio.CFamily.Analysis;
using SonarLint.VisualStudio.Infrastructure.VS;
using SonarLint.VisualStudio.Integration.Vsix.CFamily.VcxProject;
using SonarLint.VisualStudio.TestInfrastructure;
using static SonarLint.VisualStudio.Integration.Vsix.CFamily.UnitTests.CFamilyTestUtility;

namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily.VcxProject
{
[TestClass]
public class FileConfigProviderTests
{
private const string SourceFilePath = "any path";
private TestLogger logger;
private IFileInSolutionIndicator fileInSolutionIndicator;
private DTE2 dte;
private IVsUIServiceOperation uiServiceOperation;
private FileConfigProvider testSubject;

[TestMethod]
public void MefCtor_CheckIsExported() =>
MefTestHelpers.CheckTypeCanBeImported<FileConfigProvider, IFileConfigProvider>(
MefTestHelpers.CreateExport<ILogger>());
MefTestHelpers.CreateExport<IFileInSolutionIndicator>(),
MefTestHelpers.CreateExport<ILogger>(),
MefTestHelpers.CreateExport<IThreadHandling>(),
MefTestHelpers.CreateExport<IVsUIServiceOperation>());

[TestMethod]
public void MefCtor_CheckIsSingleton() => MefTestHelpers.CheckIsSingletonMefComponent<FileConfigProvider>();

[TestMethod]
public void Get_FileIsNotInSolution_ReturnsNull()
[TestInitialize]
public void TestInitialize()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\SingleFileISense\\xxx.vcxproj");

var testSubject = CreateTestSubject();
var result = testSubject.Get(projectItemMock.Object, "c:\\dummy", new CFamilyAnalyzerOptions());
logger = new TestLogger();
fileInSolutionIndicator = CreateDefaultFileInSolutionIndicator();
dte = Substitute.For<DTE2>();
uiServiceOperation = CreateDefaultUiServiceOperation(dte);

result.Should().BeNull();
projectItemMock.Verify(x => x.ContainingProject, Times.Once);
testSubject = new FileConfigProvider(uiServiceOperation, fileInSolutionIndicator, logger, new NoOpThreadHandler());
}

[TestMethod]
public void Get_FailureToCheckIfFileIsInSolution_NonCriticalException_ExceptionCaughtAndNullReturned()
private static IFileInSolutionIndicator CreateDefaultFileInSolutionIndicator()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\SingleFileISense\\xxx.vcxproj");
projectItemMock.Setup(x => x.ContainingProject).Throws<NotImplementedException>();

var testSubject = CreateTestSubject();
var result = testSubject.Get(projectItemMock.Object, "c:\\dummy", new CFamilyAnalyzerOptions());

result.Should().BeNull();
projectItemMock.Verify(x => x.ContainingProject, Times.Once);
var mock = Substitute.For<IFileInSolutionIndicator>();
mock.IsFileInSolution(Arg.Any<ProjectItem>()).Returns(true);
return mock;
}

[TestMethod]
public void Get_FailureToCheckIfFileIsInSolution_CriticalException_ExceptionThrown()
private static IVsUIServiceOperation CreateDefaultUiServiceOperation(DTE2 dte2)
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\SingleFileISense\\xxx.vcxproj");
projectItemMock.Setup(x => x.ContainingProject).Throws<StackOverflowException>();

var testSubject = CreateTestSubject();
Action act = () => testSubject.Get(projectItemMock.Object, "c:\\dummy", new CFamilyAnalyzerOptions());

act.Should().Throw<StackOverflowException>();
projectItemMock.Verify(x => x.ContainingProject, Times.Once);
var mock = Substitute.For<IVsUIServiceOperation>();
mock.Execute<SDTE, DTE2, IFileConfig>(Arg.Any<Func<DTE2, IFileConfig>>()).Returns(info => info.Arg<Func<DTE2, IFileConfig>>()(dte2));
return mock;
}

[TestMethod]
public void Get_FailsToRetrieveFileConfig_NonCriticalException_ExceptionCaughtAndNullReturned()
public void Get_FailsToRetrieveProjectItem_NonCriticalException_ExceptionCaughtAndNullReturned()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\xxx.vcxproj");
var containingProject = Mock.Get(projectItemMock.Object.ContainingProject.Object as VCProject);
containingProject.Setup(x => x.ActiveConfiguration).Throws<NotImplementedException>();
dte.Solution.ThrowsForAnyArgs<NotImplementedException>();

var testSubject = CreateTestSubject();
var result = testSubject.Get(projectItemMock.Object, "c:\\dummy", new CFamilyAnalyzerOptions());
var result = testSubject.Get(SourceFilePath, new CFamilyAnalyzerOptions());

result.Should().BeNull();
containingProject.Verify(x => x.ActiveConfiguration, Times.Once);
logger.AssertPartialOutputStringExists(nameof(NotImplementedException), SourceFilePath);
}

[TestMethod]
public void Get_FailsToRetrieveFileConfig_CriticalException_ExceptionThrown()
public void Get_FailsToRetrieveProjectItem_NonCriticalException_Pch_ExceptionCaughtNotLoggedAndNullReturned()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\xxx.vcxproj");
var containingProject = Mock.Get(projectItemMock.Object.ContainingProject.Object as VCProject);
containingProject.Setup(x => x.ActiveConfiguration).Throws<StackOverflowException>();
dte.Solution.ThrowsForAnyArgs<NotImplementedException>();

var testSubject = CreateTestSubject();
Action act = () => testSubject.Get(projectItemMock.Object, "c:\\dummy", new CFamilyAnalyzerOptions());
var result = testSubject.Get(SourceFilePath, new CFamilyAnalyzerOptions{CreatePreCompiledHeaders = true});

act.Should().Throw<StackOverflowException>();
containingProject.Verify(x => x.ActiveConfiguration, Times.Once);
result.Should().BeNull();
logger.AssertNoOutputMessages();
}

[TestMethod]
public void Get_FailsToRetrieveFileConfig_NotPch_ExceptionLogged()
public void Get_FailsToRetrieveProjectItem_CriticalException_ExceptionThrown()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\xxx.vcxproj");
var containingProject = Mock.Get(projectItemMock.Object.ContainingProject.Object as VCProject);
containingProject.Setup(x => x.ActiveConfiguration).Throws<NotImplementedException>();
dte.Solution.ThrowsForAnyArgs<DivideByZeroException>();

var logger = new TestLogger();
var testSubject = CreateTestSubject(logger);
testSubject.Get(projectItemMock.Object, "c:\\dummy", new CFamilyAnalyzerOptions());
Action act = () => testSubject.Get(SourceFilePath, new CFamilyAnalyzerOptions());

logger.AssertPartialOutputStringExists(nameof(NotImplementedException), "c:\\dummy");
act.Should().Throw<DivideByZeroException>();
}

[TestMethod]
public void Get_FailsToRetrieveFileConfig_Pch_ExceptionNotLogged()
public void Get_NoProjectItem_ReturnsNull()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\xxx.vcxproj");
var containingProject = Mock.Get(projectItemMock.Object.ContainingProject.Object as VCProject);
containingProject.Setup(x => x.ActiveConfiguration).Throws<NotImplementedException>();
dte.Solution.FindProjectItem(SourceFilePath).ReturnsNull();

var logger = new TestLogger();
var testSubject = CreateTestSubject(logger);
testSubject.Get(projectItemMock.Object, "c:\\dummy", new CFamilyAnalyzerOptions{CreatePreCompiledHeaders = true});
testSubject.Get(SourceFilePath, null)
.Should().BeNull();

logger.AssertNoOutputMessages();
Received.InOrder(() =>
{
uiServiceOperation.Execute<SDTE, DTE2, IFileConfig>(Arg.Any<Func<DTE2, IFileConfig>>());
dte.Solution.FindProjectItem(SourceFilePath);
});
}

[TestMethod]
public void Get_SuccessfulConfig_ConfigReturned()
public void Get_ProjectItemNotInSolution_ReturnsNull()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\xxx.vcxproj");

var testSubject = CreateTestSubject();
var result = testSubject.Get(projectItemMock.Object, "c:\\dummy", new CFamilyAnalyzerOptions());

result.Should().NotBeNull();
var mockProjectItem = CreateMockProjectItem(SourceFilePath);
dte.Solution.FindProjectItem(SourceFilePath).Returns(mockProjectItem.Object);
fileInSolutionIndicator.IsFileInSolution(mockProjectItem.Object).Returns(false);

testSubject.Get(SourceFilePath, null)
.Should().BeNull();

Received.InOrder(() =>
{
uiServiceOperation.Execute<SDTE, DTE2, IFileConfig>(Arg.Any<Func<DTE2, IFileConfig>>());
dte.Solution.FindProjectItem(SourceFilePath);
fileInSolutionIndicator.IsFileInSolution(mockProjectItem.Object);
});
}

private FileConfigProvider CreateTestSubject(ILogger logger = null)
[TestMethod]
public void Get_SuccessfulConfig_ConfigReturned()
{
logger ??= Mock.Of<ILogger>();
var mockProjectItem = CreateMockProjectItem(SourceFilePath);
dte.Solution.FindProjectItem(SourceFilePath).Returns(mockProjectItem.Object);

return new FileConfigProvider(logger);
testSubject.Get(SourceFilePath, null)
.Should().NotBeNull();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using EnvDTE;
using SonarLint.VisualStudio.Core;
using SonarLint.VisualStudio.Integration.Vsix.CFamily.VcxProject;
using static SonarLint.VisualStudio.Integration.Vsix.CFamily.UnitTests.CFamilyTestUtility;

namespace SonarLint.VisualStudio.Integration.UnitTests.CFamily.VcxProject;

[TestClass]
public class FileInSolutionIndicatorTests
{
private FileInSolutionIndicator testSubject;
private IThreadHandling threadHandling;

[TestInitialize]
public void TestInitialize()
{
threadHandling = Substitute.For<IThreadHandling>();
testSubject = new FileInSolutionIndicator(threadHandling);
}

[TestMethod]
public void MefCtor_CheckIsExported() =>
MefTestHelpers.CheckTypeCanBeImported<FileInSolutionIndicator, IFileInSolutionIndicator>(
MefTestHelpers.CreateExport<IThreadHandling>());

[TestMethod]
public void MefCtor_CheckIsSingleton() =>
MefTestHelpers.CheckIsSingletonMefComponent<FileInSolutionIndicator>();

[TestMethod]
public void Get_FileIsNotInSolution_ReturnsFalse()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\SingleFileISense\\xxx.vcxproj");

testSubject.IsFileInSolution(projectItemMock.Object)
.Should().BeFalse();

projectItemMock.Verify(x => x.ContainingProject);
threadHandling.Received().ThrowIfNotOnUIThread();
}

[TestMethod]
public void Get_NoConfigurationManager_ReturnsFalse()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\Any\\xxx.vcxproj");
projectItemMock.SetupGet(x => x.ConfigurationManager).Returns(null as ConfigurationManager);

testSubject.IsFileInSolution(projectItemMock.Object)
.Should().BeFalse();

projectItemMock.Verify(x => x.ContainingProject);
projectItemMock.Verify(x => x.ConfigurationManager);
threadHandling.Received().ThrowIfNotOnUIThread();
}

[TestMethod]
public void Get_NoConfiguration_ReturnsFalse()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\Any\\xxx.vcxproj");
var configurationManagerMock = Mock.Get(projectItemMock.Object.ConfigurationManager);
configurationManagerMock.SetupGet(x => x.ActiveConfiguration).Returns(null as Configuration);

testSubject.IsFileInSolution(projectItemMock.Object)
.Should().BeFalse();

projectItemMock.Verify(x => x.ContainingProject);
projectItemMock.Verify(x => x.ConfigurationManager);
configurationManagerMock.Verify(x => x.ActiveConfiguration);
threadHandling.Received().ThrowIfNotOnUIThread();
}

[TestMethod]
public void Get_FileInSolution_ReturnsTrue()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\Any\\xxx.vcxproj");

testSubject.IsFileInSolution(projectItemMock.Object)
.Should().BeTrue();

projectItemMock.Verify(x => x.ContainingProject);
projectItemMock.Verify(x => x.ConfigurationManager);
threadHandling.Received().ThrowIfNotOnUIThread();
}

[TestMethod]
public void Get_FailureToCheckIfFileIsInSolution_NonCriticalException_ExceptionCaughtAndFalseReturned()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\Any\\xxx.vcxproj");
projectItemMock.Setup(x => x.ContainingProject).Throws<NotImplementedException>();

testSubject.IsFileInSolution(projectItemMock.Object)
.Should().BeFalse();

projectItemMock.Verify(x => x.ContainingProject);
threadHandling.Received().ThrowIfNotOnUIThread();
}

[TestMethod]
public void Get_FailureToCheckIfFileIsInSolution_CriticalException_ExceptionThrown()
{
var projectItemMock = CreateMockProjectItem("c:\\foo\\Any\\xxx.vcxproj");
projectItemMock.Setup(x => x.ContainingProject).Throws<DivideByZeroException>();

Action act = () => testSubject.IsFileInSolution(projectItemMock.Object);

act.Should().Throw<DivideByZeroException>();
projectItemMock.Verify(x => x.ContainingProject);
threadHandling.Received().ThrowIfNotOnUIThread();
}
}
Loading

0 comments on commit b1ddcbd

Please sign in to comment.