diff --git a/src/NzbDrone.Common.Test/DiskTests/DiskTransferServiceFixture.cs b/src/NzbDrone.Common.Test/DiskTests/DiskTransferServiceFixture.cs index d90d8e53d..1f0bee20b 100644 --- a/src/NzbDrone.Common.Test/DiskTests/DiskTransferServiceFixture.cs +++ b/src/NzbDrone.Common.Test/DiskTests/DiskTransferServiceFixture.cs @@ -1,11 +1,13 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; using NzbDrone.Test.Common; -using FluentAssertions; namespace NzbDrone.Common.Test.DiskTests { @@ -794,6 +796,57 @@ namespace NzbDrone.Common.Test.DiskTests VerifyCopyFolder(original.FullName, destination.FullName); } + [Test] + public void TransferFolder_should_use_movefolder_if_on_same_mount() + { + WithEmulatedDiskProvider(); + + var src = @"C:\Base1\TestDir1".AsOsAgnostic(); + var dst = @"C:\Base1\TestDir2".AsOsAgnostic(); + + WithMockMount(@"C:\Base1".AsOsAgnostic()); + WithExistingFile(@"C:\Base1\TestDir1\test.file.txt".AsOsAgnostic()); + + Subject.TransferFolder(src, dst, TransferMode.Move); + + Mocker.GetMock() + .Verify(v => v.MoveFolder(src, dst), Times.Once()); + } + + [Test] + public void TransferFolder_should_not_use_movefolder_if_on_same_mount_but_transactional() + { + WithEmulatedDiskProvider(); + + var src = @"C:\Base1\TestDir1".AsOsAgnostic(); + var dst = @"C:\Base1\TestDir2".AsOsAgnostic(); + + WithMockMount(@"C:\Base1".AsOsAgnostic()); + WithExistingFile(@"C:\Base1\TestDir1\test.file.txt".AsOsAgnostic()); + + Subject.TransferFolder(src, dst, TransferMode.Move, DiskTransferVerificationMode.Transactional); + + Mocker.GetMock() + .Verify(v => v.MoveFolder(src, dst), Times.Never()); + } + + [Test] + public void TransferFolder_should_not_use_movefolder_if_on_different_mount() + { + WithEmulatedDiskProvider(); + + var src = @"C:\Base1\TestDir1".AsOsAgnostic(); + var dst = @"C:\Base2\TestDir2".AsOsAgnostic(); + + WithMockMount(@"C:\Base1".AsOsAgnostic()); + WithMockMount(@"C:\Base2".AsOsAgnostic()); + + Subject.TransferFolder(src, dst, TransferMode.Move); + + Mocker.GetMock() + .Verify(v => v.MoveFolder(src, dst), Times.Never()); + } + public DirectoryInfo GetFilledTempFolder() { var tempFolder = GetTempFilePath(); @@ -810,8 +863,23 @@ namespace NzbDrone.Common.Test.DiskTests return new DirectoryInfo(tempFolder); } + private void WithExistingFolder(string path, bool exists = true) + { + var dir = Path.GetDirectoryName(path); + if (exists && dir.IsNotNullOrWhiteSpace()) + WithExistingFolder(dir); + + Mocker.GetMock() + .Setup(v => v.FolderExists(path)) + .Returns(exists); + } + private void WithExistingFile(string path, bool exists = true, int size = 1000) { + var dir = Path.GetDirectoryName(path); + if (exists && dir.IsNotNullOrWhiteSpace()) + WithExistingFolder(dir); + Mocker.GetMock() .Setup(v => v.FileExists(path)) .Returns(exists); @@ -863,6 +931,45 @@ namespace NzbDrone.Common.Test.DiskTests { WithExistingFile(v, false); }); + + + Mocker.GetMock() + .Setup(v => v.FolderExists(It.IsAny())) + .Returns(false); + + Mocker.GetMock() + .Setup(v => v.CreateFolder(It.IsAny())) + .Callback((f) => + { + WithExistingFolder(f); + }); + + Mocker.GetMock() + .Setup(v => v.MoveFolder(It.IsAny(), It.IsAny())) + .Callback((s, d) => + { + WithExistingFolder(s, false); + WithExistingFolder(d); + // Note: Should also deal with the files. + }); + + Mocker.GetMock() + .Setup(v => v.DeleteFolder(It.IsAny(), It.IsAny())) + .Callback((f, r) => + { + WithExistingFolder(f, false); + // Note: Should also deal with the files. + }); + + // Note: never returns anything. + Mocker.GetMock() + .Setup(v => v.GetDirectoryInfos(It.IsAny())) + .Returns(new List()); + + // Note: never returns anything. + Mocker.GetMock() + .Setup(v => v.GetFileInfos(It.IsAny())) + .Returns(new List()); } private void WithRealDiskProvider() @@ -919,6 +1026,18 @@ namespace NzbDrone.Common.Test.DiskTests .Returns(s => new FileStream(s, FileMode.Open, FileAccess.Read)); } + private void WithMockMount(string root) + { + var rootDir = root; + var mock = new Mock(); + mock.SetupGet(v => v.RootDirectory) + .Returns(rootDir); + + Mocker.GetMock() + .Setup(v => v.GetMount(It.Is(s => s.StartsWith(rootDir)))) + .Returns(mock.Object); + } + private void VerifyCopyFolder(string source, string destination) { var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray(); diff --git a/src/NzbDrone.Common/Disk/DiskTransferService.cs b/src/NzbDrone.Common/Disk/DiskTransferService.cs index 3dedc38d0..79eacccd8 100644 --- a/src/NzbDrone.Common/Disk/DiskTransferService.cs +++ b/src/NzbDrone.Common/Disk/DiskTransferService.cs @@ -56,6 +56,23 @@ namespace NzbDrone.Common.Disk Ensure.That(sourcePath, () => sourcePath).IsValidPath(); Ensure.That(targetPath, () => targetPath).IsValidPath(); + if (mode == TransferMode.Move) + { + if (verificationMode == DiskTransferVerificationMode.TryTransactional || verificationMode == DiskTransferVerificationMode.VerifyOnly) + { + var sourceMount = _diskProvider.GetMount(sourcePath); + var targetMount = _diskProvider.GetMount(targetPath); + + // If we're on the same mount, do a simple folder move. + if (sourceMount != null && targetMount != null && sourceMount.RootDirectory == targetMount.RootDirectory) + { + _logger.Debug("Move Directory [{0}] > [{1}]", sourcePath, targetPath); + _diskProvider.MoveFolder(sourcePath, targetPath); + return mode; + } + } + } + if (!_diskProvider.FolderExists(targetPath)) { _diskProvider.CreateFolder(targetPath); diff --git a/src/NzbDrone.Core/Tv/MoveSeriesService.cs b/src/NzbDrone.Core/Tv/MoveSeriesService.cs index 6797a1529..705f19e54 100644 --- a/src/NzbDrone.Core/Tv/MoveSeriesService.cs +++ b/src/NzbDrone.Core/Tv/MoveSeriesService.cs @@ -1,7 +1,6 @@ using System.IO; using NLog; using NzbDrone.Common.Disk; -using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; @@ -26,7 +25,6 @@ namespace NzbDrone.Core.Tv IBuildFileNames filenameBuilder, IDiskProvider diskProvider, IDiskTransferService diskTransferService, - IRootFolderService rootFolderService, IEventAggregator eventAggregator, Logger logger) { @@ -34,7 +32,6 @@ namespace NzbDrone.Core.Tv _filenameBuilder = filenameBuilder; _diskProvider = diskProvider; _diskTransferService = diskTransferService; - _rootFolderService = rootFolderService; _eventAggregator = eventAggregator; _logger = logger; } @@ -58,17 +55,7 @@ namespace NzbDrone.Core.Tv try { - var sourceRootFolder = _rootFolderService.GetBestRootFolderPath(sourcePath); - var destinationRootFolder = _rootFolderService.GetBestRootFolderPath(destinationPath); - - if (sourceRootFolder.PathEquals(destinationRootFolder) && !_diskProvider.FolderExists(destinationPath)) - { - _diskProvider.MoveFolder(sourcePath, destinationPath); - } - else - { - _diskTransferService.TransferFolder(sourcePath, destinationPath, TransferMode.Move); - } + _diskTransferService.TransferFolder(sourcePath, destinationPath, TransferMode.Move); _logger.ProgressInfo("{0} moved successfully to {1}", series.Title, series.Path);