diff --git a/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj b/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj index 4f405ea959..5d6749adda 100644 --- a/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj +++ b/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj @@ -102,6 +102,7 @@ + @@ -245,6 +246,7 @@ + diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/AddPackageReferenceViewModel.cs b/src/AddIns/Misc/PackageManagement/Project/Src/AddPackageReferenceViewModel.cs index c7f482a7fd..cba4cce219 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/AddPackageReferenceViewModel.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/AddPackageReferenceViewModel.cs @@ -13,7 +13,7 @@ namespace ICSharpCode.PackageManagement public class AddPackageReferenceViewModel : ViewModelBase, IDisposable { IPackageManagementSolution solution; - IPackageManagementEvents packageManagementEvents; + IThreadSafePackageManagementEvents packageManagementEvents; ILicenseAcceptanceService licenseAcceptanceService; string message; bool hasError; @@ -21,7 +21,7 @@ namespace ICSharpCode.PackageManagement public AddPackageReferenceViewModel( IPackageManagementSolution solution, IRegisteredPackageRepositories registeredPackageRepositories, - IPackageManagementEvents packageManagementEvents, + IThreadSafePackageManagementEvents packageManagementEvents, IPackageActionRunner actionRunner, ILicenseAcceptanceService licenseAcceptanceService, ITaskFactory taskFactory) @@ -62,6 +62,7 @@ namespace ICSharpCode.PackageManagement packageManagementEvents.AcceptLicenses -= AcceptLicenses; packageManagementEvents.PackageOperationError -= PackageOperationError; packageManagementEvents.PackageOperationsStarting -= PackageOperationsStarting; + packageManagementEvents.Dispose(); } void PackageOperationError(object sender, PackageOperationExceptionEventArgs e) diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementWorkbench.cs b/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementWorkbench.cs index ddf64f8340..c1d076d78e 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementWorkbench.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementWorkbench.cs @@ -7,6 +7,9 @@ namespace ICSharpCode.PackageManagement { public interface IPackageManagementWorkbench { + bool InvokeRequired { get; } + + void SafeThreadAsyncCall(Action method, A arg1, B arg2); void CreateConsolePad(); } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/IThreadSafePackageManagementEvents.cs b/src/AddIns/Misc/PackageManagement/Project/Src/IThreadSafePackageManagementEvents.cs new file mode 100644 index 0000000000..bc0b659f8a --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Project/Src/IThreadSafePackageManagementEvents.cs @@ -0,0 +1,11 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.PackageManagement +{ + public interface IThreadSafePackageManagementEvents : IPackageManagementEvents, IDisposable + { + } +} diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementViewModels.cs b/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementViewModels.cs index dc10d8d48e..f8d061f013 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementViewModels.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementViewModels.cs @@ -27,11 +27,12 @@ namespace ICSharpCode.PackageManagement { CreateRegisteredPackageRepositories(); CreateSolution(); + var packageManagementEvents = new ThreadSafePackageManagementEvents(PackageManagementServices.PackageManagementEvents); addPackageReferenceViewModel = new AddPackageReferenceViewModel( solution, registeredPackageRepositories, - PackageManagementServices.PackageManagementEvents, + packageManagementEvents, PackageManagementServices.PackageActionRunner, new LicenseAcceptanceService(), new PackageManagementTaskFactory()); diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementWorkbench.cs b/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementWorkbench.cs index 74d079666f..742ec7f60e 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementWorkbench.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementWorkbench.cs @@ -22,5 +22,14 @@ namespace ICSharpCode.PackageManagement // Force creation of view model. object control = pad.PadContent.Control; } + + public bool InvokeRequired { + get { return WorkbenchSingleton.InvokeRequired; } + } + + public void SafeThreadAsyncCall(Action method, A arg1, B arg2) + { + WorkbenchSingleton.SafeThreadAsyncCall(method, arg1, arg2); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/ThreadSafePackageManagementEvents.cs b/src/AddIns/Misc/PackageManagement/Project/Src/ThreadSafePackageManagementEvents.cs new file mode 100644 index 0000000000..2cdad21cb4 --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Project/Src/ThreadSafePackageManagementEvents.cs @@ -0,0 +1,178 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using NuGet; + +namespace ICSharpCode.PackageManagement +{ + public class ThreadSafePackageManagementEvents : IThreadSafePackageManagementEvents + { + IPackageManagementEvents unsafeEvents; + IPackageManagementWorkbench workbench; + + public ThreadSafePackageManagementEvents(IPackageManagementEvents unsafeEvents) + : this(unsafeEvents, new PackageManagementWorkbench()) + { + } + + public ThreadSafePackageManagementEvents( + IPackageManagementEvents unsafeEvents, + IPackageManagementWorkbench workbench) + { + this.unsafeEvents = unsafeEvents; + this.workbench = workbench; + + RegisterEventHandlers(); + } + + void RegisterEventHandlers() + { + unsafeEvents.PackageOperationsStarting += RaisePackageOperationStartingEventIfHasSubscribers; + unsafeEvents.PackageOperationError += RaisePackageOperationErrorEventIfHasSubscribers; + unsafeEvents.ParentPackageInstalled += RaiseParentPackageInstalledEventIfHasSubscribers; + unsafeEvents.ParentPackageUninstalled += RaiseParentPackageUninstalledEventIfHasSubscribers; + } + + public void Dispose() + { + UnregisterEventHandlers(); + } + + void UnregisterEventHandlers() + { + unsafeEvents.PackageOperationsStarting -= RaisePackageOperationStartingEventIfHasSubscribers; + unsafeEvents.PackageOperationError -= RaisePackageOperationErrorEventIfHasSubscribers; + unsafeEvents.ParentPackageInstalled -= RaiseParentPackageInstalledEventIfHasSubscribers; + unsafeEvents.ParentPackageUninstalled -= RaiseParentPackageUninstalledEventIfHasSubscribers; + } + + void RaisePackageOperationStartingEventIfHasSubscribers(object sender, EventArgs e) + { + if (PackageOperationsStarting != null) { + RaisePackageOperationStartingEvent(sender, e); + } + } + + void RaisePackageOperationStartingEvent(object sender, EventArgs e) + { + if (InvokeRequired) { + Action action = RaisePackageOperationStartingEvent; + SafeThreadAsyncCall(action, sender, e); + } else { + PackageOperationsStarting(sender, e); + } + } + + bool InvokeRequired { + get { return workbench.InvokeRequired; } + } + + void SafeThreadAsyncCall(Action method, A arg1, B arg2) + { + workbench.SafeThreadAsyncCall(method, arg1, arg2); + } + + public event EventHandler PackageOperationsStarting; + + void RaisePackageOperationErrorEventIfHasSubscribers(object sender, PackageOperationExceptionEventArgs e) + { + if (PackageOperationError != null) { + RaisePackageOperationErrorEvent(sender, e); + } + } + + void RaisePackageOperationErrorEvent(object sender, PackageOperationExceptionEventArgs e) + { + if (PackageOperationError != null) { + if (InvokeRequired) { + Action action = RaisePackageOperationErrorEvent; + SafeThreadAsyncCall(action, sender, e); + } else { + PackageOperationError(sender, e); + } + } + } + + public event EventHandler PackageOperationError; + + void RaiseParentPackageInstalledEventIfHasSubscribers(object sender, ParentPackageOperationEventArgs e) + { + if (ParentPackageInstalled != null) { + RaiseParentPackageInstalledEvent(sender, e); + } + } + + void RaiseParentPackageInstalledEvent(object sender, ParentPackageOperationEventArgs e) + { + if (InvokeRequired) { + Action action = RaiseParentPackageInstalledEvent; + SafeThreadAsyncCall(action, sender, e); + } else { + ParentPackageInstalled(sender, e); + } + } + + public event EventHandler ParentPackageInstalled; + + void RaiseParentPackageUninstalledEventIfHasSubscribers(object sender, ParentPackageOperationEventArgs e) + { + if (ParentPackageUninstalled != null) { + RaiseParentPackageUninstalledEvent(sender, e); + } + } + + void RaiseParentPackageUninstalledEvent(object sender, ParentPackageOperationEventArgs e) + { + if (InvokeRequired) { + Action action = RaiseParentPackageUninstalledEvent; + SafeThreadAsyncCall(action, sender, e); + } else { + ParentPackageUninstalled(sender, e); + } + } + + public event EventHandler ParentPackageUninstalled; + + public event EventHandler AcceptLicenses { + add { unsafeEvents.AcceptLicenses += value; } + remove { unsafeEvents.AcceptLicenses -= value; } + } + + public event EventHandler PackageOperationMessageLogged { + add { unsafeEvents.PackageOperationMessageLogged += value; } + remove { unsafeEvents.PackageOperationMessageLogged -= value; } + } + + public void OnPackageOperationsStarting() + { + unsafeEvents.OnPackageOperationsStarting(); + } + + public void OnPackageOperationError(Exception ex) + { + unsafeEvents.OnPackageOperationError(ex); + } + + public bool OnAcceptLicenses(IEnumerable packages) + { + return unsafeEvents.OnAcceptLicenses(packages); + } + + public void OnParentPackageInstalled(IPackage package) + { + unsafeEvents.OnParentPackageInstalled(package); + } + + public void OnParentPackageUninstalled(IPackage package) + { + unsafeEvents.OnParentPackageUninstalled(package); + } + + public void OnPackageOperationMessageLogged(MessageLevel level, string message, params object[] args) + { + unsafeEvents.OnPackageOperationMessageLogged(level, message, args); + } + } +} diff --git a/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj b/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj index 7c99688258..54d4397f24 100644 --- a/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj +++ b/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj @@ -199,6 +199,7 @@ + diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/AddPackageReferenceViewModelTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/AddPackageReferenceViewModelTests.cs index b1cdfad276..639ac230cd 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/AddPackageReferenceViewModelTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/AddPackageReferenceViewModelTests.cs @@ -23,6 +23,7 @@ namespace PackageManagement.Tests FakeTaskFactory taskFactory; List packagesPassedToOnAcceptLicenses; FakePackageActionRunner fakeActionRunner; + FakePackageManagementEvents fakeThreadSafeEvents; void CreateSolution() { @@ -38,8 +39,14 @@ namespace PackageManagement.Tests void CreateViewModel(FakePackageManagementSolution solution) { - taskFactory = new FakeTaskFactory(); packageManagementEvents = new PackageManagementEvents(); + var threadSafeEvents = new ThreadSafePackageManagementEvents(packageManagementEvents, new FakePackageManagementWorkbench()); + CreateViewModel(fakeSolution, threadSafeEvents); + } + + void CreateViewModel(FakePackageManagementSolution solution, IThreadSafePackageManagementEvents packageManagementEvents) + { + taskFactory = new FakeTaskFactory(); fakeLicenseAcceptanceSevice = new FakeLicenseAcceptanceService(); fakeActionRunner = new FakePackageActionRunner(); viewModel = new AddPackageReferenceViewModel( @@ -52,6 +59,13 @@ namespace PackageManagement.Tests taskFactory.ExecuteAllFakeTasks(); } + void CreateViewModelWithFakeThreadSafePackageManagementEvents() + { + CreateSolution(); + fakeThreadSafeEvents = new FakePackageManagementEvents(); + CreateViewModel(fakeSolution, fakeThreadSafeEvents); + } + List RecordViewModelPropertiesChanged() { var propertyNamesChanged = new List(); @@ -345,5 +359,16 @@ namespace PackageManagement.Tests Assert.AreEqual("Test", viewModel.Message); } + + [Test] + public void Dispose_MethodCalled_DisposesThreadSafePackageManagementEvents() + { + CreateViewModelWithFakeThreadSafePackageManagementEvents(); + viewModel.Dispose(); + + bool disposed = fakeThreadSafeEvents.IsDisposed; + + Assert.IsTrue(disposed); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakePackageManagementEvents.cs b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakePackageManagementEvents.cs index b0244949aa..0204e4bde6 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakePackageManagementEvents.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakePackageManagementEvents.cs @@ -8,7 +8,7 @@ using NuGet; namespace PackageManagement.Tests.Helpers { - public class FakePackageManagementEvents : IPackageManagementEvents + public class FakePackageManagementEvents : IThreadSafePackageManagementEvents { #pragma warning disable 0067 public event EventHandler PackageOperationsStarting; @@ -66,5 +66,12 @@ namespace PackageManagement.Tests.Helpers MessageLevelPassedToOnPackageOperationMessageLogged = level; FormattedStringPassedToOnPackageOperationMessageLogged = String.Format(message, args); } + + public bool IsDisposed; + + public void Dispose() + { + IsDisposed = true; + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakePackageManagementWorkbench.cs b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakePackageManagementWorkbench.cs index 9f1e95cea1..2ccd407883 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakePackageManagementWorkbench.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakePackageManagementWorkbench.cs @@ -14,5 +14,22 @@ namespace PackageManagement.Tests.Helpers { IsCreateConsolePadCalled = true; } + + public bool InvokeRequiredReturnValue; + + public bool InvokeRequired { + get { return InvokeRequiredReturnValue; } + } + + public bool IsSafeThreadAsyncCallMade; + public object Arg1PassedToSafeThreadAsyncCall; + public object Arg2PassedToSafeThreadAsyncCall; + + public void SafeThreadAsyncCall(Action method, A arg1, B arg2) + { + IsSafeThreadAsyncCallMade = true; + Arg1PassedToSafeThreadAsyncCall = arg1; + Arg2PassedToSafeThreadAsyncCall = arg2; + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/ThreadSafePackageManagementEventsTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/ThreadSafePackageManagementEventsTests.cs new file mode 100644 index 0000000000..8411668dea --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Test/Src/ThreadSafePackageManagementEventsTests.cs @@ -0,0 +1,408 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using ICSharpCode.PackageManagement; +using ICSharpCode.PackageManagement.Design; +using NuGet; +using NUnit.Framework; +using PackageManagement.Tests.Helpers; + +namespace PackageManagement.Tests +{ + [TestFixture] + public class ThreadSafePackageManagementEventsTests + { + ThreadSafePackageManagementEvents threadSafeEvents; + FakePackageManagementEvents fakeEvents; + FakePackageManagementWorkbench fakeWorkbench; + PackageManagementEvents unsafeEvents; + bool eventHandlerFired; + + void CreateEvents() + { + fakeEvents = new FakePackageManagementEvents(); + fakeWorkbench = new FakePackageManagementWorkbench(); + threadSafeEvents = new ThreadSafePackageManagementEvents(fakeEvents, fakeWorkbench); + } + + void CreateEventsWithRealPackageManagementEvents() + { + unsafeEvents = new PackageManagementEvents(); + fakeWorkbench = new FakePackageManagementWorkbench(); + threadSafeEvents = new ThreadSafePackageManagementEvents(unsafeEvents, fakeWorkbench); + } + + void OnEventHandlerFired(object sender, EventArgs e) + { + eventHandlerFired = true; + } + + [Test] + public void OnPackageOperationsStarting_NoInvokeRequired_NonThreadSafePackageOperationsStartingMethodCalled() + { + CreateEvents(); + threadSafeEvents.OnPackageOperationsStarting(); + + Assert.IsTrue(fakeEvents.IsOnPackageOperationsStartingCalled); + } + + [Test] + public void OnPackageOperationError_NoInvokeRequired_NonThreadSafeOnPackageOperationErrorMethodCalled() + { + CreateEvents(); + var expectedException = new Exception("test"); + threadSafeEvents.OnPackageOperationError(expectedException); + + Exception exception = fakeEvents.ExceptionPassedToOnPackageOperationError; + + Assert.AreEqual(expectedException, exception); + } + + [Test] + public void OnAcceptLicenses_NoInvokeRequired_NonThreadSafeOnAcceptLicensesMethodCalled() + { + CreateEvents(); + var expectedPackages = new List(); + bool result = threadSafeEvents.OnAcceptLicenses(expectedPackages); + + IEnumerable packages = fakeEvents.PackagesPassedToOnAcceptLicenses; + + Assert.AreEqual(expectedPackages, packages); + } + + [Test] + public void OnAcceptLicenses_NoInvokeRequired_NonThreadSafeOnAcceptLicensesMethodCalledAndReturnsResult() + { + CreateEvents(); + fakeEvents.AcceptLicensesReturnValue = false; + bool result = threadSafeEvents.OnAcceptLicenses(null); + + Assert.IsFalse(result); + } + + [Test] + public void OnParentPackageInstalled_NoInvokeRequired_NonThreadSafeOnParentPackageInstalledMethodCalled() + { + CreateEvents(); + var expectedPackage = new FakePackage(); + threadSafeEvents.OnParentPackageInstalled(expectedPackage); + + IPackage package = fakeEvents.PackagePassedToOnParentPackageInstalled; + + Assert.AreEqual(expectedPackage, package); + } + + [Test] + public void OnParentPackageUninstalled_NoInvokeRequired_NonThreadSafeOnParentPackageUninstalledMethodCalled() + { + CreateEvents(); + var expectedPackage = new FakePackage(); + threadSafeEvents.OnParentPackageUninstalled(expectedPackage); + + IPackage package = fakeEvents.PackagePassedToOnParentPackageUninstalled; + + Assert.AreEqual(expectedPackage, package); + } + + [Test] + public void OnPackageOperationMessageLogged_NoInvokeRequired_NonThreadSafeOnPackageOperationMessageLoggedMethodCalled() + { + CreateEvents(); + var messageLevel = MessageLevel.Warning; + string message = "abc {0}"; + string arg = "test"; + threadSafeEvents.OnPackageOperationMessageLogged(messageLevel, message, arg); + + Assert.AreEqual(messageLevel, fakeEvents.MessageLevelPassedToOnPackageOperationMessageLogged); + Assert.AreEqual("abc test", fakeEvents.FormattedStringPassedToOnPackageOperationMessageLogged); + } + + [Test] + public void AcceptLicenses_UnsafeEventFired_ThreadSafeEventFired() + { + CreateEventsWithRealPackageManagementEvents(); + bool fired = false; + threadSafeEvents.AcceptLicenses += (sender, e) => fired = true; + unsafeEvents.OnAcceptLicenses(null); + + Assert.IsTrue(fired); + } + + [Test] + public void AcceptLicenses_UnsafeEventFiredAfterEventHandlerRemoved_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.AcceptLicenses += OnEventHandlerFired; + threadSafeEvents.AcceptLicenses -= OnEventHandlerFired; + unsafeEvents.OnAcceptLicenses(null); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void PackageOperationsStarting_UnsafeEventFired_ThreadSafeEventFired() + { + CreateEventsWithRealPackageManagementEvents(); + bool fired = false; + threadSafeEvents.PackageOperationsStarting += (sender, e) => fired = true; + unsafeEvents.OnPackageOperationsStarting(); + + Assert.IsTrue(fired); + } + + [Test] + public void PackageOperationsStarting_UnsafeEventFiredAndInvokeRequired_ThreadSafeEventIsSafelyInvoked() + { + CreateEventsWithRealPackageManagementEvents(); + fakeWorkbench.InvokeRequiredReturnValue = true; + threadSafeEvents.PackageOperationsStarting += OnEventHandlerFired; + unsafeEvents.OnPackageOperationsStarting(); + + Assert.IsTrue(fakeWorkbench.IsSafeThreadAsyncCallMade); + } + + [Test] + public void PackageOperationsStarting_UnsafeEventFiredAndInvokeRequiredButNoEventHandlerRegistered_ThreadSafeEventIsNotInvoked() + { + CreateEventsWithRealPackageManagementEvents(); + fakeWorkbench.InvokeRequiredReturnValue = true; + unsafeEvents.OnPackageOperationsStarting(); + + Assert.IsFalse(fakeWorkbench.IsSafeThreadAsyncCallMade); + } + + [Test] + public void PackageOperationsStarting_UnsafeEventFiredAfterEventHandlerRemoved_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.PackageOperationsStarting += OnEventHandlerFired; + threadSafeEvents.PackageOperationsStarting -= OnEventHandlerFired; + unsafeEvents.OnPackageOperationsStarting(); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void PackageOperationError_UnsafeEventFired_ThreadSafeEventFired() + { + CreateEventsWithRealPackageManagementEvents(); + bool fired = false; + threadSafeEvents.PackageOperationError += (sender, e) => fired = true; + unsafeEvents.OnPackageOperationError(null); + + Assert.IsTrue(fired); + } + + [Test] + public void PackageOperationError_UnsafeEventFiredAndInvokeRequired_ThreadSafeEventIsSafelyInvoked() + { + CreateEventsWithRealPackageManagementEvents(); + fakeWorkbench.InvokeRequiredReturnValue = true; + threadSafeEvents.PackageOperationError += OnEventHandlerFired; + var expectedException = new Exception("Test"); + unsafeEvents.OnPackageOperationError(expectedException); + + var eventArgs = fakeWorkbench.Arg2PassedToSafeThreadAsyncCall as PackageOperationExceptionEventArgs; + Exception exception = eventArgs.Exception; + + Assert.AreEqual(expectedException, exception); + } + + [Test] + public void PackageOperationError_UnsafeEventFiredAndInvokeRequiredButNoEventHandlerRegistered_ThreadSafeEventIsNotInvoked() + { + CreateEventsWithRealPackageManagementEvents(); + fakeWorkbench.InvokeRequiredReturnValue = true; + unsafeEvents.OnPackageOperationError(new Exception()); + + Assert.IsFalse(fakeWorkbench.IsSafeThreadAsyncCallMade); + } + + [Test] + public void PackageOperationError_UnsafeEventFiredAfterEventHandlerRemoved_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.PackageOperationError += OnEventHandlerFired; + threadSafeEvents.PackageOperationError -= OnEventHandlerFired; + unsafeEvents.OnPackageOperationError(null); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void ParentPackageInstalled_UnsafeEventFired_ThreadSafeEventFired() + { + CreateEventsWithRealPackageManagementEvents(); + bool fired = false; + threadSafeEvents.ParentPackageInstalled += (sender, e) => fired = true; + unsafeEvents.OnParentPackageInstalled(null); + + Assert.IsTrue(fired); + } + + [Test] + public void ParentPackageInstalled_UnsafeEventFiredAndInvokeRequired_ThreadSafeEventIsSafelyInvoked() + { + CreateEventsWithRealPackageManagementEvents(); + fakeWorkbench.InvokeRequiredReturnValue = true; + threadSafeEvents.ParentPackageInstalled += OnEventHandlerFired; + var expectedPackage = new FakePackage(); + unsafeEvents.OnParentPackageInstalled(expectedPackage); + + var eventArgs = fakeWorkbench.Arg2PassedToSafeThreadAsyncCall as ParentPackageOperationEventArgs; + IPackage package = eventArgs.Package; + + Assert.AreEqual(expectedPackage, package); + } + + [Test] + public void ParentPackageInstalled_UnsafeEventFiredAndInvokeRequiredButNoEventHandlerRegistered_ThreadSafeEventIsNotInvoked() + { + CreateEventsWithRealPackageManagementEvents(); + fakeWorkbench.InvokeRequiredReturnValue = true; + unsafeEvents.OnParentPackageInstalled(new FakePackage()); + + Assert.IsFalse(fakeWorkbench.IsSafeThreadAsyncCallMade); + } + + [Test] + public void ParentPackageInstalled_UnsafeEventFiredAfterEventHandlerRemoved_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.ParentPackageInstalled += OnEventHandlerFired; + threadSafeEvents.ParentPackageInstalled -= OnEventHandlerFired; + unsafeEvents.OnParentPackageInstalled(null); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void ParentPackageUninstalled_UnsafeEventFired_ThreadSafeEventFired() + { + CreateEventsWithRealPackageManagementEvents(); + bool fired = false; + threadSafeEvents.ParentPackageUninstalled += (sender, e) => fired = true; + unsafeEvents.OnParentPackageUninstalled(null); + + Assert.IsTrue(fired); + } + + [Test] + public void ParentPackageUninstalled_UnsafeEventFiredAndInvokeRequired_ThreadSafeEventIsSafelyInvoked() + { + CreateEventsWithRealPackageManagementEvents(); + fakeWorkbench.InvokeRequiredReturnValue = true; + threadSafeEvents.ParentPackageUninstalled += OnEventHandlerFired; + var expectedPackage = new FakePackage(); + unsafeEvents.OnParentPackageUninstalled(expectedPackage); + + var eventArgs = fakeWorkbench.Arg2PassedToSafeThreadAsyncCall as ParentPackageOperationEventArgs; + IPackage package = eventArgs.Package; + + Assert.AreEqual(expectedPackage, package); + } + + [Test] + public void ParentPackageUninstalled_UnsafeEventFiredAndInvokeRequiredButNoEventHandlerRegistered_ThreadSafeEventIsNotInvoked() + { + CreateEventsWithRealPackageManagementEvents(); + fakeWorkbench.InvokeRequiredReturnValue = true; + unsafeEvents.OnParentPackageUninstalled(new FakePackage()); + + Assert.IsFalse(fakeWorkbench.IsSafeThreadAsyncCallMade); + } + + [Test] + public void ParentPackageUninstalled_UnsafeEventFiredAfterEventHandlerRemoved_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.ParentPackageUninstalled += OnEventHandlerFired; + threadSafeEvents.ParentPackageUninstalled -= OnEventHandlerFired; + unsafeEvents.OnParentPackageUninstalled(null); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void PackageOperationMessageLogged_UnsafeEventFired_ThreadSafeEventFired() + { + CreateEventsWithRealPackageManagementEvents(); + bool fired = false; + threadSafeEvents.PackageOperationMessageLogged += (sender, e) => fired = true; + unsafeEvents.OnPackageOperationMessageLogged(MessageLevel.Info, String.Empty, new object[0]); + + Assert.IsTrue(fired); + } + + [Test] + public void PackageOperationMessageLogged_UnsafeEventFiredAfterEventHandlerRemoved_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.PackageOperationMessageLogged += OnEventHandlerFired; + threadSafeEvents.PackageOperationMessageLogged -= OnEventHandlerFired; + unsafeEvents.OnPackageOperationMessageLogged(MessageLevel.Info, String.Empty, new object[0]); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void Dispose_PackageOperationsStartingHandlerExistsAndThreadUnsafeEventFiredAfterDispose_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.PackageOperationsStarting += OnEventHandlerFired; + + threadSafeEvents.Dispose(); + unsafeEvents.OnPackageOperationsStarting(); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void Dispose_PackageOperationErrorHandlerExistsAndThreadUnsafeEventFiredAfterDispose_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.PackageOperationError += OnEventHandlerFired; + + threadSafeEvents.Dispose(); + unsafeEvents.OnPackageOperationError(new Exception()); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void Dispose_ParentPackageInstalledHandlerExistsAndThreadUnsafeEventFiredAfterDispose_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.ParentPackageInstalled += OnEventHandlerFired; + + threadSafeEvents.Dispose(); + unsafeEvents.OnParentPackageInstalled(new FakePackage()); + + Assert.IsFalse(eventHandlerFired); + } + + [Test] + public void Dispose_ParentParentPackageUninstalledHandlerExistsAndThreadUnsafeEventFiredAfterDispose_ThreadSafeEventIsNotFired() + { + CreateEventsWithRealPackageManagementEvents(); + eventHandlerFired = false; + threadSafeEvents.ParentPackageUninstalled += OnEventHandlerFired; + + threadSafeEvents.Dispose(); + unsafeEvents.OnParentPackageUninstalled(new FakePackage()); + + Assert.IsFalse(eventHandlerFired); + } + } +}