diff --git a/FineCodeCoverageTests/AppOptionsProvider_Tests.cs b/FineCodeCoverageTests/AppOptionsProvider_Tests.cs index b79277e6..d005303e 100644 --- a/FineCodeCoverageTests/AppOptionsProvider_Tests.cs +++ b/FineCodeCoverageTests/AppOptionsProvider_Tests.cs @@ -144,6 +144,12 @@ public void Should_Default_Enabled_True() DefaultTest(appOptions => appOptions.Enabled = true); } + [Test] + public void Should_Default_DisabledNoCoverage_True() + { + DefaultTest(appOptions => appOptions.DisabledNoCoverage = true); + } + [Test] public void Should_Default_True_ShowCoverageInOverviewMargin() { @@ -198,7 +204,8 @@ public void Should_Not_Default_Any_Other_AppOptions_Properties() nameof(IAppOptions.ShowUncoveredInOverviewMargin), nameof(IAppOptions.ShowPartiallyCoveredInOverviewMargin), nameof(IAppOptions.ShowToolWindowToolbar), - nameof(IAppOptions.Hide0Coverable) + nameof(IAppOptions.Hide0Coverable), + nameof(IAppOptions.DisabledNoCoverage) }; CollectionAssert.AreEquivalent(expectedSetters.Select(s => $"set_{s}"), invocationNames); } @@ -272,6 +279,7 @@ internal void Should_Use_Deseralized_String_From_Store_For_AppOption_Property(Fu { nameof(IAppOptions.CoverletConsoleGlobal), true}, { nameof(IAppOptions.CoverletConsoleLocal), true}, { nameof(IAppOptions.Enabled), true}, + { nameof(IAppOptions.DisabledNoCoverage), true}, { nameof(IAppOptions.Exclude), new string[]{"exclude" } }, { nameof(IAppOptions.ExcludeByAttribute), new string[]{ "ebyatt"} }, { nameof(IAppOptions.ExcludeByFile), new string[]{ "ebyfile"} }, diff --git a/FineCodeCoverageTests/MsCodeCoverage/RunSettingsTemplateReplacementsFactory_Tests.cs b/FineCodeCoverageTests/MsCodeCoverage/RunSettingsTemplateReplacementsFactory_Tests.cs index 3987d6a3..1e6fc222 100644 --- a/FineCodeCoverageTests/MsCodeCoverage/RunSettingsTemplateReplacementsFactory_Tests.cs +++ b/FineCodeCoverageTests/MsCodeCoverage/RunSettingsTemplateReplacementsFactory_Tests.cs @@ -32,6 +32,7 @@ internal class TestMsCodeCoverageOptions : IMsCodeCoverageOptions public bool IncludeReferencedProjects { get; set; } public string[] ExcludeAssemblies { get; set; } public string[] IncludeAssemblies { get; set; } + public bool DisabledNoCoverage { get; set; } } internal static class ReplacementsAssertions @@ -699,5 +700,6 @@ internal class TestCoverageProjectOptions : IAppOptions public bool Hide0Coverage { get; set; } public string[] ExcludeAssemblies { get; set; } public string[] IncludeAssemblies { get; set; } + public bool DisabledNoCoverage { get; set; } } } diff --git a/FineCodeCoverageTests/TestContainerDiscovery_Tests.cs b/FineCodeCoverageTests/TestContainerDiscovery_Tests.cs index 195f481a..4868125a 100644 --- a/FineCodeCoverageTests/TestContainerDiscovery_Tests.cs +++ b/FineCodeCoverageTests/TestContainerDiscovery_Tests.cs @@ -46,6 +46,11 @@ private void AssertShouldNotReloadCoverage() mocker.Verify(engine => engine.ReloadCoverage(It.IsAny>>>()), Times.Never()); } + private void AssertReloadsCoverage() + { + mocker.Verify(engine => engine.ReloadCoverage(It.IsAny>>>()), Times.Once()); + } + private void SetUpOptions(Action> setupAppOptions) { var mockAppOptions = new Mock(); @@ -252,18 +257,33 @@ public async Task Should_ReloadCoverage_When_TestExecutionStarting_And_Settings_ } [Test] - public void Should_Not_ReloadCoverage_When_TestExecutionStarting_And_Settings_RunInParallel_Is_True_When_Enabled_is_False() + public void Should_Not_ReloadCoverage_When_TestExecutionStarting_And_Settings_RunInParallel_Is_True_When_Enabled_Is_False_And_DisabledNoCoverage_True() { SetUpOptions(mockAppOptions => { mockAppOptions.Setup(o => o.Enabled).Returns(false); mockAppOptions.Setup(o => o.RunInParallel).Returns(true); + mockAppOptions.Setup(o => o.DisabledNoCoverage).Returns(true); }); RaiseTestExecutionStarting(); AssertShouldNotReloadCoverage(); } + [Test] + public void Should_ReloadCoverage_When_TestExecutionStarting_And_Settings_RunInParallel_Is_True_When_Enabled_Is_False_And_DisabledNoCoverage_False() + { + SetUpOptions(mockAppOptions => + { + mockAppOptions.Setup(o => o.Enabled).Returns(false); + mockAppOptions.Setup(o => o.RunInParallel).Returns(true); + mockAppOptions.Setup(o => o.DisabledNoCoverage).Returns(false); + }); + + RaiseTestExecutionStarting(); + AssertReloadsCoverage(); + } + [TestCase(true, 10, 1, 0, true, Description = "Should run when tests fail if settings RunWhenTestsFail is true")] [TestCase(false, 10, 1, 0, false, Description = "Should not run when tests fail if settings RunWhenTestsFail is false")] [TestCase(false, 0, 1, 1, false, Description = "Should not run when total tests does not exceed the RunWhenTestsExceed setting")] diff --git a/README.md b/README.md index 90a3343f..0550f386 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,7 @@ If you are using option 1) then project and global options will only be used whe |Hide0Coverage|Set to true to hide classes, namespaces and assemblies that have 0% coverage.| |Hide0Coverable|Set to false to show classes, namespaces and assemblies that are not coverable.| |Enabled|Specifies whether or not coverage output is enabled| +|DisabledNoCoverage|Set to false for VS Option Enabled=false to not disable coverage| |RunWhenTestsFail|By default coverage runs when tests fail. Set to false to prevent this. **Cannot be used in conjunction with RunInParallel**| |RunWhenTestsExceed|Specify a value to only run coverage based upon the number of executing tests. **Cannot be used in conjunction with RunInParallel**| |RunMsCodeCoverage|Change to IfInRunSettings to only collect with configured runsettings. Yes for runsettings generation.| diff --git a/SharedProject/Impl/TestContainerDiscovery/TestContainerDiscoverer.cs b/SharedProject/Impl/TestContainerDiscovery/TestContainerDiscoverer.cs index 5f39b22b..829dd567 100644 --- a/SharedProject/Impl/TestContainerDiscovery/TestContainerDiscoverer.cs +++ b/SharedProject/Impl/TestContainerDiscovery/TestContainerDiscoverer.cs @@ -91,6 +91,11 @@ IMsCodeCoverageRunSettingsService msCodeCoverageRunSettingsService ThreadHelper.JoinableTaskFactory.Run(taskProvider); }; + private bool CoverageDisabled(IAppOptions settings) + { + return !settings.Enabled && settings.DisabledNoCoverage; + } + private async Task TestExecutionStartingAsync(IOperation operation) { cancelling = false; @@ -98,7 +103,7 @@ private async Task TestExecutionStartingAsync(IOperation operation) StopCoverage(); var settings = appOptionsProvider.Get(); - if (!settings.Enabled) + if (CoverageDisabled(settings)) { CombinedLog("Coverage not collected as FCC disabled."); reportGeneratorUtil.EndOfCoverageRun(); @@ -155,7 +160,7 @@ private async Task TestExecutionFinishedAsync(IOperation operation) private bool ShouldNotCollectWhenTestExecutionFinished() { settings = appOptionsProvider.Get(); - return !settings.Enabled || runningInParallel || MsCodeCoverageErrored; + return CoverageDisabled(settings) || runningInParallel || MsCodeCoverageErrored; } diff --git a/SharedProject/Options/AppOptionsPage.cs b/SharedProject/Options/AppOptionsPage.cs index 480669cd..8153a64a 100644 --- a/SharedProject/Options/AppOptionsPage.cs +++ b/SharedProject/Options/AppOptionsPage.cs @@ -47,6 +47,10 @@ private static IAppOptionsStorageProvider GetAppOptionsStorageProvider() [Description("Specifies whether or not coverage output is enabled")] public bool Enabled { get; set; } + [Category(commonRunCategory)] + [Description("Set to false for VS Option Enabled=false to not disable coverage")] + public bool DisabledNoCoverage { get; set; } + [Category(commonRunCategory)] [Description("Specifies whether or not the ms code coverage is used (BETA). No, IfInRunSettings, Yes")] public RunMsCodeCoverage RunMsCodeCoverage { get; set; } diff --git a/SharedProject/Options/AppOptionsProvider.cs b/SharedProject/Options/AppOptionsProvider.cs index 478317f3..a54663b3 100644 --- a/SharedProject/Options/AppOptionsProvider.cs +++ b/SharedProject/Options/AppOptionsProvider.cs @@ -64,6 +64,7 @@ private void AddDefaults(IAppOptions appOptions) appOptions.IncludeTestAssembly = true; appOptions.ExcludeByFile = new[] { "**/Migrations/*" }; appOptions.Enabled = true; + appOptions.DisabledNoCoverage = true; appOptions.ShowCoverageInOverviewMargin = true; appOptions.ShowCoveredInOverviewMargin = true; appOptions.ShowPartiallyCoveredInOverviewMargin = true; @@ -200,6 +201,7 @@ internal class AppOptions : IAppOptions public string[] FunctionsExclude { get; set; } public bool Enabled { get; set; } + public bool DisabledNoCoverage { get; set; } public bool IncludeTestAssembly { get; set; } diff --git a/SharedProject/Options/IAppOptions.cs b/SharedProject/Options/IAppOptions.cs index bd548652..ba0121db 100644 --- a/SharedProject/Options/IAppOptions.cs +++ b/SharedProject/Options/IAppOptions.cs @@ -3,6 +3,7 @@ internal interface IFCCCommonOptions { bool Enabled { get; set; } + bool DisabledNoCoverage { get; set; } bool IncludeTestAssembly { get; set; } bool IncludeReferencedProjects { get; set; }