// // Copyright (c) PlaceholderCompany. All rights reserved. // namespace Jellyfin.Controller.Tests { using System.Collections.Generic; using System.Linq; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; using Moq; using Xunit; /// /// Tests for . /// public class DirectoryServiceTests { private const string LowerCasePath = "/music/someartist"; private const string UpperCasePath = "/music/SOMEARTIST"; private static readonly FileSystemMetadata[] LowerCaseFileSystemMetadata = [ new() { FullName = LowerCasePath + "/Artwork", IsDirectory = true, }, new() { FullName = LowerCasePath + "/Some Other Folder", IsDirectory = true, }, new() { FullName = LowerCasePath + "/Song 2.mp3", IsDirectory = false, }, new() { FullName = LowerCasePath + "/Song 3.mp3", IsDirectory = false, }, ]; private static readonly FileSystemMetadata[] UpperCaseFileSystemMetadata = [ new() { FullName = UpperCasePath + "/Lyrics", IsDirectory = true, }, new() { FullName = UpperCasePath + "/Song 1.mp3", IsDirectory = false, }, ]; /// /// Tests that GetFileSystemEntries caches entries for paths with different casing. /// [Fact] public void GetFileSystemEntries_GivenPathsWithDifferentCasing_CachesAll() { Mock fileSystemMock = new(); _ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == UpperCasePath), false)).Returns(UpperCaseFileSystemMetadata); _ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == LowerCasePath), false)).Returns(LowerCaseFileSystemMetadata); DirectoryService directoryService = new(fileSystemMock.Object); FileSystemMetadata[] upperCaseResult = directoryService.GetFileSystemEntries(UpperCasePath); FileSystemMetadata[] lowerCaseResult = directoryService.GetFileSystemEntries(LowerCasePath); Assert.Equal(UpperCaseFileSystemMetadata, upperCaseResult); Assert.Equal(LowerCaseFileSystemMetadata, lowerCaseResult); } /// /// Tests that GetFiles returns correct files for paths with different casing. /// [Fact] public void GetFiles_GivenPathsWithDifferentCasing_ReturnsCorrectFiles() { Mock fileSystemMock = new(); _ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == UpperCasePath), false)).Returns(UpperCaseFileSystemMetadata); _ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == LowerCasePath), false)).Returns(LowerCaseFileSystemMetadata); DirectoryService directoryService = new(fileSystemMock.Object); List upperCaseResult = directoryService.GetFiles(UpperCasePath); List lowerCaseResult = directoryService.GetFiles(LowerCasePath); Assert.Equal(UpperCaseFileSystemMetadata.Where(f => !f.IsDirectory), upperCaseResult); Assert.Equal(LowerCaseFileSystemMetadata.Where(f => !f.IsDirectory), lowerCaseResult); } /// /// Tests that GetDirectories returns correct directories for paths with different casing. /// [Fact] public void GetDirectories_GivenPathsWithDifferentCasing_ReturnsCorrectDirectories() { Mock fileSystemMock = new(); _ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == UpperCasePath), false)).Returns(UpperCaseFileSystemMetadata); _ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is(x => x == LowerCasePath), false)).Returns(LowerCaseFileSystemMetadata); DirectoryService directoryService = new(fileSystemMock.Object); List upperCaseResult = directoryService.GetDirectories(UpperCasePath); List lowerCaseResult = directoryService.GetDirectories(LowerCasePath); Assert.Equal(UpperCaseFileSystemMetadata.Where(f => f.IsDirectory), upperCaseResult); Assert.Equal(LowerCaseFileSystemMetadata.Where(f => f.IsDirectory), lowerCaseResult); } /// /// Tests that GetFile returns correct file for paths with different casing. /// [Fact] public void GetFile_GivenFilePathsWithDifferentCasing_ReturnsCorrectFile() { const string lowerCasePath = "/music/someartist/song 1.mp3"; FileSystemMetadata lowerCaseFileSystemMetadata = new() { FullName = lowerCasePath, Exists = true, }; const string upperCasePath = "/music/SOMEARTIST/SONG 1.mp3"; FileSystemMetadata upperCaseFileSystemMetadata = new() { FullName = upperCasePath, Exists = false, }; Mock fileSystemMock = new(); _ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata); _ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata); DirectoryService directoryService = new(fileSystemMock.Object); FileSystemMetadata? lowerCaseDirResult = directoryService.GetDirectory(lowerCasePath); FileSystemMetadata? lowerCaseFileResult = directoryService.GetFile(lowerCasePath); FileSystemMetadata? upperCaseDirResult = directoryService.GetDirectory(upperCasePath); FileSystemMetadata? upperCaseFileResult = directoryService.GetFile(upperCasePath); Assert.Null(lowerCaseDirResult); Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseFileResult); Assert.Null(upperCaseDirResult); Assert.Null(upperCaseFileResult); } /// /// Tests that GetDirectory returns correct directory for paths with different casing. /// [Fact] public void GetDirectory_GivenFilePathsWithDifferentCasing_ReturnsCorrectDirectory() { const string lowerCasePath = "/music/someartist/Lyrics"; FileSystemMetadata lowerCaseFileSystemMetadata = new() { FullName = lowerCasePath, IsDirectory = true, Exists = true, }; const string upperCasePath = "/music/SOMEARTIST/LYRICS"; FileSystemMetadata upperCaseFileSystemMetadata = new() { FullName = upperCasePath, IsDirectory = true, Exists = false, }; Mock fileSystemMock = new(); _ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata); _ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata); DirectoryService directoryService = new(fileSystemMock.Object); FileSystemMetadata? lowerCaseDirResult = directoryService.GetDirectory(lowerCasePath); FileSystemMetadata? lowerCaseFileResult = directoryService.GetFile(lowerCasePath); FileSystemMetadata? upperCaseDirResult = directoryService.GetDirectory(upperCasePath); FileSystemMetadata? upperCaseFileResult = directoryService.GetFile(upperCasePath); Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseDirResult); Assert.Null(lowerCaseFileResult); Assert.Null(upperCaseDirResult); Assert.Null(upperCaseFileResult); } /// /// Tests that GetFile returns cached file when the path is already cached. /// [Fact] public void GetFile_GivenCachedPath_ReturnsCachedFile() { const string path = "/music/someartist/song 1.mp3"; FileSystemMetadata cachedFileSystemMetadata = new() { FullName = path, Exists = true, }; FileSystemMetadata newFileSystemMetadata = new() { FullName = "/music/SOMEARTIST/song 1.mp3", Exists = true, }; Mock fileSystemMock = new(); _ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is(x => x == path))).Returns(cachedFileSystemMetadata); DirectoryService directoryService = new(fileSystemMock.Object); FileSystemMetadata? result = directoryService.GetFile(path); _ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is(x => x == path))).Returns(newFileSystemMetadata); FileSystemMetadata? secondResult = directoryService.GetFile(path); Assert.Equivalent(cachedFileSystemMetadata, result); Assert.Equivalent(cachedFileSystemMetadata, secondResult); } /// /// Tests that GetFilePaths returns only cached paths when clear is not called. /// [Fact] public void GetFilePaths_GivenCachedFilePathWithoutClear_ReturnsOnlyCachedPaths() { const string path = "/music/someartist"; string[] cachedPaths = [ "/music/someartist/song 1.mp3", "/music/someartist/song 2.mp3", "/music/someartist/song 3.mp3", "/music/someartist/song 4.mp3", ]; string[] newPaths = [ "/music/someartist/song 5.mp3", "/music/someartist/song 6.mp3", "/music/someartist/song 7.mp3", "/music/someartist/song 8.mp3", ]; Mock fileSystemMock = new(); _ = fileSystemMock.Setup(f => f.GetFilePaths(It.Is(x => x == path), false)).Returns(cachedPaths); DirectoryService directoryService = new(fileSystemMock.Object); IReadOnlyList result = directoryService.GetFilePaths(path); _ = fileSystemMock.Setup(f => f.GetFilePaths(It.Is(x => x == path), false)).Returns(newPaths); IReadOnlyList secondResult = directoryService.GetFilePaths(path); Assert.Equal(cachedPaths, result); Assert.Equal(cachedPaths, secondResult); } /// /// Tests that GetFilePaths returns new paths when clear is called. /// [Fact] public void GetFilePaths_GivenCachedFilePathWithClear_ReturnsNewPaths() { const string path = "/music/someartist"; string[] cachedPaths = [ "/music/someartist/song 1.mp3", "/music/someartist/song 2.mp3", "/music/someartist/song 3.mp3", "/music/someartist/song 4.mp3", ]; string[] newPaths = [ "/music/someartist/song 5.mp3", "/music/someartist/song 6.mp3", "/music/someartist/song 7.mp3", "/music/someartist/song 8.mp3", ]; Mock fileSystemMock = new(); _ = fileSystemMock.Setup(f => f.GetFilePaths(It.Is(x => x == path), false)).Returns(cachedPaths); DirectoryService directoryService = new(fileSystemMock.Object); IReadOnlyList result = directoryService.GetFilePaths(path); _ = fileSystemMock.Setup(f => f.GetFilePaths(It.Is(x => x == path), false)).Returns(newPaths); IReadOnlyList secondResult = directoryService.GetFilePaths(path, true); Assert.Equal(cachedPaths, result); Assert.Equal(newPaths, secondResult); } } }