diff --git a/FineCodeCoverageTests/FCCEngine_Tests.cs b/FineCodeCoverageTests/FCCEngine_Tests.cs index 55cee15f..58385bb9 100644 --- a/FineCodeCoverageTests/FCCEngine_Tests.cs +++ b/FineCodeCoverageTests/FCCEngine_Tests.cs @@ -1,17 +1,16 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using AutoMoq; +using FineCodeCoverage.Core.Initialization; using FineCodeCoverage.Core.Utilities; using FineCodeCoverage.Engine; using FineCodeCoverage.Engine.Cobertura; using FineCodeCoverage.Engine.Model; using FineCodeCoverage.Engine.MsTestPlatform; using FineCodeCoverage.Engine.ReportGenerator; -using FineCodeCoverage.Impl; using FineCodeCoverage.Options; using FineCodeCoverage.Output; using Moq; @@ -50,7 +49,7 @@ public void Should_Initialize_AppFolder_Then_Utils() var openCoverMock = mocker.GetMock().Setup(openCover => openCover.Initialize(appDataFolderPath, disposalToken)).Callback(() => callOrder.Add(4)); - fccEngine.Initialize(null,disposalToken); + fccEngine.Initialize(disposalToken); Assert.AreEqual(4, callOrder.Count); Assert.AreEqual(1, callOrder[0]); @@ -63,7 +62,7 @@ public void Should_Set_AppDataFolderPath_From_Initialized_AppDataFolder_Director var appDataFolderPath = "some path"; var mockAppDataFolder = mocker.GetMock(); mockAppDataFolder.Setup(appDataFolder => appDataFolder.DirectoryPath).Returns(appDataFolderPath); - fccEngine.Initialize(null, CancellationToken.None); + fccEngine.Initialize(CancellationToken.None); Assert.AreEqual("some path", fccEngine.AppDataFolderPath); } @@ -102,46 +101,6 @@ public async Task Should_Log_Starting_When_Initialized() VerifyLogsReloadCoverageStatus(ReloadCoverageStatus.Start); } - [Test] - public async Task Should_Poll_For_Initialized() - { - var times = 5; - var initializeWait = 1000; - fccEngine.InitializeWait = initializeWait; - - var mockInitializeStatusProvider = new Mock(); - mockInitializeStatusProvider.SetupProperty(i => i.InitializeStatus); - var initializeStatusProvider = mockInitializeStatusProvider.Object; - - fccEngine.Initialize(initializeStatusProvider, CancellationToken.None); - - fccEngine.ReloadCoverage(() => Task.FromResult(new List())); - await Task.Delay(times * initializeWait).ContinueWith(_ => - { - initializeStatusProvider.InitializeStatus = InitializeStatus.Initialized; - }); - await fccEngine.reloadCoverageTask; - mocker.Verify(l => l.Log(fccEngine.GetLogReloadCoverageStatusMessage(ReloadCoverageStatus.Initializing)), Times.AtLeast(times)); - } - - [Test] - public async Task Should_Throw_With_initializationFailedMessagePrefix_When_Initialize_Has_Failed() - { - var mockInitializerStatusProvider = new Mock(); - mockInitializerStatusProvider.Setup(i => i.InitializeStatus).Returns(InitializeStatus.Error); - var initializeExceptionMessage = "An exception was thrown"; - mockInitializerStatusProvider.Setup(i => i.InitializeExceptionMessage).Returns(initializeExceptionMessage); - - fccEngine.Initialize(mockInitializerStatusProvider.Object, CancellationToken.None); - - fccEngine.ReloadCoverage(() => Task.FromResult(new List())); - - await fccEngine.reloadCoverageTask; - - mocker.Verify(l => l.Log(fccEngine.GetLogReloadCoverageStatusMessage(ReloadCoverageStatus.Error),It.Is(exc => (FCCEngine.initializationFailedMessagePrefix + Environment.NewLine + initializeExceptionMessage) == exc.Message))); - - } - [Test] public async Task Should_Prepare_For_Coverage_Suitable_CoverageProjects() { @@ -302,14 +261,6 @@ public async Task Should_Process_ReportGenerator_Output_If_Success_Raising_Event } - [Test] - public async Task Should_Log_Single_Exception_From_Aggregate_Exception() - { - Exception exception = null; - await ThrowException(exc => exception = exc); - mocker.Verify(l => l.Log(fccEngine.GetLogReloadCoverageStatusMessage(ReloadCoverageStatus.Error),exception)); - } - [Test] public async Task Should_Cancel_Running_Coverage_Logging_Cancelled_When_StopCoverage() { @@ -417,21 +368,6 @@ private async Task ThrowReadingReportHtml() } - private async Task ThrowException(Action exceptionCallback = null) - { - var exception = new Exception("an exception"); - exceptionCallback?.Invoke(exception); - Task> thrower() => Task.FromException>(exception); - - var mockInitializeStatusProvider = new Mock(); - mockInitializeStatusProvider.Setup(i => i.InitializeStatus).Returns(InitializeStatus.Initialized); - fccEngine.Initialize(mockInitializeStatusProvider.Object, CancellationToken.None); - - fccEngine.ReloadCoverage(thrower); - - await fccEngine.reloadCoverageTask; - } - private async Task StopCoverage() { var mockSuitableCoverageProject = new Mock(); @@ -461,9 +397,7 @@ private void SetUpSuccessfulRunReportGenerator() private async Task ReloadInitializedCoverage(params ICoverageProject[] coverageProjects) { var projectsFromTask = Task.FromResult(coverageProjects.ToList()); - var mockInitializeStatusProvider = new Mock(); - mockInitializeStatusProvider.Setup(i => i.InitializeStatus).Returns(InitializeStatus.Initialized); - fccEngine.Initialize(mockInitializeStatusProvider.Object, CancellationToken.None); + fccEngine.Initialize(CancellationToken.None); fccEngine.ReloadCoverage(() => projectsFromTask); await fccEngine.reloadCoverageTask; } diff --git a/FineCodeCoverageTests/FineCodeCoverageTests.csproj b/FineCodeCoverageTests/FineCodeCoverageTests.csproj index 95265a83..9707b01b 100644 --- a/FineCodeCoverageTests/FineCodeCoverageTests.csproj +++ b/FineCodeCoverageTests/FineCodeCoverageTests.csproj @@ -115,6 +115,7 @@ + @@ -130,6 +131,8 @@ + + diff --git a/FineCodeCoverageTests/FirstTimeToolWindowOpener_Tests.cs b/FineCodeCoverageTests/FirstTimeToolWindowOpener_Tests.cs new file mode 100644 index 00000000..920c28b0 --- /dev/null +++ b/FineCodeCoverageTests/FirstTimeToolWindowOpener_Tests.cs @@ -0,0 +1,42 @@ +using AutoMoq; +using FineCodeCoverage.Core.Initialization; +using FineCodeCoverage.Core.Utilities; +using Moq; +using NUnit.Framework; +using System.Threading; +using System.Threading.Tasks; + +namespace FineCodeCoverageTests +{ + internal class FirstTimeToolWindowOpener_Tests + { + private AutoMoqer mocker; + private FirstTimeToolWindowOpener firstTimeToolWindowOpener; + + [SetUp] + public void SetUp() { + mocker = new AutoMoqer(); + firstTimeToolWindowOpener = mocker.Create(); + } + + [TestCase(true,false,true)] + [TestCase(true, true, false)] + [TestCase(false, false, false)] + [TestCase(false, true, false)] + public async Task It_Should_Open_If_Have_Never_Shown_The_ToolWindow_And_InitializedFromTestContainerDiscoverer( + bool initializedFromTestContainerDiscoverer, + bool hasShownToolWindow, + bool expectedShown + ) + { + mocker.GetMock().Setup(x => x.InitializedFromTestContainerDiscoverer).Returns(initializedFromTestContainerDiscoverer); + mocker.GetMock().Setup(x => x.HasShownToolWindow).Returns(hasShownToolWindow); + + await firstTimeToolWindowOpener.OpenIfFirstTimeAsync(CancellationToken.None); + + var expectedTimes = expectedShown ? Times.Once() : Times.Never(); + mocker.Verify(toolWindowOpener => toolWindowOpener.OpenToolWindowAsync(), expectedTimes); + + } + } +} diff --git a/FineCodeCoverageTests/Initializer_Tests.cs b/FineCodeCoverageTests/Initializer_Tests.cs index 789bf7e6..ba4c9cb7 100644 --- a/FineCodeCoverageTests/Initializer_Tests.cs +++ b/FineCodeCoverageTests/Initializer_Tests.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; -using System.IO; using System.Threading; using System.Threading.Tasks; using AutoMoq; +using FineCodeCoverage.Core.Initialization; using FineCodeCoverage.Engine; using FineCodeCoverage.Engine.Model; -using FineCodeCoverage.Impl; using NUnit.Framework; namespace Test @@ -90,12 +89,12 @@ public async Task Should_Initialize_Dependencies_In_Order() { callOrder.Add(1); }); - mocker.GetMock().Setup(engine => engine.Initialize(initializer, disposalToken)).Callback(() => + mocker.GetMock().Setup(engine => engine.Initialize(disposalToken)).Callback(() => { callOrder.Add(2); }); - mocker.GetMock().Setup(p => p.InitializeAsync(disposalToken)).Callback(() => + mocker.GetMock().Setup(firstTimeToolWindowOpener => firstTimeToolWindowOpener.OpenIfFirstTimeAsync(disposalToken)).Callback(() => { callOrder.Add(3); }); @@ -103,14 +102,5 @@ public async Task Should_Initialize_Dependencies_In_Order() await initializer.InitializeAsync(disposalToken); Assert.AreEqual(new List { 1, 2, 3 }, callOrder); } - - [Test] - public async Task Should_Pass_Itself_To_FCCEngine_For_InitializeStatus() - { - var disposalToken = CancellationToken.None; - await initializer.InitializeAsync(disposalToken); - mocker.Verify(engine => engine.Initialize(initializer, disposalToken)); - } - } } \ No newline at end of file diff --git a/FineCodeCoverageTests/PackageLoader_Tests.cs b/FineCodeCoverageTests/PackageLoader_Tests.cs new file mode 100644 index 00000000..5f000aae --- /dev/null +++ b/FineCodeCoverageTests/PackageLoader_Tests.cs @@ -0,0 +1,44 @@ +using AutoMoq; +using FineCodeCoverage.Core.Initialization; +using NUnit.Framework; +using System.Threading; +using System.Threading.Tasks; + +namespace FineCodeCoverageTests +{ + internal class PackageLoader_Tests + { + private AutoMoqer mocker; + private PackageLoader packageLoader; + + [SetUp] + public void SetUp() + { + mocker = new AutoMoqer(); + packageLoader = mocker.Create(); + } + + + + [Test] + public void Should_Not_Be_InitializedFromTestContainerDiscoverer_If_LoadPackageAsync() + { + Assert.That(packageLoader.InitializedFromTestContainerDiscoverer, Is.False); + } + + [Test] + public async Task Should_Be_InitializedFromTestContainerDiscoverer_If_LoadPackageAsync() + { + await packageLoader.LoadPackageAsync(CancellationToken.None); + Assert.That(packageLoader.InitializedFromTestContainerDiscoverer, Is.True); + } + + [Test] + public async Task It_Should_Load_The_Package_If_LoadPackageAsync() + { + await packageLoader.LoadPackageAsync(CancellationToken.None); + + mocker.Verify(x => x.LoadPackageAsync()); + } + } +} diff --git a/FineCodeCoverageTests/ShownToolWindowHistory_Tests.cs b/FineCodeCoverageTests/ShownToolWindowHistory_Tests.cs new file mode 100644 index 00000000..33fde94b --- /dev/null +++ b/FineCodeCoverageTests/ShownToolWindowHistory_Tests.cs @@ -0,0 +1,65 @@ +using AutoMoq; +using FineCodeCoverage.Core.Utilities; +using FineCodeCoverage.Engine; +using Moq; +using NUnit.Framework; +using StructureMap.AutoMocking; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FineCodeCoverageTests +{ + internal class ShownToolWindowHistory_Tests + { + private AutoMoqer mocker; + private ShownToolWindowHistory shownToolWindowHistory; + private string markerFilePath; + + [SetUp] + public void SetUp() + { + mocker = new AutoMoqer(); + shownToolWindowHistory = mocker.Create(); + mocker.GetMock().Setup(fccEngine => fccEngine.AppDataFolderPath).Returns("AppDataFolderPath"); + markerFilePath = Path.Combine("AppDataFolderPath", "outputWindowInitialized"); + } + + [Test] + public void It_Should_Write_Marker_File_When_ShowedToolWindow_First_Time() + { + shownToolWindowHistory.ShowedToolWindow(); + mocker.Verify(f => f.WriteAllText(markerFilePath, string.Empty)); + shownToolWindowHistory.ShowedToolWindow(); + mocker.Verify(f => f.WriteAllText(markerFilePath, string.Empty),Times.Once()); + } + + [Test] + public void It_Should_HasShownToolWindow_Without_Searching_For_Marker_File_When_ShowedToolWindow_Is_Invoked() + { + shownToolWindowHistory.ShowedToolWindow(); + mocker.Verify(f => f.Exists(It.IsAny()), Times.Never()); + Assert.That(shownToolWindowHistory.HasShownToolWindow, Is.True); + } + + [TestCase(true)] + [TestCase(false)] + public void When_ShowedToolWindow_Has_Not_Been_Invoked_Should_Search_For_Marker_File_Once_When_HasShownToolWindow(bool fileExists) + { + mocker.GetMock().Setup(f => f.Exists(markerFilePath)).Returns(fileExists); + + void HasShownToolWindow() + { + var hasShownToolWindow = shownToolWindowHistory.HasShownToolWindow; + Assert.That(hasShownToolWindow, Is.EqualTo(fileExists)); + } + HasShownToolWindow(); + HasShownToolWindow(); + + mocker.Verify(f => f.Exists(markerFilePath), Times.Once()); + } + } +} diff --git a/FineCodeCoverageTests/TestContainerDiscovery_Tests.cs b/FineCodeCoverageTests/TestContainerDiscovery_Tests.cs index 4868125a..effaa881 100644 --- a/FineCodeCoverageTests/TestContainerDiscovery_Tests.cs +++ b/FineCodeCoverageTests/TestContainerDiscovery_Tests.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using AutoMoq; +using FineCodeCoverage.Core.Initialization; using FineCodeCoverage.Core.Utilities; using FineCodeCoverage.Engine; using FineCodeCoverage.Engine.Model; @@ -15,6 +16,53 @@ namespace Test { + + internal class TestOperationStateInvocationManager_Tests + { + private AutoMoqer mocker; + private TestOperationStateInvocationManager testOperationStateInvocationManager; + + [SetUp] + public void SetUp() + { + mocker = new AutoMoqer(); + testOperationStateInvocationManager = mocker.Create(); + } + + [Test] + public void Should_Return_True_When_Initialized_And_TestExecutionStarting() + { + mocker.GetMock().Setup(initializeStatusProvider => initializeStatusProvider.InitializeStatus).Returns(InitializeStatus.Initialized); + Assert.That(testOperationStateInvocationManager.CanInvoke(TestOperationStates.TestExecutionStarting), Is.True); + } + + [Test] + public void Should_Return_False_When_Not_Initialized_And_TestExecutionStarting() + { + mocker.GetMock().Setup(initializeStatusProvider => initializeStatusProvider.InitializeStatus).Returns(InitializeStatus.Initializing); + Assert.That(testOperationStateInvocationManager.CanInvoke(TestOperationStates.TestExecutionStarting), Is.False); + } + + [TestCase(true)] + [TestCase(false)] + public void Should_Return_True_For_All_Other_States_If_Was_Initialized_When_TestExecutionStarting(bool initializedWhenStarting) + { + var startingInitializeStatus = initializedWhenStarting ? InitializeStatus.Initialized : InitializeStatus.Initializing; + mocker.GetMock().Setup(initializeStatusProvider => initializeStatusProvider.InitializeStatus).Returns(startingInitializeStatus); + testOperationStateInvocationManager.CanInvoke(TestOperationStates.TestExecutionStarting); + Assert.That(testOperationStateInvocationManager.CanInvoke(TestOperationStates.TestExecutionCancelAndFinished), Is.EqualTo(initializedWhenStarting)); + } + + [TestCase(TestOperationStates.TestExecutionStarting)] + [TestCase(TestOperationStates.TestExecutionFinished)] + public void Should_Log_When_Cannot_Invoke(TestOperationStates testOperationState) + { + testOperationStateInvocationManager.CanInvoke(testOperationState); + mocker.Verify(logger => logger.Log($"Skipping {testOperationState} as FCC not initialized")); + } + + } + internal class TestContainerDiscovery_Tests { private AutoMoqer mocker; @@ -73,42 +121,19 @@ private void SetUpOptions(Action> setupAppOptions) public void SetUp() { mocker = new AutoMoqer(); - var mockDisposeAwareTaskRunner = mocker.GetMock(); - mockDisposeAwareTaskRunner.Setup(runner => runner.RunAsync(It.IsAny>())).Callback>(async taskProvider => await taskProvider()); testContainerDiscoverer = mocker.Create(); testContainerDiscoverer.RunAsync = (taskProvider) => { taskProvider().Wait(); }; - testContainerDiscoverer.initializeTask.Wait(); - } - - [Test] - public void It_Should_Initialize_As_Is_The_Entrance() - { - mocker.Verify(i => i.InitializeAsync(It.IsAny())); + var mockTestOperationStateInvocationManager = mocker.GetMock(); + mockTestOperationStateInvocationManager.Setup(testOperationStateInvocationManager => testOperationStateInvocationManager.CanInvoke(It.IsAny())).Returns(true); } [Test] - public async Task It_Should_Watch_For_Operation_State_Change_Before_Initialize() + public void It_Should_Load_The_Package() { - List order = new List(); - mocker = new AutoMoqer(); - var mockDisposeAwareTaskRunner = mocker.GetMock(); - mockDisposeAwareTaskRunner.Setup(runner => runner.RunAsync(It.IsAny>())).Callback>(async taskProvider => await taskProvider()); - var mockOperationState = mocker.GetMock(); - mockOperationState.SetupAdd(o => o.StateChanged += It.IsAny>()).Callback(() => - { - order.Add(1); - }); - var mockInitializer = mocker.GetMock(); - mockInitializer.Setup(i => i.InitializeAsync(It.IsAny())).Callback(() => - { - order.Add(2); - }); - var testContainerDiscoverer = mocker.Create(); - await testContainerDiscoverer.initializeTask; - Assert.AreEqual(new List { 1, 2 }, order); + mocker.Verify(packageLoader => packageLoader.LoadPackageAsync(It.IsAny())); } [Test] @@ -348,5 +373,23 @@ public void Should_Handle_Any_Exception_In_OperationState_Changed_Handler_Loggin RaiseTestExecutionCancelling(); mocker.Verify(logger => logger.Log("Error processing unit test events", exception)); } + + [TestCase(true)] + [TestCase(false)] + public void Should_Not_Handle_OperationState_Changes_When_The_testOperationStateInvocationManager_Cannot_Invoke(bool canInvoke) + { + var invoked = false; + testContainerDiscoverer.testOperationStateChangeHandlers = new Dictionary> + { + {TestOperationStates.TestExecutionCanceling, (_) => {invoked = true; return Task.CompletedTask; } } + }; + var mockTestOperationStateInvocationManager = mocker.GetMock(); + mockTestOperationStateInvocationManager.Setup(testOperationStateInvocationManager => testOperationStateInvocationManager.CanInvoke(It.IsAny())).Returns(canInvoke); + + RaiseTestExecutionCancelling(); + Assert.That(invoked, Is.EqualTo(canInvoke)); + + + } } } \ No newline at end of file diff --git a/SharedProject/Core/FCCEngine.cs b/SharedProject/Core/FCCEngine.cs index 039c717e..d706f99c 100644 --- a/SharedProject/Core/FCCEngine.cs +++ b/SharedProject/Core/FCCEngine.cs @@ -3,13 +3,13 @@ using System.ComponentModel.Composition; using System.Linq; using System.Threading; +using FineCodeCoverage.Core.Initialization; using FineCodeCoverage.Core.Utilities; using FineCodeCoverage.Engine.Cobertura; using FineCodeCoverage.Engine.Model; using FineCodeCoverage.Engine.MsTestPlatform; using FineCodeCoverage.Engine.MsTestPlatform.CodeCoverage; using FineCodeCoverage.Engine.ReportGenerator; -using FineCodeCoverage.Impl; using FineCodeCoverage.Options; using FineCodeCoverage.Output; @@ -60,7 +60,6 @@ internal class FCCEngine : IFCCEngine,IDisposable private readonly ILogger logger; private readonly IAppDataFolder appDataFolder; - private IInitializeStatusProvider initializeStatusProvider; private readonly ICoverageToolOutputManager coverageOutputManager; internal System.Threading.Tasks.Task reloadCoverageTask; #pragma warning disable IDE0052 // Remove unread private members @@ -116,10 +115,8 @@ private void LogReloadCoverageStatus(ReloadCoverageStatus reloadCoverageStatus) logger.Log(GetLogReloadCoverageStatusMessage(reloadCoverageStatus)); } - public void Initialize(IInitializeStatusProvider initializeStatusProvider, CancellationToken cancellationToken) + public void Initialize(CancellationToken cancellationToken) { - this.initializeStatusProvider = initializeStatusProvider; - appDataFolder.Initialize(cancellationToken); AppDataFolderPath = appDataFolder.DirectoryPath; @@ -323,29 +320,7 @@ private void RaiseReportFiles(ReportResult reportResult) this.eventAggregator.SendMessage(new ReportFilesMessage { CoberturaFile = reportResult.CoberturaFile, HotspotsFile = reportResult.HotspotsFile }); } } - - private async System.Threading.Tasks.Task PollInitializedStatusAsync(CancellationToken cancellationToken) - { - while (true) - { - cancellationToken.ThrowIfCancellationRequested(); - var InitializeStatus = initializeStatusProvider.InitializeStatus; - switch (InitializeStatus) - { - case InitializeStatus.Initialized: - return; - - case InitializeStatus.Initializing: - reportGeneratorUtil.LogCoverageProcess("Initializing"); - LogReloadCoverageStatus(ReloadCoverageStatus.Initializing); - await System.Threading.Tasks.Task.Delay(InitializeWait); - break; - case InitializeStatus.Error: - throw new Exception(initializationFailedMessagePrefix + Environment.NewLine + initializeStatusProvider.InitializeExceptionMessage); - } - } - } - + public void RunAndProcessReport(string[] coberturaFiles, Action cleanUp = null) { RunCancellableCoverageTask(async (vsShutdownLinkedCancellationToken) => @@ -369,7 +344,6 @@ private void RunCancellableCoverageTask( { reloadCoverageTask = System.Threading.Tasks.Task.Run(async () => { - await PollInitializedStatusAsync(vsShutdownLinkedCancellationToken); var result = await taskCreator(vsShutdownLinkedCancellationToken); return result; @@ -385,8 +359,6 @@ public void ReloadCoverage(Func>> coverageRequestCallback); void RunAndProcessReport(string[] coberturaFiles,Action cleanUp = null); diff --git a/SharedProject/Core/Initialization/FirstTimeToolWindowOpener.cs b/SharedProject/Core/Initialization/FirstTimeToolWindowOpener.cs new file mode 100644 index 00000000..d7bb6c3e --- /dev/null +++ b/SharedProject/Core/Initialization/FirstTimeToolWindowOpener.cs @@ -0,0 +1,39 @@ +using FineCodeCoverage.Core.Utilities; +using System.ComponentModel.Composition; +using System.Threading; +using System.Threading.Tasks; + +namespace FineCodeCoverage.Core.Initialization +{ + [Export(typeof(IFirstTimeToolWindowOpener))] + internal class FirstTimeToolWindowOpener : IFirstTimeToolWindowOpener + { + private readonly IInitializedFromTestContainerDiscoverer initializedFromTestContainerDiscoverer; + private readonly IShownToolWindowHistory shownToolWindowHistory; + private readonly IToolWindowOpener toolWindowOpener; + + [ImportingConstructor] + public FirstTimeToolWindowOpener( + IInitializedFromTestContainerDiscoverer initializedFromTestContainerDiscoverer, + IShownToolWindowHistory shownToolWindowHistory, + IToolWindowOpener toolWindowOpener + ) + { + this.initializedFromTestContainerDiscoverer = initializedFromTestContainerDiscoverer; + this.shownToolWindowHistory = shownToolWindowHistory; + this.toolWindowOpener = toolWindowOpener; + } + + public async Task OpenIfFirstTimeAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + if ( + initializedFromTestContainerDiscoverer.InitializedFromTestContainerDiscoverer && + !shownToolWindowHistory.HasShownToolWindow + ) + { + await toolWindowOpener.OpenToolWindowAsync(); + } + } + } +} diff --git a/SharedProject/Core/Initialization/IFirstTimeToolWindowOpener.cs b/SharedProject/Core/Initialization/IFirstTimeToolWindowOpener.cs new file mode 100644 index 00000000..bfef9922 --- /dev/null +++ b/SharedProject/Core/Initialization/IFirstTimeToolWindowOpener.cs @@ -0,0 +1,10 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace FineCodeCoverage.Core.Initialization +{ + internal interface IFirstTimeToolWindowOpener + { + Task OpenIfFirstTimeAsync(CancellationToken cancellationToken); + } +} diff --git a/SharedProject/Impl/TestContainerDiscovery/IInitializeStatusProvider.cs b/SharedProject/Core/Initialization/IInitializeStatusProvider.cs similarity index 78% rename from SharedProject/Impl/TestContainerDiscovery/IInitializeStatusProvider.cs rename to SharedProject/Core/Initialization/IInitializeStatusProvider.cs index 6cc153de..a6d80413 100644 --- a/SharedProject/Impl/TestContainerDiscovery/IInitializeStatusProvider.cs +++ b/SharedProject/Core/Initialization/IInitializeStatusProvider.cs @@ -1,4 +1,4 @@ -namespace FineCodeCoverage.Impl +namespace FineCodeCoverage.Core.Initialization { internal interface IInitializeStatusProvider { diff --git a/SharedProject/Core/Initialization/IInitializedFromTestContainerDiscoverer.cs b/SharedProject/Core/Initialization/IInitializedFromTestContainerDiscoverer.cs new file mode 100644 index 00000000..6241a56a --- /dev/null +++ b/SharedProject/Core/Initialization/IInitializedFromTestContainerDiscoverer.cs @@ -0,0 +1,7 @@ +namespace FineCodeCoverage.Core.Initialization +{ + internal interface IInitializedFromTestContainerDiscoverer + { + bool InitializedFromTestContainerDiscoverer { get; } + } +} diff --git a/SharedProject/Impl/TestContainerDiscovery/IInitializer.cs b/SharedProject/Core/Initialization/IInitializer.cs similarity index 81% rename from SharedProject/Impl/TestContainerDiscovery/IInitializer.cs rename to SharedProject/Core/Initialization/IInitializer.cs index 3ed5fcab..67e55b5e 100644 --- a/SharedProject/Impl/TestContainerDiscovery/IInitializer.cs +++ b/SharedProject/Core/Initialization/IInitializer.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; -namespace FineCodeCoverage.Impl +namespace FineCodeCoverage.Core.Initialization { internal interface IInitializer : IInitializeStatusProvider { diff --git a/SharedProject/Core/Initialization/IPackageLoader.cs b/SharedProject/Core/Initialization/IPackageLoader.cs new file mode 100644 index 00000000..d0c7e024 --- /dev/null +++ b/SharedProject/Core/Initialization/IPackageLoader.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace FineCodeCoverage.Core.Initialization +{ + internal interface IPackageLoader + { + Task LoadPackageAsync(System.Threading.CancellationToken cancellationToken); + } + +} + diff --git a/SharedProject/Impl/TestContainerDiscovery/InitializeStatus.cs b/SharedProject/Core/Initialization/InitializeStatus.cs similarity index 59% rename from SharedProject/Impl/TestContainerDiscovery/InitializeStatus.cs rename to SharedProject/Core/Initialization/InitializeStatus.cs index 5df01278..bce17166 100644 --- a/SharedProject/Impl/TestContainerDiscovery/InitializeStatus.cs +++ b/SharedProject/Core/Initialization/InitializeStatus.cs @@ -1,6 +1,5 @@ -namespace FineCodeCoverage.Impl +namespace FineCodeCoverage.Core.Initialization { internal enum InitializeStatus { Initializing, Initialized, Error }; -} - +} \ No newline at end of file diff --git a/SharedProject/Impl/TestContainerDiscovery/Initializer.cs b/SharedProject/Core/Initialization/Initializer.cs similarity index 80% rename from SharedProject/Impl/TestContainerDiscovery/Initializer.cs rename to SharedProject/Core/Initialization/Initializer.cs index 4a76010a..1dcbb5fd 100644 --- a/SharedProject/Impl/TestContainerDiscovery/Initializer.cs +++ b/SharedProject/Core/Initialization/Initializer.cs @@ -5,15 +5,16 @@ using FineCodeCoverage.Engine; using FineCodeCoverage.Engine.Model; -namespace FineCodeCoverage.Impl +namespace FineCodeCoverage.Core.Initialization { [Export(typeof(IInitializer))] + [Export(typeof(IInitializeStatusProvider))] internal class Initializer : IInitializer { private readonly IFCCEngine fccEngine; private readonly ILogger logger; private readonly ICoverageProjectFactory coverageProjectFactory; - private readonly IPackageInitializer packageInitializer; + private readonly IFirstTimeToolWindowOpener firstTimeToolWindowOpener; public InitializeStatus InitializeStatus { get; set; } = InitializeStatus.Initializing; public string InitializeExceptionMessage { get; set; } @@ -23,13 +24,13 @@ public Initializer( IFCCEngine fccEngine, ILogger logger, ICoverageProjectFactory coverageProjectFactory, - IPackageInitializer packageInitializer + IFirstTimeToolWindowOpener firstTimeToolWindowOpener ) { this.fccEngine = fccEngine; this.logger = logger; this.coverageProjectFactory = coverageProjectFactory; - this.packageInitializer = packageInitializer; + this.firstTimeToolWindowOpener = firstTimeToolWindowOpener; } public async Task InitializeAsync(CancellationToken cancellationToken) { @@ -41,11 +42,13 @@ public async Task InitializeAsync(CancellationToken cancellationToken) cancellationToken.ThrowIfCancellationRequested(); coverageProjectFactory.Initialize(); - fccEngine.Initialize(this, cancellationToken); - await packageInitializer.InitializeAsync(cancellationToken); + fccEngine.Initialize(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); logger.Log($"Initialized"); + + + await firstTimeToolWindowOpener.OpenIfFirstTimeAsync(cancellationToken); } catch (Exception exception) { diff --git a/SharedProject/Core/Initialization/PackageLoader.cs b/SharedProject/Core/Initialization/PackageLoader.cs new file mode 100644 index 00000000..e8a3ecc9 --- /dev/null +++ b/SharedProject/Core/Initialization/PackageLoader.cs @@ -0,0 +1,68 @@ +using System; +using System.ComponentModel.Composition; +using System.Threading; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Task = System.Threading.Tasks.Task; + +namespace FineCodeCoverage.Core.Initialization +{ + internal interface IShellPackageLoader + { + Task LoadPackageAsync(); + } + + [Export(typeof(IShellPackageLoader))] + internal class ShellPackageLoader : IShellPackageLoader + { + private IServiceProvider serviceProvider; + + [ImportingConstructor] + public ShellPackageLoader( + [Import(typeof(SVsServiceProvider))] + IServiceProvider serviceProvider + ) + { + this.serviceProvider = serviceProvider; + } + public async Task LoadPackageAsync() + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + if (serviceProvider.GetService(typeof(SVsShell)) is IVsShell shell) + { + var packageToBeLoadedGuid = PackageGuids.guidOutputToolWindowPackage; + shell.LoadPackage(ref packageToBeLoadedGuid, out var _); + } + } + + } + + [Export(typeof(IPackageLoader))] + [Export(typeof(IInitializedFromTestContainerDiscoverer))] + internal class PackageLoader : IPackageLoader, IInitializedFromTestContainerDiscoverer + { + private readonly IShellPackageLoader shellPackageLoader; + + public bool InitializedFromTestContainerDiscoverer { get; private set; } + + [ImportingConstructor] + public PackageLoader( + IShellPackageLoader shellPackageLoader + ) + { + this.shellPackageLoader = shellPackageLoader; + } + + public async Task LoadPackageAsync(CancellationToken cancellationToken) + { + InitializedFromTestContainerDiscoverer = true; + cancellationToken.ThrowIfCancellationRequested(); + await shellPackageLoader.LoadPackageAsync(); + + } + } +} + + + diff --git a/SharedProject/Core/Utilities/IShownToolWindowHistory.cs b/SharedProject/Core/Utilities/IShownToolWindowHistory.cs new file mode 100644 index 00000000..1ecc888f --- /dev/null +++ b/SharedProject/Core/Utilities/IShownToolWindowHistory.cs @@ -0,0 +1,8 @@ +namespace FineCodeCoverage.Core.Utilities +{ + interface IShownToolWindowHistory + { + bool HasShownToolWindow { get; } + void ShowedToolWindow(); + } +} diff --git a/SharedProject/Core/Utilities/IToolWindowOpener.cs b/SharedProject/Core/Utilities/IToolWindowOpener.cs new file mode 100644 index 00000000..a89e4f49 --- /dev/null +++ b/SharedProject/Core/Utilities/IToolWindowOpener.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace FineCodeCoverage.Core.Utilities +{ + internal interface IToolWindowOpener + { + Task OpenToolWindowAsync(); + } +} diff --git a/SharedProject/Core/Utilities/ShownToolWindowHistory.cs b/SharedProject/Core/Utilities/ShownToolWindowHistory.cs new file mode 100644 index 00000000..c943fc80 --- /dev/null +++ b/SharedProject/Core/Utilities/ShownToolWindowHistory.cs @@ -0,0 +1,44 @@ +using FineCodeCoverage.Engine; +using System.ComponentModel.Composition; +using System.IO; + +namespace FineCodeCoverage.Core.Utilities +{ + [Export(typeof(IShownToolWindowHistory))] + internal class ShownToolWindowHistory : IShownToolWindowHistory + { + private readonly IFCCEngine fccEngine; + private readonly IFileUtil fileUtil; + private bool hasShownToolWindow; + private bool checkedFileExists; + + [ImportingConstructor] + public ShownToolWindowHistory(IFCCEngine fccEngine, IFileUtil fileUtil) + { + this.fccEngine = fccEngine; + this.fileUtil = fileUtil; + } + private string shownToolWindowFilePath => Path.Combine(fccEngine.AppDataFolderPath, "outputWindowInitialized"); + public bool HasShownToolWindow + { + get + { + if (!hasShownToolWindow && !checkedFileExists) + { + hasShownToolWindow = fileUtil.Exists(shownToolWindowFilePath); + checkedFileExists = true; + } + return hasShownToolWindow; + } + } + + public void ShowedToolWindow() + { + if (!hasShownToolWindow) + { + hasShownToolWindow = true; + fileUtil.WriteAllText(shownToolWindowFilePath, string.Empty); + } + } + } +} diff --git a/SharedProject/Core/Utilities/ToolWindowOpener.cs b/SharedProject/Core/Utilities/ToolWindowOpener.cs new file mode 100644 index 00000000..ea4e127c --- /dev/null +++ b/SharedProject/Core/Utilities/ToolWindowOpener.cs @@ -0,0 +1,15 @@ +using FineCodeCoverage.Output; +using System.ComponentModel.Composition; +using System.Threading.Tasks; + +namespace FineCodeCoverage.Core.Utilities +{ + [Export(typeof(IToolWindowOpener))] + internal class ToolWindowOpener : IToolWindowOpener + { + public async Task OpenToolWindowAsync() + { + await OutputToolWindowCommand.Instance.ShowToolWindowAsync(); + } + } +} diff --git a/SharedProject/Impl/TestContainerDiscovery/IPackageInitializer.cs b/SharedProject/Impl/TestContainerDiscovery/IPackageInitializer.cs deleted file mode 100644 index 68c81b3c..00000000 --- a/SharedProject/Impl/TestContainerDiscovery/IPackageInitializer.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; - -namespace FineCodeCoverage.Impl -{ - internal interface IPackageInitializer - { - Task InitializeAsync(System.Threading.CancellationToken cancellationToken); - } - -} - diff --git a/SharedProject/Impl/TestContainerDiscovery/ITestOperationStateInvocationManager.cs b/SharedProject/Impl/TestContainerDiscovery/ITestOperationStateInvocationManager.cs new file mode 100644 index 00000000..57366ed6 --- /dev/null +++ b/SharedProject/Impl/TestContainerDiscovery/ITestOperationStateInvocationManager.cs @@ -0,0 +1,9 @@ +using Microsoft.VisualStudio.TestWindow.Extensibility; + +namespace FineCodeCoverage.Impl +{ + internal interface ITestOperationStateInvocationManager + { + bool CanInvoke(TestOperationStates testOperationState); + } +} diff --git a/SharedProject/Impl/TestContainerDiscovery/PackageInitializer.cs b/SharedProject/Impl/TestContainerDiscovery/PackageInitializer.cs deleted file mode 100644 index 22b40fe1..00000000 --- a/SharedProject/Impl/TestContainerDiscovery/PackageInitializer.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.ComponentModel.Composition; -using System.IO; -using System.Threading; -using FineCodeCoverage.Engine; -using FineCodeCoverage.Output; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; -using Task = System.Threading.Tasks.Task; - -namespace FineCodeCoverage.Impl -{ - [Export(typeof(IPackageInitializer))] - internal class PackageInitializer : IPackageInitializer - { - private readonly IFCCEngine fccEngine; - private readonly IServiceProvider serviceProvider; - - [ImportingConstructor] - public PackageInitializer( - IFCCEngine fccEngine, - [Import(typeof(SVsServiceProvider))] - IServiceProvider serviceProvider) - { - this.fccEngine = fccEngine; - this.serviceProvider = serviceProvider; - } - - public async Task InitializeAsync(CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - - if (serviceProvider.GetService(typeof(SVsShell)) is IVsShell shell) - { - var packageToBeLoadedGuid = PackageGuids.guidOutputToolWindowPackage; - shell.LoadPackage(ref packageToBeLoadedGuid, out var _); - - var outputWindowInitializedFile = Path.Combine(fccEngine.AppDataFolderPath, "outputWindowInitialized"); - - if (File.Exists(outputWindowInitializedFile)) - { - await OutputToolWindowCommand.Instance.FindToolWindowAsync(); - } - else - { - // for first time users, the window is automatically docked - await OutputToolWindowCommand.Instance.ShowToolWindowAsync(); - File.WriteAllText(outputWindowInitializedFile, string.Empty); - } - } - - } - } - -} - diff --git a/SharedProject/Impl/TestContainerDiscovery/TestContainerDiscoverer.cs b/SharedProject/Impl/TestContainerDiscovery/TestContainerDiscoverer.cs index 829dd567..dd5c584c 100644 --- a/SharedProject/Impl/TestContainerDiscovery/TestContainerDiscoverer.cs +++ b/SharedProject/Impl/TestContainerDiscovery/TestContainerDiscoverer.cs @@ -3,7 +3,6 @@ using System.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; -using FineCodeCoverage.Core.Utilities; using FineCodeCoverage.Engine; using FineCodeCoverage.Engine.ReportGenerator; using FineCodeCoverage.Options; @@ -12,7 +11,8 @@ using Task = System.Threading.Tasks.Task; using Microsoft.VisualStudio.Utilities; using FineCodeCoverage.Engine.MsTestPlatform.CodeCoverage; -using System.Diagnostics; +using System.Threading; +using FineCodeCoverage.Core.Initialization; namespace FineCodeCoverage.Impl { @@ -26,16 +26,18 @@ internal class TestContainerDiscoverer : ITestContainerDiscoverer public event EventHandler TestContainersUpdated; #pragma warning restore 67 private readonly IFCCEngine fccEngine; + private readonly ITestOperationStateInvocationManager testOperationStateInvocationManager; private readonly ITestOperationFactory testOperationFactory; private readonly ILogger logger; private readonly IAppOptionsProvider appOptionsProvider; private readonly IReportGeneratorUtil reportGeneratorUtil; private readonly IMsCodeCoverageRunSettingsService msCodeCoverageRunSettingsService; - private readonly Dictionary> testOperationStateChangeHandlers; + internal Dictionary> testOperationStateChangeHandlers; private bool cancelling; private MsCodeCoverageCollectionStatus msCodeCoverageCollectionStatus; private bool runningInParallel; private IAppOptions settings; + internal Task initializeTask; [ExcludeFromCodeCoverage] @@ -51,20 +53,20 @@ public TestContainerDiscoverer IOperationState operationState, IFCCEngine fccEngine, - IInitializer initializer, + ITestOperationStateInvocationManager testOperationStateInvocationManager, + IPackageLoader packageLoader, ITestOperationFactory testOperationFactory, ILogger logger, IAppOptionsProvider appOptionsProvider, IReportGeneratorUtil reportGeneratorUtil, - IDisposeAwareTaskRunner disposeAwareTaskRunner, IMsCodeCoverageRunSettingsService msCodeCoverageRunSettingsService - ) { this.appOptionsProvider = appOptionsProvider; this.reportGeneratorUtil = reportGeneratorUtil; this.msCodeCoverageRunSettingsService = msCodeCoverageRunSettingsService; this.fccEngine = fccEngine; + this.testOperationStateInvocationManager = testOperationStateInvocationManager; this.testOperationFactory = testOperationFactory; this.logger = logger; testOperationStateChangeHandlers = new Dictionary> @@ -74,16 +76,8 @@ IMsCodeCoverageRunSettingsService msCodeCoverageRunSettingsService { TestOperationStates.TestExecutionFinished, TestExecutionFinishedAsync}, { TestOperationStates.TestExecutionCancelAndFinished, TestExecutionCancelAndFinishedAsync}, }; - - disposeAwareTaskRunner.RunAsync(() => - { - initializeTask = Task.Run(async () => - { - operationState.StateChanged += OperationState_StateChanged; - await initializer.InitializeAsync(disposeAwareTaskRunner.DisposalToken); - }); - return initializeTask; - }); + _ = packageLoader.LoadPackageAsync(CancellationToken.None); + operationState.StateChanged += OperationState_StateChanged; } internal Action> RunAsync = (taskProvider) => @@ -128,7 +122,7 @@ private async Task TestExecutionStartingAsync(IOperation operation) } } } - + private void CombinedLog(string message) { reportGeneratorUtil.LogCoverageProcess(message); @@ -138,10 +132,10 @@ private void CombinedLog(string message) private async Task TestExecutionFinishedAsync(IOperation operation) { var (should, testOperation) = ShouldConditionallyCollectWhenTestExecutionFinished(operation); - if (should) { + if (should) + { await TestExecutionFinishedCollectionAsync(operation, testOperation); } - } private (bool should, ITestOperation testOperation) ShouldConditionallyCollectWhenTestExecutionFinished(IOperation operation) @@ -254,7 +248,10 @@ private async Task TestExecutionCancelAndFinishedAsync(IOperation operation) private async Task OperationState_StateChangedAsync(OperationStateChangedEventArgs e) { if (testOperationStateChangeHandlers.TryGetValue(e.State, out var handler)) { - await handler(e.Operation); + if (testOperationStateInvocationManager.CanInvoke(e.State)) + { + await handler(e.Operation); + } } } diff --git a/SharedProject/Impl/TestContainerDiscovery/TestOperationStateInvocationManager.cs b/SharedProject/Impl/TestContainerDiscovery/TestOperationStateInvocationManager.cs new file mode 100644 index 00000000..9b0cd802 --- /dev/null +++ b/SharedProject/Impl/TestContainerDiscovery/TestOperationStateInvocationManager.cs @@ -0,0 +1,36 @@ +using FineCodeCoverage.Core.Initialization; +using Microsoft.VisualStudio.TestWindow.Extensibility; +using System.ComponentModel.Composition; + +namespace FineCodeCoverage.Impl +{ + [Export(typeof(ITestOperationStateInvocationManager))] + internal class TestOperationStateInvocationManager : ITestOperationStateInvocationManager + { + private readonly IInitializeStatusProvider initializeStatusProvider; + private readonly ILogger logger; + private bool initializedWhenTestExecutionStarting; + + [ImportingConstructor] + public TestOperationStateInvocationManager( + IInitializeStatusProvider initializeStatusProvider, + ILogger logger + ) + { + this.initializeStatusProvider = initializeStatusProvider; + this.logger = logger; + } + public bool CanInvoke(TestOperationStates testOperationState) + { + if (testOperationState == TestOperationStates.TestExecutionStarting) + { + initializedWhenTestExecutionStarting = initializeStatusProvider.InitializeStatus == InitializeStatus.Initialized; + } + if (!initializedWhenTestExecutionStarting) + { + logger.Log($"Skipping {testOperationState} as FCC not initialized"); + } + return initializedWhenTestExecutionStarting; + } + } +} diff --git a/SharedProject/Output/OutputToolWindowCommand.cs b/SharedProject/Output/OutputToolWindowCommand.cs index 59ecb1da..76d016b6 100644 --- a/SharedProject/Output/OutputToolWindowCommand.cs +++ b/SharedProject/Output/OutputToolWindowCommand.cs @@ -3,6 +3,7 @@ using Microsoft.VisualStudio.Shell; using System.Threading.Tasks; using Task = System.Threading.Tasks.Task; +using FineCodeCoverage.Core.Utilities; namespace FineCodeCoverage.Output { @@ -27,17 +28,19 @@ internal sealed class OutputToolWindowCommand private readonly AsyncPackage package; private readonly ILogger logger; - - /// - /// Initializes a new instance of the class. - /// Adds our command handlers for menu (commands must exist in the command table file) - /// - /// Owner package, not null. - /// Command service to add command to, not null. - private OutputToolWindowCommand(AsyncPackage package, OleMenuCommandService commandService, ILogger logger) + private readonly IShownToolWindowHistory shownToolWindowHistory; + + /// + /// Initializes a new instance of the class. + /// Adds our command handlers for menu (commands must exist in the command table file) + /// + /// Owner package, not null. + /// Command service to add command to, not null. + private OutputToolWindowCommand(AsyncPackage package, OleMenuCommandService commandService, ILogger logger, IShownToolWindowHistory shownToolWindowHistory) { this.logger = logger; - this.package = package ?? throw new ArgumentNullException(nameof(package)); + this.shownToolWindowHistory = shownToolWindowHistory; + this.package = package ?? throw new ArgumentNullException(nameof(package)); commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); var menuCommandID = new CommandID(CommandSet, CommandId); @@ -69,14 +72,14 @@ public IAsyncServiceProvider ServiceProvider /// Initializes the singleton instance of the command. /// /// Owner package, not null. - public static async Task InitializeAsync(AsyncPackage package, ILogger logger) + public static async Task InitializeAsync(AsyncPackage package, ILogger logger, IShownToolWindowHistory shownToolWindowHistory) { // Switch to the main thread - the call to AddCommand in OutputToolWindowCommand's constructor requires // the UI thread. await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); OleMenuCommandService commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as OleMenuCommandService; - Instance = new OutputToolWindowCommand(package, commandService, logger); + Instance = new OutputToolWindowCommand(package, commandService, logger, shownToolWindowHistory); } /// @@ -102,6 +105,7 @@ public void Execute(object sender, EventArgs e) public async Task ShowToolWindowAsync() { + shownToolWindowHistory.ShowedToolWindow(); ToolWindowPane window = await package.ShowToolWindowAsync(typeof(OutputToolWindow), 0, true, package.DisposalToken); return ReturnOrThrowIfCannotCreateToolWindow(window); diff --git a/SharedProject/Output/OutputToolWindowPackage.cs b/SharedProject/Output/OutputToolWindowPackage.cs index 1f7c91e1..3c1e7b61 100644 --- a/SharedProject/Output/OutputToolWindowPackage.cs +++ b/SharedProject/Output/OutputToolWindowPackage.cs @@ -11,6 +11,7 @@ using FineCodeCoverage.Engine; using EnvDTE80; using FineCodeCoverage.Core.Utilities; +using FineCodeCoverage.Core.Initialization; namespace FineCodeCoverage.Output { @@ -91,7 +92,12 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke await OpenCoberturaCommand.InitializeAsync(this, eventAggregator); await OpenHotspotsCommand.InitializeAsync(this, eventAggregator); await ClearUICommand.InitializeAsync(this, fccEngine); - await OutputToolWindowCommand.InitializeAsync(this, componentModel.GetService()); + await OutputToolWindowCommand.InitializeAsync( + this, + componentModel.GetService(), + componentModel.GetService() + ); + await componentModel.GetService().InitializeAsync(cancellationToken); } protected override Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) diff --git a/SharedProject/SharedProject.projitems b/SharedProject/SharedProject.projitems index 14269039..bb2f62c7 100644 --- a/SharedProject/SharedProject.projitems +++ b/SharedProject/SharedProject.projitems @@ -68,6 +68,9 @@ + + + @@ -151,6 +154,7 @@ + @@ -160,6 +164,7 @@ + @@ -199,15 +204,17 @@ - - - - - + + + + + + + - + @@ -247,6 +254,8 @@ + +