diff --git a/src/NzbDrone.Common.Test/PathExtensionFixture.cs b/src/NzbDrone.Common.Test/PathExtensionFixture.cs index db140d626..11f8c21f3 100644 --- a/src/NzbDrone.Common.Test/PathExtensionFixture.cs +++ b/src/NzbDrone.Common.Test/PathExtensionFixture.cs @@ -138,20 +138,34 @@ namespace NzbDrone.Common.Test } [TestCase(@"C:\Test\mydir", @"C:\Test")] - [TestCase(@"C:\Test\", @"C:")] + [TestCase(@"C:\Test\", @"C:\")] [TestCase(@"C:\", null)] + [TestCase(@"\\server\share", null)] + [TestCase(@"\\server\share\test", @"\\server\share")] + public void path_should_return_parent_windows(string path, string parentPath) + { + WindowsOnly(); + path.GetParentPath().Should().Be(parentPath); + } + [TestCase(@"/", null)] [TestCase(@"/test", null)] - public void path_should_return_parent(string path, string parentPath) + public void path_should_return_parent_mono(string path, string parentPath) { + MonoOnly(); path.GetParentPath().Should().Be(parentPath); } [Test] public void path_should_return_parent_for_oversized_path() { - var path = @"/media/2e168617-f2ae-43fb-b88c-3663af1c8eea/downloads/sabnzbd/nzbdrone/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories"; - var parentPath = @"/media/2e168617-f2ae-43fb-b88c-3663af1c8eea/downloads/sabnzbd/nzbdrone/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing/With.Alot.Of.Nested.Directories/Some.Real.Big.Thing"; + MonoOnly(); + + // This test will fail on Windows if long path support is not enabled: https://www.howtogeek.com/266621/how-to-make-windows-10-accept-file-paths-over-260-characters/ + // It will also fail if the app isn't configured to use long path (such as resharper): https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/ + + var path = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories"; + var parentPath = @"C:\media\2e168617-f2ae-43fb-b88c-3663af1c8eea\downloads\sabnzbd\nzbdrone\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing\With.Alot.Of.Nested.Directories\Some.Real.Big.Thing"; path.GetParentPath().Should().Be(parentPath); } diff --git a/src/NzbDrone.Common/Extensions/PathExtensions.cs b/src/NzbDrone.Common/Extensions/PathExtensions.cs index ff95d8973..10aa28b74 100644 --- a/src/NzbDrone.Common/Extensions/PathExtensions.cs +++ b/src/NzbDrone.Common/Extensions/PathExtensions.cs @@ -24,6 +24,8 @@ namespace NzbDrone.Common.Extensions private static readonly string UPDATE_CLIENT_FOLDER_NAME = "Sonarr.Update" + Path.DirectorySeparatorChar; private static readonly string UPDATE_LOG_FOLDER_NAME = "UpdateLogs" + Path.DirectorySeparatorChar; + private static readonly Regex PARENT_PATH_END_SLASH_REGEX = new Regex(@"(? path).IsNotNullOrWhiteSpace(); @@ -67,16 +69,11 @@ namespace NzbDrone.Common.Extensions public static string GetParentPath(this string childPath) { - var parentPath = childPath.TrimEnd('\\', '/'); + var cleanPath = OsInfo.IsWindows + ? PARENT_PATH_END_SLASH_REGEX.Replace(childPath, "") + : childPath.TrimEnd(Path.DirectorySeparatorChar); - var index = parentPath.LastIndexOfAny(new[] { '\\', '/' }); - - if (index > 0) - { - return parentPath.Substring(0, index); - } - - return null; + return Directory.GetParent(cleanPath)?.FullName; } public static bool IsParentPath(this string parentPath, string childPath) diff --git a/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs index f16be5c30..d6755949a 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs @@ -346,7 +346,7 @@ namespace NzbDrone.Core.Test.MediaFiles public void should_get_relative_path_when_there_is_no_grandparent() { var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr"; - var outputPath = Path.Combine(@"C:\".AsOsAgnostic()); + var outputPath = @"C:\".AsOsAgnostic(); var localEpisode = _approvedDecisions.First().LocalEpisode; localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo { ReleaseTitle = name }; @@ -357,6 +357,23 @@ namespace NzbDrone.Core.Test.MediaFiles Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.OriginalFilePath == $"{name}.mkv".AsOsAgnostic()))); } + [Test] + public void should_get_relative_path_when_there_is_no_grandparent_for_UNC_path() + { + WindowsOnly(); + + var name = "Series.Title.S01E01.720p.HDTV.x264-Sonarr"; + var outputPath = @"\\server\share"; + var localEpisode = _approvedDecisions.First().LocalEpisode; + + localEpisode.FolderEpisodeInfo = new ParsedEpisodeInfo { ReleaseTitle = name }; + localEpisode.Path = Path.Combine(outputPath, name + ".mkv"); + + Subject.Import(new List { _approvedDecisions.First() }, true, null); + + Mocker.GetMock().Verify(v => v.Add(It.Is(c => c.OriginalFilePath == $"{name}.mkv"))); + } + [Test] public void should_delete_existing_metadata_files_with_the_same_path() {