diff --git a/NzbDrone.Api/Directories/DirectoryModule.cs b/NzbDrone.Api/Directories/DirectoryModule.cs index d78d48f27..5ee387740 100644 --- a/NzbDrone.Api/Directories/DirectoryModule.cs +++ b/NzbDrone.Api/Directories/DirectoryModule.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using Nancy; using NzbDrone.Api.Extensions; using NzbDrone.Common; @@ -8,12 +10,12 @@ namespace NzbDrone.Api.Directories { public class DirectoryModule : NzbDroneApiModule { - private readonly IDiskProvider _diskProvider; + private readonly IDirectoryLookupService _directoryLookupService; - public DirectoryModule(IDiskProvider diskProvider) + public DirectoryModule(IDirectoryLookupService directoryLookupService) : base("/directories") { - _diskProvider = diskProvider; + _directoryLookupService = directoryLookupService; Get["/"] = x => GetDirectories(); } @@ -24,30 +26,7 @@ private Response GetDirectories() string query = Request.Query.query.Value; - IEnumerable dirs = null; - try - { - //Windows (Including UNC) - var windowsSep = query.LastIndexOf('\\'); - - if (windowsSep > -1) - { - dirs = _diskProvider.GetDirectories(query.Substring(0, windowsSep + 1)); - } - - //Unix - var index = query.LastIndexOf('/'); - - if (index > -1) - { - dirs = _diskProvider.GetDirectories(query.Substring(0, index + 1)); - } - } - catch (Exception) - { - //Swallow the exceptions so proper JSON is returned to the client (Empty results) - return new List().AsResponse(); - } + var dirs = _directoryLookupService.LookupSubDirectories(query); if (dirs == null) throw new Exception("A valid path was not provided"); diff --git a/NzbDrone.Common.Test/DirectoryLookupServiceFixture.cs b/NzbDrone.Common.Test/DirectoryLookupServiceFixture.cs new file mode 100644 index 000000000..845ceb5af --- /dev/null +++ b/NzbDrone.Common.Test/DirectoryLookupServiceFixture.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Common.Test +{ + [TestFixture] + public class DirectoryLookupServiceFixture :TestBase + { + private const string RECYCLING_BIN = "$Recycle.Bin"; + private const string SYSTEM_VOLUME_INFORMATION = "System Volume Information"; + private List _folders; + + [SetUp] + public void Setup() + { + _folders = new List + { + RECYCLING_BIN, + "Chocolatey", + "Documents and Settings", + "Dropbox", + "Intel", + "PerfLogs", + "Program Files", + "Program Files (x86)", + "ProgramData", + SYSTEM_VOLUME_INFORMATION, + "Test", + "Users", + "Windows" + }; + } + + private void SetupFolders(string root) + { + _folders.ForEach(e => + { + e = Path.Combine(root, e); + }); + } + + [Test] + public void should_get_all_folder_for_none_root_path() + { + const string root = @"C:\Test\"; + SetupFolders(root); + + Mocker.GetMock() + .Setup(s => s.GetDirectories(It.IsAny())) + .Returns(_folders.ToArray()); + + Subject.LookupSubDirectories(root).Should() + .HaveCount(_folders.Count); + } + + [Test] + public void should_not_contain_recycling_bin_for_root_of_drive() + { + const string root = @"C:\"; + SetupFolders(root); + + Mocker.GetMock() + .Setup(s => s.GetDirectories(It.IsAny())) + .Returns(_folders.ToArray()); + + Subject.LookupSubDirectories(root).Should().NotContain(Path.Combine(root, RECYCLING_BIN)); + } + + [Test] + public void should_not_contain_system_volume_information_for_root_of_drive() + { + const string root = @"C:\"; + SetupFolders(root); + + Mocker.GetMock() + .Setup(s => s.GetDirectories(It.IsAny())) + .Returns(_folders.ToArray()); + + Subject.LookupSubDirectories(root).Should().NotContain(Path.Combine(root, SYSTEM_VOLUME_INFORMATION)); + } + + [Test] + public void should_not_contain_recycling_bin_or_system_volume_information_for_root_of_drive() + { + const string root = @"C:\"; + SetupFolders(root); + + Mocker.GetMock() + .Setup(s => s.GetDirectories(It.IsAny())) + .Returns(_folders.ToArray()); + + Subject.LookupSubDirectories(root).Should().HaveCount(_folders.Count - 2); + } + } +} diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj index dd04fc5bf..f953d6c07 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj @@ -62,6 +62,7 @@ + diff --git a/NzbDrone.Common/DirectoryLookupService.cs b/NzbDrone.Common/DirectoryLookupService.cs new file mode 100644 index 000000000..6fd9062b3 --- /dev/null +++ b/NzbDrone.Common/DirectoryLookupService.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace NzbDrone.Common +{ + public interface IDirectoryLookupService + { + List LookupSubDirectories(string query); + } + + public class DirectoryLookupService : IDirectoryLookupService + { + private readonly IDiskProvider _diskProvider; + + public DirectoryLookupService(IDiskProvider diskProvider) + { + _diskProvider = diskProvider; + } + + public List LookupSubDirectories(string query) + { + List dirs = null; + try + { + //Windows (Including UNC) + var windowsSep = query.LastIndexOf('\\'); + + if (windowsSep > -1) + { + var path = query.Substring(0, windowsSep + 1); + var dirsList = _diskProvider.GetDirectories(path).ToList(); + + if (Path.GetPathRoot(path).Equals(path, StringComparison.InvariantCultureIgnoreCase)) + { + var setToRemove = new HashSet { "$Recycle.Bin", "System Volume Information" }; + dirsList.RemoveAll(x => setToRemove.Contains(new DirectoryInfo(x).Name)); + } + + dirs = dirsList; + } + + //Unix + var index = query.LastIndexOf('/'); + + if (index > -1) + { + dirs = _diskProvider.GetDirectories(query.Substring(0, index + 1)).ToList(); + } + } + catch (Exception) + { + //Swallow the exceptions so proper JSON is returned to the client (Empty results) + return new List(); + } + + return dirs; + } + } +} diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index 93658190d..9d01e2f7c 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -58,7 +58,7 @@ static extern bool GetDiskFreeSpaceEx(string lpDirectoryName, private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public virtual DateTime GetLastFolderWrite(string path) + public DateTime GetLastFolderWrite(string path) { Ensure.That(() => path).IsValidPath(); @@ -78,7 +78,7 @@ public virtual DateTime GetLastFolderWrite(string path) .Max(c => c.LastWriteTimeUtc); } - public virtual DateTime GetLastFileWrite(string path) + public DateTime GetLastFileWrite(string path) { Ensure.That(() => path).IsValidPath(); @@ -89,7 +89,7 @@ public virtual DateTime GetLastFileWrite(string path) return new FileInfo(path).LastWriteTimeUtc; } - public virtual void EnsureFolder(string path) + public void EnsureFolder(string path) { if (!FolderExists(path)) { @@ -97,13 +97,13 @@ public virtual void EnsureFolder(string path) } } - public virtual bool FolderExists(string path) + public bool FolderExists(string path) { Ensure.That(() => path).IsValidPath(); return Directory.Exists(path); } - public virtual bool FolderExists(string path, bool caseSensitive) + public bool FolderExists(string path, bool caseSensitive) { if (caseSensitive) { @@ -113,13 +113,13 @@ public virtual bool FolderExists(string path, bool caseSensitive) return FolderExists(path); } - public virtual bool FileExists(string path) + public bool FileExists(string path) { Ensure.That(() => path).IsValidPath(); return File.Exists(path); } - public virtual bool FileExists(string path, bool caseSensitive) + public bool FileExists(string path, bool caseSensitive) { if (caseSensitive) { @@ -129,28 +129,28 @@ public virtual bool FileExists(string path, bool caseSensitive) return FileExists(path); } - public virtual string[] GetDirectories(string path) + public string[] GetDirectories(string path) { Ensure.That(() => path).IsValidPath(); return Directory.GetDirectories(path); } - public virtual string[] GetFiles(string path, SearchOption searchOption) + public string[] GetFiles(string path, SearchOption searchOption) { Ensure.That(() => path).IsValidPath(); return Directory.GetFiles(path, "*.*", searchOption); } - public virtual long GetFolderSize(string path) + public long GetFolderSize(string path) { Ensure.That(() => path).IsValidPath(); return GetFiles(path, SearchOption.AllDirectories).Sum(e => new FileInfo(e).Length); } - public virtual long GetFileSize(string path) + public long GetFileSize(string path) { Ensure.That(() => path).IsValidPath(); @@ -161,14 +161,14 @@ public virtual long GetFileSize(string path) return fi.Length; } - public virtual String CreateFolder(string path) + public String CreateFolder(string path) { Ensure.That(() => path).IsValidPath(); return Directory.CreateDirectory(path).FullName; } - public virtual void CopyFolder(string source, string target) + public void CopyFolder(string source, string target) { Ensure.That(() => source).IsValidPath(); Ensure.That(() => target).IsValidPath(); @@ -176,7 +176,7 @@ public virtual void CopyFolder(string source, string target) TransferFolder(source, target, TransferAction.Copy); } - public virtual void MoveFolder(string source, string destination) + public void MoveFolder(string source, string destination) { Ensure.That(() => source).IsValidPath(); Ensure.That(() => destination).IsValidPath(); @@ -237,7 +237,7 @@ private void TransferFolder(string source, string target, TransferAction transfe } } - public virtual void DeleteFile(string path) + public void DeleteFile(string path) { Ensure.That(() => path).IsValidPath(); @@ -245,7 +245,7 @@ public virtual void DeleteFile(string path) File.Delete(path); } - public virtual void MoveFile(string source, string destination) + public void MoveFile(string source, string destination) { Ensure.That(() => source).IsValidPath(); Ensure.That(() => destination).IsValidPath(); @@ -264,14 +264,14 @@ public virtual void MoveFile(string source, string destination) File.Move(source, destination); } - public virtual void DeleteFolder(string path, bool recursive) + public void DeleteFolder(string path, bool recursive) { Ensure.That(() => path).IsValidPath(); Directory.Delete(path, recursive); } - public virtual void InheritFolderPermissions(string filename) + public void InheritFolderPermissions(string filename) { Ensure.That(() => filename).IsValidPath(); @@ -280,7 +280,7 @@ public virtual void InheritFolderPermissions(string filename) File.SetAccessControl(filename, fs); } - public virtual long GetAvilableSpace(string path) + public long GetAvilableSpace(string path) { Ensure.That(() => path).IsValidPath(); @@ -327,14 +327,14 @@ private static long DriveFreeSpaceEx(string folderName) return 0; } - public virtual string ReadAllText(string filePath) + public string ReadAllText(string filePath) { Ensure.That(() => filePath).IsValidPath(); return File.ReadAllText(filePath); } - public virtual void WriteAllText(string filename, string contents) + public void WriteAllText(string filename, string contents) { Ensure.That(() => filename).IsValidPath(); @@ -349,21 +349,21 @@ public static bool PathEquals(string firstPath, string secondPath) return String.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath(), StringComparison.InvariantCultureIgnoreCase); } - public virtual void FileSetLastWriteTimeUtc(string path, DateTime dateTime) + public void FileSetLastWriteTimeUtc(string path, DateTime dateTime) { Ensure.That(() => path).IsValidPath(); File.SetLastWriteTimeUtc(path, dateTime); } - public virtual void FolderSetLastWriteTimeUtc(string path, DateTime dateTime) + public void FolderSetLastWriteTimeUtc(string path, DateTime dateTime) { Ensure.That(() => path).IsValidPath(); Directory.SetLastWriteTimeUtc(path, dateTime); } - public virtual bool IsFileLocked(FileInfo file) + public bool IsFileLocked(FileInfo file) { FileStream stream = null; @@ -385,7 +385,7 @@ public virtual bool IsFileLocked(FileInfo file) return false; } - public virtual string GetPathRoot(string path) + public string GetPathRoot(string path) { Ensure.That(() => path).IsValidPath(); diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index 8b1b1259a..51bc2324e 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -66,6 +66,7 @@ + diff --git a/NzbDrone.Core.Test/MediaFileTests/DropFolderImportServiceFixture.cs b/NzbDrone.Core.Test/MediaFileTests/DropFolderImportServiceFixture.cs index 49eb46543..870e5025a 100644 --- a/NzbDrone.Core.Test/MediaFileTests/DropFolderImportServiceFixture.cs +++ b/NzbDrone.Core.Test/MediaFileTests/DropFolderImportServiceFixture.cs @@ -109,7 +109,7 @@ public void should_not_delete_folder_if_no_files_were_imported() Subject.Execute(new DownloadedEpisodesScanCommand()); - Mocker.GetMock() + Mocker.GetMock() .Verify(v => v.GetFolderSize(It.IsAny()), Times.Never()); } diff --git a/NzbDrone.Core/Datastore/BasicRepository.cs b/NzbDrone.Core/Datastore/BasicRepository.cs index 303a9eef9..bcfe31652 100644 --- a/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/NzbDrone.Core/Datastore/BasicRepository.cs @@ -85,13 +85,13 @@ public TModel Get(int id) public IEnumerable Get(IEnumerable ids) { - var query = String.Format("Id IN ({0})", String.Join(",", ids)); - + var idList = ids.ToList(); + var query = String.Format("Id IN ({0})", String.Join(",", idList)); var result = Query.Where(query).ToList(); - if (result.Count != ids.Count()) + if (result.Count != idList.Count()) { - throw new ApplicationException("Expected query to return {0} rows but returned {1}".Inject(ids.Count(), result.Count)); + throw new ApplicationException("Expected query to return {0} rows but returned {1}".Inject(idList.Count(), result.Count)); } return result;