diff --git a/src/NzbDrone.Common/Processes/ProcessProvider.cs b/src/NzbDrone.Common/Processes/ProcessProvider.cs index 6e9d2c0db..a2eff8d3d 100644 --- a/src/NzbDrone.Common/Processes/ProcessProvider.cs +++ b/src/NzbDrone.Common/Processes/ProcessProvider.cs @@ -21,6 +21,7 @@ namespace NzbDrone.Common.Processes void SetPriority(int processId, ProcessPriorityClass priority); void KillAll(string processName); void Kill(int processId); + Boolean Exists(int processId); Boolean Exists(string processName); ProcessPriorityClass GetCurrentProcessPriority(); Process Start(string path, string args = null, Action onOutputDataReceived = null, Action onErrorDataReceived = null); @@ -40,6 +41,11 @@ namespace NzbDrone.Common.Processes return ConvertToProcessInfo(Process.GetCurrentProcess()); } + public bool Exists(int processId) + { + return GetProcessById(processId) != null; + } + public Boolean Exists(string processName) { return GetProcessesByName(processName).Any(); diff --git a/src/NzbDrone.Update.Test/InstallUpdateServiceFixture.cs b/src/NzbDrone.Update.Test/InstallUpdateServiceFixture.cs index 2e240decd..6d9a95687 100644 --- a/src/NzbDrone.Update.Test/InstallUpdateServiceFixture.cs +++ b/src/NzbDrone.Update.Test/InstallUpdateServiceFixture.cs @@ -2,9 +2,9 @@ using System.IO; using FluentAssertions; using NUnit.Framework; -using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; using NzbDrone.Test.Common; using NzbDrone.Update.UpdateEngine; @@ -13,11 +13,28 @@ namespace NzbDrone.Update.Test [TestFixture] public class InstallUpdateServiceFixture : TestBase { + private string _targetFolder = @"C:\NzbDrone\".AsOsAgnostic(); + private const int _processId = 12; + [SetUp] public void Setup() { Mocker.GetMock() - .Setup(c => c.TempFolder).Returns(@"C:\Temp\"); + .Setup(c => c.TempFolder).Returns(@"C:\Temp\"); + } + + private void GivenTargetFolderExists() + { + Mocker.GetMock() + .Setup(c => c.FolderExists(_targetFolder)) + .Returns(true); + } + + private void GivenProcessExists() + { + Mocker.GetMock() + .Setup(c => c.Exists(_processId)) + .Returns(true); } [TestCase(null)] @@ -25,16 +42,14 @@ namespace NzbDrone.Update.Test [TestCase(" ")] public void update_should_throw_target_folder_is_blank(string target) { - Assert.Throws(() => Subject.Start(target)) + Assert.Throws(() => Subject.Start(target, _processId)) .Message.Should().StartWith("Target folder can not be null or empty"); } [Test] public void update_should_throw_if_target_folder_doesnt_exist() { - string targetFolder = "c:\\NzbDrone\\"; - - Assert.Throws(() => Subject.Start(targetFolder)) + Assert.Throws(() => Subject.Start(_targetFolder, _processId)) .Message.Should().StartWith("Target folder doesn't exist"); } @@ -42,18 +57,34 @@ namespace NzbDrone.Update.Test public void update_should_throw_if_update_folder_doesnt_exist() { const string sandboxFolder = @"C:\Temp\NzbDrone_update\nzbdrone"; - const string targetFolder = "c:\\NzbDrone\\"; - Mocker.GetMock() - .Setup(c => c.FolderExists(targetFolder)) - .Returns(true); + GivenTargetFolderExists(); + GivenProcessExists(); Mocker.GetMock() .Setup(c => c.FolderExists(sandboxFolder)) .Returns(false); - Assert.Throws(() => Subject.Start(targetFolder)) + Assert.Throws(() => Subject.Start(_targetFolder, _processId)) .Message.Should().StartWith("Update folder doesn't exist"); } + + [Test] + public void update_should_throw_if_process_is_zero() + { + GivenTargetFolderExists(); + + Assert.Throws(() => Subject.Start(_targetFolder, 0)) + .Message.Should().StartWith("Invalid process ID"); + } + + [Test] + public void update_should_throw_if_process_id_doesnt_exist() + { + GivenTargetFolderExists(); + + Assert.Throws(() => Subject.Start(_targetFolder, _processId)) + .Message.Should().StartWith("Process with ID doesn't exist"); + } } } diff --git a/src/NzbDrone.Update.Test/ProgramFixture.cs b/src/NzbDrone.Update.Test/ProgramFixture.cs index 5be4db66e..b4cc3049a 100644 --- a/src/NzbDrone.Update.Test/ProgramFixture.cs +++ b/src/NzbDrone.Update.Test/ProgramFixture.cs @@ -43,7 +43,7 @@ namespace NzbDrone.Update.Test Subject.Start(new[] { "12", "" }); - Mocker.GetMock().Verify(c => c.Start(@"C:\NzbDrone"), Times.Once()); + Mocker.GetMock().Verify(c => c.Start(@"C:\NzbDrone", 12), Times.Once()); } diff --git a/src/NzbDrone.Update/UpdateApp.cs b/src/NzbDrone.Update/UpdateApp.cs index 46de9307e..582efd936 100644 --- a/src/NzbDrone.Update/UpdateApp.cs +++ b/src/NzbDrone.Update/UpdateApp.cs @@ -68,7 +68,7 @@ namespace NzbDrone.Update } logger.Info("Starting update process. Target Path:{0}", targetFolder); - _installUpdateService.Start(targetFolder); + _installUpdateService.Start(targetFolder, startupContext.ProcessId); } private UpdateStartupContext ParseArgs(string[] args) diff --git a/src/NzbDrone.Update/UpdateEngine/InstallUpdateService.cs b/src/NzbDrone.Update/UpdateEngine/InstallUpdateService.cs index e70027a3a..3109372ad 100644 --- a/src/NzbDrone.Update/UpdateEngine/InstallUpdateService.cs +++ b/src/NzbDrone.Update/UpdateEngine/InstallUpdateService.cs @@ -4,12 +4,13 @@ using NLog; using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; namespace NzbDrone.Update.UpdateEngine { public interface IInstallUpdateService { - void Start(string installationFolder); + void Start(string installationFolder, int processId); } public class InstallUpdateService : IInstallUpdateService @@ -21,6 +22,7 @@ namespace NzbDrone.Update.UpdateEngine private readonly IBackupAndRestore _backupAndRestore; private readonly IBackupAppData _backupAppData; private readonly IStartNzbDrone _startNzbDrone; + private readonly IProcessProvider _processProvider; private readonly Logger _logger; public InstallUpdateService(IDiskProvider diskProvider, @@ -30,6 +32,7 @@ namespace NzbDrone.Update.UpdateEngine IBackupAndRestore backupAndRestore, IBackupAppData backupAppData, IStartNzbDrone startNzbDrone, + IProcessProvider processProvider, Logger logger) { _diskProvider = diskProvider; @@ -39,10 +42,11 @@ namespace NzbDrone.Update.UpdateEngine _backupAndRestore = backupAndRestore; _backupAppData = backupAppData; _startNzbDrone = startNzbDrone; + _processProvider = processProvider; _logger = logger; } - private void Verify(string targetFolder) + private void Verify(string targetFolder, int processId) { _logger.Info("Verifying requirements before update..."); @@ -52,20 +56,30 @@ namespace NzbDrone.Update.UpdateEngine if (!_diskProvider.FolderExists(targetFolder)) throw new DirectoryNotFoundException("Target folder doesn't exist " + targetFolder); + if (processId < 1) + { + throw new ArgumentException("Invalid process ID: " + processId); + } + + if (!_processProvider.Exists(processId)) + { + throw new ArgumentException("Process with ID doesn't exist " + processId); + } + _logger.Info("Verifying Update Folder"); if (!_diskProvider.FolderExists(_appFolderInfo.GetUpdatePackageFolder())) throw new DirectoryNotFoundException("Update folder doesn't exist " + _appFolderInfo.GetUpdatePackageFolder()); } - public void Start(string installationFolder) + public void Start(string installationFolder, int processId) { - Verify(installationFolder); + Verify(installationFolder, processId); var appType = _detectApplicationType.GetAppType(); try { - _terminateNzbDrone.Terminate(); + _terminateNzbDrone.Terminate(processId); _backupAndRestore.Backup(installationFolder); _backupAppData.Backup(); diff --git a/src/NzbDrone.Update/UpdateEngine/TerminateNzbDrone.cs b/src/NzbDrone.Update/UpdateEngine/TerminateNzbDrone.cs index 4c8934f03..62d56b2d6 100644 --- a/src/NzbDrone.Update/UpdateEngine/TerminateNzbDrone.cs +++ b/src/NzbDrone.Update/UpdateEngine/TerminateNzbDrone.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Update.UpdateEngine { public interface ITerminateNzbDrone { - void Terminate(); + void Terminate(int processId); } public class TerminateNzbDrone : ITerminateNzbDrone @@ -25,11 +25,12 @@ namespace NzbDrone.Update.UpdateEngine _logger = logger; } - public void Terminate() + public void Terminate(int processId) { if (OsInfo.IsMono) { _logger.Info("Stopping all instances"); + _processProvider.Kill(processId); _processProvider.KillAll(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME); _processProvider.KillAll(ProcessProvider.NZB_DRONE_PROCESS_NAME);