diff --git a/src/NzbDrone.Core.Test/Messaging/Commands/CommandExecutorFixture.cs b/src/NzbDrone.Core.Test/Messaging/Commands/CommandExecutorFixture.cs index 4a039e699..e0b3d37c5 100644 --- a/src/NzbDrone.Core.Test/Messaging/Commands/CommandExecutorFixture.cs +++ b/src/NzbDrone.Core.Test/Messaging/Commands/CommandExecutorFixture.cs @@ -1,121 +1,211 @@ -//using System; -//using System.Collections.Generic; -//using Moq; -//using NUnit.Framework; -//using NzbDrone.Common; -//using NzbDrone.Core.Messaging.Commands; -//using NzbDrone.Core.Messaging.Commands.Tracking; -//using NzbDrone.Core.Messaging.Events; -//using NzbDrone.Test.Common; -// -//namespace NzbDrone.Core.Test.Messaging.Commands -//{ -// [TestFixture] -// public class CommandExecutorFixture : TestBase -// { -// private Mock> _executorA; -// private Mock> _executorB; -// -// [SetUp] -// public void Setup() -// { -// _executorA = new Mock>(); -// _executorB = new Mock>(); -// -// Mocker.GetMock() -// .Setup(c => c.Build(typeof(IExecute))) -// .Returns(_executorA.Object); -// -// Mocker.GetMock() -// .Setup(c => c.Build(typeof(IExecute))) -// .Returns(_executorB.Object); -// -// -// Mocker.GetMock() -// .Setup(c => c.FindExisting(It.IsAny())) -// .Returns(null); -// } -// -// [Test] -// public void should_publish_command_to_executor() -// { -// var commandA = new CommandA(); -// -// Subject.Push(commandA); -// -// _executorA.Verify(c => c.Execute(commandA), Times.Once()); -// } -// -// [Test] -// public void should_publish_command_by_with_optional_arg_using_name() -// { -// Mocker.GetMock().Setup(c => c.GetImplementations(typeof(Command))) -// .Returns(new List { typeof(CommandA), typeof(CommandB) }); -// -// Subject.Push(typeof(CommandA).FullName); -// _executorA.Verify(c => c.Execute(It.IsAny()), Times.Once()); -// } -// -// -// [Test] -// public void should_not_publish_to_incompatible_executor() -// { -// var commandA = new CommandA(); -// -// Subject.Push(commandA); -// -// _executorA.Verify(c => c.Execute(commandA), Times.Once()); -// _executorB.Verify(c => c.Execute(It.IsAny()), Times.Never()); -// } -// -// [Test] -// public void broken_executor_should_throw_the_exception() -// { -// var commandA = new CommandA(); -// -// _executorA.Setup(c => c.Execute(It.IsAny())) -// .Throws(new NotImplementedException()); -// -// Assert.Throws(() => Subject.Push(commandA)); -// } -// -// -// [Test] -// public void broken_executor_should_publish_executed_event() -// { -// var commandA = new CommandA(); -// -// _executorA.Setup(c => c.Execute(It.IsAny())) -// .Throws(new NotImplementedException()); -// -// Assert.Throws(() => Subject.Push(commandA)); -// -// VerifyEventPublished(); -// } -// -// [Test] -// public void should_publish_executed_event_on_success() -// { -// var commandA = new CommandA(); -// Subject.Push(commandA); -// -// VerifyEventPublished(); -// } -// } -// -// public class CommandA : Command -// { -// public CommandA(int id = 0) -// { -// } -// } -// -// public class CommandB : Command -// { -// -// public CommandB() -// { -// } -// } -// -//} \ No newline at end of file +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.Messaging.Commands +{ + [TestFixture] + public class CommandExecutorFixture : TestBase + { + private BlockingCollection _commandQueue; + private Mock> _executorA; + private Mock> _executorB; + private bool _commandExecuted = false; + + [SetUp] + public void Setup() + { + _executorA = new Mock>(); + _executorB = new Mock>(); + + Mocker.GetMock() + .Setup(c => c.Build(typeof(IExecute))) + .Returns(_executorA.Object); + + Mocker.GetMock() + .Setup(c => c.Build(typeof(IExecute))) + .Returns(_executorB.Object); + } + + private void GivenCommandQueue() + { + _commandQueue = new BlockingCollection(new CommandQueue()); + + Mocker.GetMock() + .Setup(s => s.Queue(It.IsAny())) + .Returns(_commandQueue.GetConsumingEnumerable); + } + + private void WaitForExecution(CommandModel commandModel) + { + Mocker.GetMock() + .Setup(s => s.Complete(It.Is(c => c == commandModel), It.IsAny())) + .Callback(() => _commandExecuted = true); + + Mocker.GetMock() + .Setup(s => s.Fail(It.Is(c => c == commandModel), It.IsAny(), It.IsAny())) + .Callback(() => _commandExecuted = true); + + while (!_commandExecuted) + { + Thread.Sleep(100); + } + + var t1 = 1; + } + + [Test] + public void should_start_executor_threads() + { + Subject.Handle(new ApplicationStartedEvent()); + + Mocker.GetMock() + .Verify(v => v.Queue(It.IsAny()), Times.AtLeastOnce()); + } + + [Test] + public void should_execute_on_executor() + { + GivenCommandQueue(); + var commandA = new CommandA(); + var commandModel = new CommandModel + { + Body = commandA + }; + + Subject.Handle(new ApplicationStartedEvent()); + _commandQueue.Add(commandModel); + + WaitForExecution(commandModel); + + _executorA.Verify(c => c.Execute(commandA), Times.Once()); + } + + [Test] + public void should_not_execute_on_incompatible_executor() + { + GivenCommandQueue(); + var commandA = new CommandA(); + var commandModel = new CommandModel + { + Body = commandA + }; + + Subject.Handle(new ApplicationStartedEvent()); + _commandQueue.Add(commandModel); + + WaitForExecution(commandModel); + + _executorA.Verify(c => c.Execute(commandA), Times.Once()); + _executorB.Verify(c => c.Execute(It.IsAny()), Times.Never()); + } + + [Test] + public void broken_executor_should_publish_executed_event() + { + GivenCommandQueue(); + var commandA = new CommandA(); + var commandModel = new CommandModel + { + Body = commandA + }; + + _executorA.Setup(s => s.Execute(It.IsAny())) + .Throws(new NotImplementedException()); + + Subject.Handle(new ApplicationStartedEvent()); + _commandQueue.Add(commandModel); + + WaitForExecution(commandModel); + + VerifyEventPublished(); + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void should_publish_executed_event_on_success() + { + GivenCommandQueue(); + var commandA = new CommandA(); + var commandModel = new CommandModel + { + Body = commandA + }; + + Subject.Handle(new ApplicationStartedEvent()); + _commandQueue.Add(commandModel); + + WaitForExecution(commandModel); + + VerifyEventPublished(); + } + + [Test] + public void should_use_completion_message() + { + GivenCommandQueue(); + var commandA = new CommandA(); + var commandModel = new CommandModel + { + Body = commandA + }; + + Subject.Handle(new ApplicationStartedEvent()); + _commandQueue.Add(commandModel); + + WaitForExecution(commandModel); + + Mocker.GetMock() + .Setup(s => s.Complete(It.Is(c => c == commandModel), commandA.CompletionMessage)) + .Callback(() => _commandExecuted = true); + } + + [Test] + public void should_use_last_progress_message_if_completion_message_is_null() + { + GivenCommandQueue(); + var commandA = new CommandA(); + var commandModel = new CommandModel + { + Body = commandA, + Message = "Do work" + }; + + Subject.Handle(new ApplicationStartedEvent()); + _commandQueue.Add(commandModel); + + WaitForExecution(commandModel); + + Mocker.GetMock() + .Setup(s => s.Complete(It.Is(c => c == commandModel), commandModel.Message)) + .Callback(() => _commandExecuted = true); + } + } + + public class CommandA : Command + { + public CommandA(int id = 0) + { + } + } + + public class CommandB : Command + { + + public CommandB() + { + + } + + public override string CompletionMessage => null; + } + +} diff --git a/src/NzbDrone.Core/Messaging/Commands/CommandExecutor.cs b/src/NzbDrone.Core/Messaging/Commands/CommandExecutor.cs index 15843ef7b..3a577a287 100644 --- a/src/NzbDrone.Core/Messaging/Commands/CommandExecutor.cs +++ b/src/NzbDrone.Core/Messaging/Commands/CommandExecutor.cs @@ -76,7 +76,7 @@ namespace NzbDrone.Core.Messaging.Commands handler.Execute(command); - _commandQueueManager.Complete(commandModel, command.CompletionMessage); + _commandQueueManager.Complete(commandModel, command.CompletionMessage ?? commandModel.Message); } catch (CommandFailedException ex) { diff --git a/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs b/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs index 7c4604a7c..f3b920b08 100644 --- a/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs +++ b/src/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs @@ -1,4 +1,4 @@ -using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Update.Commands { @@ -6,6 +6,6 @@ namespace NzbDrone.Core.Update.Commands { public override bool SendUpdatesToClient => true; - public override string CompletionMessage => "Restarting Lidarr to apply updates"; + public override string CompletionMessage => null; } } diff --git a/src/NzbDrone.Core/Update/InstallUpdateService.cs b/src/NzbDrone.Core/Update/InstallUpdateService.cs index d9490ba2e..ea0b98aec 100644 --- a/src/NzbDrone.Core/Update/InstallUpdateService.cs +++ b/src/NzbDrone.Core/Update/InstallUpdateService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using NLog; @@ -200,19 +200,20 @@ namespace NzbDrone.Core.Update if (latestAvailable == null) { - _logger.ProgressDebug("No update available."); + _logger.ProgressDebug("No update available"); return; } if (OsInfo.IsNotWindows && !_configFileProvider.UpdateAutomatically && message.Trigger != CommandTrigger.Manual) { - _logger.ProgressDebug("Auto-update not enabled, not installing available update."); + _logger.ProgressDebug("Auto-update not enabled, not installing available update"); return; } try { InstallUpdate(latestAvailable); + _logger.ProgressDebug("Restarting Lidarr to apply updates"); } catch (UpdateFolderNotWritableException ex) {