Improve code clarity and test documentation
Refactored ImageProcessor to use explicit 'this.' member access for consistency and readability. Enhanced test files with XML documentation, explicit types, modern C# syntax, and clearer Moq setups. Enabled XML doc generation for test project. Updated assembly info and cache files as a result of build changes. No functional or behavioral changes.
This commit is contained in:
Binary file not shown.
Binary file not shown.
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
//
|
//
|
||||||
// Changes to this file may cause incorrect behavior and will be lost if
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// the code is regenerated.
|
||||||
@@ -13,7 +14,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Jellyfin.CodeAnalysis")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Jellyfin.CodeAnalysis")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+e8873563856ad813cb4b7695b0aaa85c32729a4c")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+48569427a5cba735184e017148b7c71f665279bc")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Jellyfin.CodeAnalysis")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Jellyfin.CodeAnalysis")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Jellyfin.CodeAnalysis")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Jellyfin.CodeAnalysis")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
4aa28b28b5f36d3ba59b76bc7e3a9a39544dcee89f6b8e3632f4df17d7b042d4
|
5afb18d45c1a1f35abd23adbc2ff716c2f51ec4673ea08b8765ec2e818a59a28
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -64,10 +64,10 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
IImageEncoder imageEncoder,
|
IImageEncoder imageEncoder,
|
||||||
IServerConfigurationManager config)
|
IServerConfigurationManager config)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
this._logger = logger;
|
||||||
_fileSystem = fileSystem;
|
this._fileSystem = fileSystem;
|
||||||
_imageEncoder = imageEncoder;
|
this._imageEncoder = imageEncoder;
|
||||||
_appPaths = appPaths;
|
this._appPaths = appPaths;
|
||||||
|
|
||||||
var semaphoreCount = config.Configuration.ParallelImageEncodingLimit;
|
var semaphoreCount = config.Configuration.ParallelImageEncodingLimit;
|
||||||
if (semaphoreCount < 1)
|
if (semaphoreCount < 1)
|
||||||
@@ -75,10 +75,10 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
semaphoreCount = Environment.ProcessorCount;
|
semaphoreCount = Environment.ProcessorCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
_parallelEncodingLimit = new(semaphoreCount);
|
this._parallelEncodingLimit = new(semaphoreCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images");
|
private string ResizedImageCachePath => Path.Combine(this._appPaths.ImageCachePath, "resized-images");
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyCollection<string> SupportedInputFormats =>
|
public IReadOnlyCollection<string> SupportedInputFormats =>
|
||||||
@@ -113,11 +113,11 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation;
|
public bool SupportsImageCollageCreation => this._imageEncoder.SupportsImageCollageCreation;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats()
|
public IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats()
|
||||||
=> _imageEncoder.SupportedOutputFormats;
|
=> this._imageEncoder.SupportedOutputFormats;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<(string Path, string? MimeType, DateTime DateModified)> ProcessImage(ImageProcessingOptions options)
|
public async Task<(string Path, string? MimeType, DateTime DateModified)> ProcessImage(ImageProcessingOptions options)
|
||||||
@@ -134,12 +134,12 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
var mimeType = MimeTypes.GetMimeType(originalImagePath);
|
var mimeType = MimeTypes.GetMimeType(originalImagePath);
|
||||||
if (!_imageEncoder.SupportsImageEncoding)
|
if (!this._imageEncoder.SupportsImageEncoding)
|
||||||
{
|
{
|
||||||
return (originalImagePath, mimeType, dateModified);
|
return (originalImagePath, mimeType, dateModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportedImageInfo = await GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false);
|
var supportedImageInfo = await this.GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false);
|
||||||
originalImagePath = supportedImageInfo.Path;
|
originalImagePath = supportedImageInfo.Path;
|
||||||
|
|
||||||
// Original file doesn't exist, or original file is gif.
|
// Original file doesn't exist, or original file is gif.
|
||||||
@@ -179,8 +179,8 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
|
|
||||||
int quality = options.Quality;
|
int quality = options.Quality;
|
||||||
|
|
||||||
ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
|
ImageFormat outputFormat = this.GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
|
||||||
string cacheFilePath = GetCacheFilePath(
|
string cacheFilePath = this.GetCacheFilePath(
|
||||||
originalImagePath,
|
originalImagePath,
|
||||||
options.Width,
|
options.Width,
|
||||||
options.Height,
|
options.Height,
|
||||||
@@ -204,9 +204,9 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
string resultPath;
|
string resultPath;
|
||||||
|
|
||||||
// Limit number of parallel (more precisely: concurrent) image encodings to prevent a high memory usage
|
// Limit number of parallel (more precisely: concurrent) image encodings to prevent a high memory usage
|
||||||
using (await _parallelEncodingLimit.LockAsync().ConfigureAwait(false))
|
using (await this._parallelEncodingLimit.LockAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat);
|
resultPath = this._imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -215,19 +215,19 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (cacheFilePath, outputFormat.GetMimeType(), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
|
return (cacheFilePath, outputFormat.GetMimeType(), this._fileSystem.GetLastWriteTimeUtc(cacheFilePath));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// If it fails for whatever reason, return the original image
|
// If it fails for whatever reason, return the original image
|
||||||
_logger.LogError(ex, "Error encoding image");
|
this._logger.LogError(ex, "Error encoding image");
|
||||||
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImageFormat GetOutputFormat(IReadOnlyCollection<ImageFormat> clientSupportedFormats, bool requiresTransparency)
|
private ImageFormat GetOutputFormat(IReadOnlyCollection<ImageFormat> clientSupportedFormats, bool requiresTransparency)
|
||||||
{
|
{
|
||||||
var serverFormats = GetSupportedImageOutputFormats();
|
var serverFormats = this.GetSupportedImageOutputFormats();
|
||||||
|
|
||||||
// Client doesn't care about format, so start with webp if supported
|
// Client doesn't care about format, so start with webp if supported
|
||||||
if (serverFormats.Contains(ImageFormat.Webp) && clientSupportedFormats.Contains(ImageFormat.Webp))
|
if (serverFormats.Contains(ImageFormat.Webp) && clientSupportedFormats.Contains(ImageFormat.Webp))
|
||||||
@@ -354,7 +354,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
filename.Append(",v=");
|
filename.Append(",v=");
|
||||||
filename.Append(Version);
|
filename.Append(Version);
|
||||||
|
|
||||||
return GetCachePath(ResizedImageCachePath, filename.ToString(), format.GetExtension());
|
return this.GetCachePath(this.ResizedImageCachePath, filename.ToString(), format.GetExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -369,9 +369,9 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
string path = info.Path;
|
string path = info.Path;
|
||||||
_logger.LogDebug("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
|
this._logger.LogDebug("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
|
||||||
|
|
||||||
ImageDimensions size = GetImageDimensions(path);
|
ImageDimensions size = this.GetImageDimensions(path);
|
||||||
info.Width = size.Width;
|
info.Width = size.Width;
|
||||||
info.Height = size.Height;
|
info.Height = size.Height;
|
||||||
|
|
||||||
@@ -380,13 +380,13 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ImageDimensions GetImageDimensions(string path)
|
public ImageDimensions GetImageDimensions(string path)
|
||||||
=> _imageEncoder.GetImageSize(path);
|
=> this._imageEncoder.GetImageSize(path);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string GetImageBlurHash(string path)
|
public string GetImageBlurHash(string path)
|
||||||
{
|
{
|
||||||
var size = GetImageDimensions(path);
|
var size = this.GetImageDimensions(path);
|
||||||
return GetImageBlurHash(path, size);
|
return this.GetImageBlurHash(path, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -406,7 +406,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
int xComp = Math.Min((int)xCompF + 1, 9);
|
int xComp = Math.Min((int)xCompF + 1, 9);
|
||||||
int yComp = Math.Min((int)yCompF + 1, 9);
|
int yComp = Math.Min((int)yCompF + 1, 9);
|
||||||
|
|
||||||
return _imageEncoder.GetImageBlurHash(xComp, yComp, path);
|
return this._imageEncoder.GetImageBlurHash(xComp, yComp, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -415,11 +415,11 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
|
public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
|
||||||
=> GetImageCacheTag(item.Path, image.DateModified);
|
=> this.GetImageCacheTag(item.Path, image.DateModified);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string GetImageCacheTag(BaseItemDto item, ItemImageInfo image)
|
public string GetImageCacheTag(BaseItemDto item, ItemImageInfo image)
|
||||||
=> GetImageCacheTag(item.Path, image.DateModified);
|
=> this.GetImageCacheTag(item.Path, image.DateModified);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string? GetImageCacheTag(BaseItemDto item, ChapterInfo chapter)
|
public string? GetImageCacheTag(BaseItemDto item, ChapterInfo chapter)
|
||||||
@@ -429,7 +429,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetImageCacheTag(item.Path, chapter.ImageDateModified);
|
return this.GetImageCacheTag(item.Path, chapter.ImageDateModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -440,7 +440,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetImageCacheTag(item, new ItemImageInfo
|
return this.GetImageCacheTag(item, new ItemImageInfo
|
||||||
{
|
{
|
||||||
Path = chapter.ImagePath,
|
Path = chapter.ImagePath,
|
||||||
Type = ImageType.Chapter,
|
Type = ImageType.Chapter,
|
||||||
@@ -456,7 +456,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetImageCacheTag(user.ProfileImage.Path, user.ProfileImage.LastModified);
|
return this.GetImageCacheTag(user.ProfileImage.Path, user.ProfileImage.LastModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<(string Path, DateTime DateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
|
private Task<(string Path, DateTime DateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
|
||||||
@@ -494,7 +494,7 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
|
|
||||||
var filename = uniqueName.GetMD5() + fileExtension;
|
var filename = uniqueName.GetMD5() + fileExtension;
|
||||||
|
|
||||||
return GetCachePath(path, filename);
|
return this.GetCachePath(path, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -528,28 +528,28 @@ public sealed class ImageProcessor : IImageProcessor, IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void CreateImageCollage(ImageCollageOptions options, string? libraryName)
|
public void CreateImageCollage(ImageCollageOptions options, string? libraryName)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Creating image collage and saving to {Path}", options.OutputPath);
|
this._logger.LogDebug("Creating image collage and saving to {Path}", options.OutputPath);
|
||||||
|
|
||||||
_imageEncoder.CreateImageCollage(options, libraryName);
|
this._imageEncoder.CreateImageCollage(options, libraryName);
|
||||||
|
|
||||||
_logger.LogDebug("Completed creation of image collage and saved to {Path}", options.OutputPath);
|
this._logger.LogDebug("Completed creation of image collage and saved to {Path}", options.OutputPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (this._disposed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_imageEncoder is IDisposable disposable)
|
if (this._imageEncoder is IDisposable disposable)
|
||||||
{
|
{
|
||||||
disposable.Dispose();
|
disposable.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_parallelEncodingLimit?.Dispose();
|
this._parallelEncodingLimit?.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
this._disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,17 @@ namespace Jellyfin.Controller.Tests
|
|||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for <see cref="BaseItemManager"/>.
|
||||||
|
/// </summary>
|
||||||
public class BaseItemManagerTests
|
public class BaseItemManagerTests
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that IsMetadataFetcherEnabled checks library and server configuration options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemType">The type of item to test.</param>
|
||||||
|
/// <param name="fetcherName">The name of the metadata fetcher.</param>
|
||||||
|
/// <param name="expected">The expected result.</param>
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(typeof(Book), "LibraryEnabled", true)]
|
[InlineData(typeof(Book), "LibraryEnabled", true)]
|
||||||
[InlineData(typeof(Book), "LibraryDisabled", false)]
|
[InlineData(typeof(Book), "LibraryDisabled", false)]
|
||||||
@@ -24,30 +33,36 @@ namespace Jellyfin.Controller.Tests
|
|||||||
{
|
{
|
||||||
BaseItem item = (BaseItem)Activator.CreateInstance(itemType)!;
|
BaseItem item = (BaseItem)Activator.CreateInstance(itemType)!;
|
||||||
|
|
||||||
var libraryTypeOptions = itemType == typeof(Book)
|
TypeOptions? libraryTypeOptions = itemType == typeof(Book)
|
||||||
? new TypeOptions
|
? new TypeOptions
|
||||||
{
|
{
|
||||||
Type = "Book",
|
Type = "Book",
|
||||||
MetadataFetchers = new[] { "LibraryEnabled" }
|
MetadataFetchers = ["LibraryEnabled"],
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
var serverConfiguration = new ServerConfiguration();
|
ServerConfiguration serverConfiguration = new();
|
||||||
foreach (var typeConfig in serverConfiguration.MetadataOptions)
|
foreach (MetadataOptions typeConfig in serverConfiguration.MetadataOptions)
|
||||||
{
|
{
|
||||||
typeConfig.DisabledMetadataFetchers = new[] { "ServerDisabled" };
|
typeConfig.DisabledMetadataFetchers = ["ServerDisabled"];
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverConfigurationManager = new Mock<IServerConfigurationManager>();
|
Mock<IServerConfigurationManager> serverConfigurationManager = new();
|
||||||
serverConfigurationManager.Setup(scm => scm.Configuration)
|
_ = serverConfigurationManager.Setup(scm => scm.Configuration)
|
||||||
.Returns(serverConfiguration);
|
.Returns(serverConfiguration);
|
||||||
|
|
||||||
var baseItemManager = new BaseItemManager(serverConfigurationManager.Object);
|
BaseItemManager baseItemManager = new(serverConfigurationManager.Object);
|
||||||
var actual = baseItemManager.IsMetadataFetcherEnabled(item, libraryTypeOptions, fetcherName);
|
bool actual = baseItemManager.IsMetadataFetcherEnabled(item, libraryTypeOptions, fetcherName);
|
||||||
|
|
||||||
Assert.Equal(expected, actual);
|
Assert.Equal(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that IsImageFetcherEnabled checks library and server configuration options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemType">The type of item to test.</param>
|
||||||
|
/// <param name="fetcherName">The name of the image fetcher.</param>
|
||||||
|
/// <param name="expected">The expected result.</param>
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(typeof(Book), "LibraryEnabled", true)]
|
[InlineData(typeof(Book), "LibraryEnabled", true)]
|
||||||
[InlineData(typeof(Book), "LibraryDisabled", false)]
|
[InlineData(typeof(Book), "LibraryDisabled", false)]
|
||||||
@@ -57,26 +72,26 @@ namespace Jellyfin.Controller.Tests
|
|||||||
{
|
{
|
||||||
BaseItem item = (BaseItem)Activator.CreateInstance(itemType)!;
|
BaseItem item = (BaseItem)Activator.CreateInstance(itemType)!;
|
||||||
|
|
||||||
var libraryTypeOptions = itemType == typeof(Book)
|
TypeOptions? libraryTypeOptions = itemType == typeof(Book)
|
||||||
? new TypeOptions
|
? new TypeOptions
|
||||||
{
|
{
|
||||||
Type = "Book",
|
Type = "Book",
|
||||||
ImageFetchers = new[] { "LibraryEnabled" }
|
ImageFetchers = ["LibraryEnabled"],
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
var serverConfiguration = new ServerConfiguration();
|
ServerConfiguration serverConfiguration = new();
|
||||||
foreach (var typeConfig in serverConfiguration.MetadataOptions)
|
foreach (MetadataOptions typeConfig in serverConfiguration.MetadataOptions)
|
||||||
{
|
{
|
||||||
typeConfig.DisabledImageFetchers = new[] { "ServerDisabled" };
|
typeConfig.DisabledImageFetchers = ["ServerDisabled"];
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverConfigurationManager = new Mock<IServerConfigurationManager>();
|
Mock<IServerConfigurationManager> serverConfigurationManager = new();
|
||||||
serverConfigurationManager.Setup(scm => scm.Configuration)
|
_ = serverConfigurationManager.Setup(scm => scm.Configuration)
|
||||||
.Returns(serverConfiguration);
|
.Returns(serverConfiguration);
|
||||||
|
|
||||||
var baseItemManager = new BaseItemManager(serverConfigurationManager.Object);
|
BaseItemManager baseItemManager = new(serverConfigurationManager.Object);
|
||||||
var actual = baseItemManager.IsImageFetcherEnabled(item, libraryTypeOptions, fetcherName);
|
bool actual = baseItemManager.IsImageFetcherEnabled(item, libraryTypeOptions, fetcherName);
|
||||||
|
|
||||||
Assert.Equal(expected, actual);
|
Assert.Equal(expected, actual);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,124 +4,140 @@
|
|||||||
|
|
||||||
namespace Jellyfin.Controller.Tests
|
namespace Jellyfin.Controller.Tests
|
||||||
{
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for <see cref="DirectoryService"/>.
|
||||||
|
/// </summary>
|
||||||
public class DirectoryServiceTests
|
public class DirectoryServiceTests
|
||||||
{
|
{
|
||||||
private const string LowerCasePath = "/music/someartist";
|
private const string LowerCasePath = "/music/someartist";
|
||||||
private const string UpperCasePath = "/music/SOMEARTIST";
|
private const string UpperCasePath = "/music/SOMEARTIST";
|
||||||
|
|
||||||
private static readonly FileSystemMetadata[] _lowerCaseFileSystemMetadata =
|
private static readonly FileSystemMetadata[] LowerCaseFileSystemMetadata =
|
||||||
{
|
[
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
FullName = LowerCasePath + "/Artwork",
|
FullName = LowerCasePath + "/Artwork",
|
||||||
IsDirectory = true
|
IsDirectory = true,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
FullName = LowerCasePath + "/Some Other Folder",
|
FullName = LowerCasePath + "/Some Other Folder",
|
||||||
IsDirectory = true
|
IsDirectory = true,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
FullName = LowerCasePath + "/Song 2.mp3",
|
FullName = LowerCasePath + "/Song 2.mp3",
|
||||||
IsDirectory = false
|
IsDirectory = false,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
FullName = LowerCasePath + "/Song 3.mp3",
|
FullName = LowerCasePath + "/Song 3.mp3",
|
||||||
IsDirectory = false
|
IsDirectory = false,
|
||||||
}
|
},
|
||||||
};
|
];
|
||||||
|
|
||||||
private static readonly FileSystemMetadata[] _upperCaseFileSystemMetadata =
|
private static readonly FileSystemMetadata[] UpperCaseFileSystemMetadata =
|
||||||
{
|
[
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
FullName = UpperCasePath + "/Lyrics",
|
FullName = UpperCasePath + "/Lyrics",
|
||||||
IsDirectory = true
|
IsDirectory = true,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
FullName = UpperCasePath + "/Song 1.mp3",
|
FullName = UpperCasePath + "/Song 1.mp3",
|
||||||
IsDirectory = false
|
IsDirectory = false,
|
||||||
}
|
},
|
||||||
};
|
];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that GetFileSystemEntries caches entries for paths with different casing.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetFileSystemEntries_GivenPathsWithDifferentCasing_CachesAll()
|
public void GetFileSystemEntries_GivenPathsWithDifferentCasing_CachesAll()
|
||||||
{
|
{
|
||||||
var fileSystemMock = new Mock<IFileSystem>();
|
Mock<IFileSystem> fileSystemMock = new();
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == UpperCasePath), false)).Returns(_upperCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == UpperCasePath), false)).Returns(UpperCaseFileSystemMetadata);
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == LowerCasePath), false)).Returns(_lowerCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == LowerCasePath), false)).Returns(LowerCaseFileSystemMetadata);
|
||||||
var directoryService = new DirectoryService(fileSystemMock.Object);
|
DirectoryService directoryService = new(fileSystemMock.Object);
|
||||||
|
|
||||||
var upperCaseResult = directoryService.GetFileSystemEntries(UpperCasePath);
|
FileSystemMetadata[] upperCaseResult = directoryService.GetFileSystemEntries(UpperCasePath);
|
||||||
var lowerCaseResult = directoryService.GetFileSystemEntries(LowerCasePath);
|
FileSystemMetadata[] lowerCaseResult = directoryService.GetFileSystemEntries(LowerCasePath);
|
||||||
|
|
||||||
Assert.Equal(_upperCaseFileSystemMetadata, upperCaseResult);
|
Assert.Equal(UpperCaseFileSystemMetadata, upperCaseResult);
|
||||||
Assert.Equal(_lowerCaseFileSystemMetadata, lowerCaseResult);
|
Assert.Equal(LowerCaseFileSystemMetadata, lowerCaseResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that GetFiles returns correct files for paths with different casing.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetFiles_GivenPathsWithDifferentCasing_ReturnsCorrectFiles()
|
public void GetFiles_GivenPathsWithDifferentCasing_ReturnsCorrectFiles()
|
||||||
{
|
{
|
||||||
var fileSystemMock = new Mock<IFileSystem>();
|
Mock<IFileSystem> fileSystemMock = new();
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == UpperCasePath), false)).Returns(_upperCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == UpperCasePath), false)).Returns(UpperCaseFileSystemMetadata);
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == LowerCasePath), false)).Returns(_lowerCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == LowerCasePath), false)).Returns(LowerCaseFileSystemMetadata);
|
||||||
var directoryService = new DirectoryService(fileSystemMock.Object);
|
DirectoryService directoryService = new(fileSystemMock.Object);
|
||||||
|
|
||||||
var upperCaseResult = directoryService.GetFiles(UpperCasePath);
|
List<FileSystemMetadata> upperCaseResult = directoryService.GetFiles(UpperCasePath);
|
||||||
var lowerCaseResult = directoryService.GetFiles(LowerCasePath);
|
List<FileSystemMetadata> lowerCaseResult = directoryService.GetFiles(LowerCasePath);
|
||||||
|
|
||||||
Assert.Equal(_upperCaseFileSystemMetadata.Where(f => !f.IsDirectory), upperCaseResult);
|
Assert.Equal(UpperCaseFileSystemMetadata.Where(f => !f.IsDirectory), upperCaseResult);
|
||||||
Assert.Equal(_lowerCaseFileSystemMetadata.Where(f => !f.IsDirectory), lowerCaseResult);
|
Assert.Equal(LowerCaseFileSystemMetadata.Where(f => !f.IsDirectory), lowerCaseResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that GetDirectories returns correct directories for paths with different casing.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDirectories_GivenPathsWithDifferentCasing_ReturnsCorrectDirectories()
|
public void GetDirectories_GivenPathsWithDifferentCasing_ReturnsCorrectDirectories()
|
||||||
{
|
{
|
||||||
var fileSystemMock = new Mock<IFileSystem>();
|
Mock<IFileSystem> fileSystemMock = new();
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == UpperCasePath), false)).Returns(_upperCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == UpperCasePath), false)).Returns(UpperCaseFileSystemMetadata);
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == LowerCasePath), false)).Returns(_lowerCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemEntries(It.Is<string>(x => x == LowerCasePath), false)).Returns(LowerCaseFileSystemMetadata);
|
||||||
var directoryService = new DirectoryService(fileSystemMock.Object);
|
DirectoryService directoryService = new(fileSystemMock.Object);
|
||||||
|
|
||||||
var upperCaseResult = directoryService.GetDirectories(UpperCasePath);
|
List<FileSystemMetadata> upperCaseResult = directoryService.GetDirectories(UpperCasePath);
|
||||||
var lowerCaseResult = directoryService.GetDirectories(LowerCasePath);
|
List<FileSystemMetadata> lowerCaseResult = directoryService.GetDirectories(LowerCasePath);
|
||||||
|
|
||||||
Assert.Equal(_upperCaseFileSystemMetadata.Where(f => f.IsDirectory), upperCaseResult);
|
Assert.Equal(UpperCaseFileSystemMetadata.Where(f => f.IsDirectory), upperCaseResult);
|
||||||
Assert.Equal(_lowerCaseFileSystemMetadata.Where(f => f.IsDirectory), lowerCaseResult);
|
Assert.Equal(LowerCaseFileSystemMetadata.Where(f => f.IsDirectory), lowerCaseResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that GetFile returns correct file for paths with different casing.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetFile_GivenFilePathsWithDifferentCasing_ReturnsCorrectFile()
|
public void GetFile_GivenFilePathsWithDifferentCasing_ReturnsCorrectFile()
|
||||||
{
|
{
|
||||||
const string lowerCasePath = "/music/someartist/song 1.mp3";
|
const string lowerCasePath = "/music/someartist/song 1.mp3";
|
||||||
var lowerCaseFileSystemMetadata = new FileSystemMetadata
|
FileSystemMetadata lowerCaseFileSystemMetadata = new()
|
||||||
{
|
{
|
||||||
FullName = lowerCasePath,
|
FullName = lowerCasePath,
|
||||||
Exists = true
|
Exists = true,
|
||||||
};
|
};
|
||||||
const string upperCasePath = "/music/SOMEARTIST/SONG 1.mp3";
|
const string upperCasePath = "/music/SOMEARTIST/SONG 1.mp3";
|
||||||
var upperCaseFileSystemMetadata = new FileSystemMetadata
|
FileSystemMetadata upperCaseFileSystemMetadata = new()
|
||||||
{
|
{
|
||||||
FullName = upperCasePath,
|
FullName = upperCasePath,
|
||||||
Exists = false
|
Exists = false,
|
||||||
};
|
};
|
||||||
var fileSystemMock = new Mock<IFileSystem>();
|
Mock<IFileSystem> fileSystemMock = new();
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata);
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata);
|
||||||
var directoryService = new DirectoryService(fileSystemMock.Object);
|
DirectoryService directoryService = new(fileSystemMock.Object);
|
||||||
|
|
||||||
var lowerCaseDirResult = directoryService.GetDirectory(lowerCasePath);
|
FileSystemMetadata? lowerCaseDirResult = directoryService.GetDirectory(lowerCasePath);
|
||||||
var lowerCaseFileResult = directoryService.GetFile(lowerCasePath);
|
FileSystemMetadata? lowerCaseFileResult = directoryService.GetFile(lowerCasePath);
|
||||||
var upperCaseDirResult = directoryService.GetDirectory(upperCasePath);
|
FileSystemMetadata? upperCaseDirResult = directoryService.GetDirectory(upperCasePath);
|
||||||
var upperCaseFileResult = directoryService.GetFile(upperCasePath);
|
FileSystemMetadata? upperCaseFileResult = directoryService.GetFile(upperCasePath);
|
||||||
|
|
||||||
Assert.Null(lowerCaseDirResult);
|
Assert.Null(lowerCaseDirResult);
|
||||||
Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseFileResult);
|
Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseFileResult);
|
||||||
@@ -129,32 +145,35 @@ namespace Jellyfin.Controller.Tests
|
|||||||
Assert.Null(upperCaseFileResult);
|
Assert.Null(upperCaseFileResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that GetDirectory returns correct directory for paths with different casing.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDirectory_GivenFilePathsWithDifferentCasing_ReturnsCorrectDirectory()
|
public void GetDirectory_GivenFilePathsWithDifferentCasing_ReturnsCorrectDirectory()
|
||||||
{
|
{
|
||||||
const string lowerCasePath = "/music/someartist/Lyrics";
|
const string lowerCasePath = "/music/someartist/Lyrics";
|
||||||
var lowerCaseFileSystemMetadata = new FileSystemMetadata
|
FileSystemMetadata lowerCaseFileSystemMetadata = new()
|
||||||
{
|
{
|
||||||
FullName = lowerCasePath,
|
FullName = lowerCasePath,
|
||||||
IsDirectory = true,
|
IsDirectory = true,
|
||||||
Exists = true
|
Exists = true,
|
||||||
};
|
};
|
||||||
const string upperCasePath = "/music/SOMEARTIST/LYRICS";
|
const string upperCasePath = "/music/SOMEARTIST/LYRICS";
|
||||||
var upperCaseFileSystemMetadata = new FileSystemMetadata
|
FileSystemMetadata upperCaseFileSystemMetadata = new()
|
||||||
{
|
{
|
||||||
FullName = upperCasePath,
|
FullName = upperCasePath,
|
||||||
IsDirectory = true,
|
IsDirectory = true,
|
||||||
Exists = false
|
Exists = false,
|
||||||
};
|
};
|
||||||
var fileSystemMock = new Mock<IFileSystem>();
|
Mock<IFileSystem> fileSystemMock = new();
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == upperCasePath))).Returns(upperCaseFileSystemMetadata);
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == lowerCasePath))).Returns(lowerCaseFileSystemMetadata);
|
||||||
var directoryService = new DirectoryService(fileSystemMock.Object);
|
DirectoryService directoryService = new(fileSystemMock.Object);
|
||||||
|
|
||||||
var lowerCaseDirResult = directoryService.GetDirectory(lowerCasePath);
|
FileSystemMetadata? lowerCaseDirResult = directoryService.GetDirectory(lowerCasePath);
|
||||||
var lowerCaseFileResult = directoryService.GetFile(lowerCasePath);
|
FileSystemMetadata? lowerCaseFileResult = directoryService.GetFile(lowerCasePath);
|
||||||
var upperCaseDirResult = directoryService.GetDirectory(upperCasePath);
|
FileSystemMetadata? upperCaseDirResult = directoryService.GetDirectory(upperCasePath);
|
||||||
var upperCaseFileResult = directoryService.GetFile(upperCasePath);
|
FileSystemMetadata? upperCaseFileResult = directoryService.GetFile(upperCasePath);
|
||||||
|
|
||||||
Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseDirResult);
|
Assert.Equal(lowerCaseFileSystemMetadata, lowerCaseDirResult);
|
||||||
Assert.Null(lowerCaseFileResult);
|
Assert.Null(lowerCaseFileResult);
|
||||||
@@ -162,92 +181,101 @@ namespace Jellyfin.Controller.Tests
|
|||||||
Assert.Null(upperCaseFileResult);
|
Assert.Null(upperCaseFileResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that GetFile returns cached file when the path is already cached.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetFile_GivenCachedPath_ReturnsCachedFile()
|
public void GetFile_GivenCachedPath_ReturnsCachedFile()
|
||||||
{
|
{
|
||||||
const string path = "/music/someartist/song 1.mp3";
|
const string path = "/music/someartist/song 1.mp3";
|
||||||
var cachedFileSystemMetadata = new FileSystemMetadata
|
FileSystemMetadata cachedFileSystemMetadata = new()
|
||||||
{
|
{
|
||||||
FullName = path,
|
FullName = path,
|
||||||
Exists = true
|
Exists = true,
|
||||||
};
|
};
|
||||||
var newFileSystemMetadata = new FileSystemMetadata
|
FileSystemMetadata newFileSystemMetadata = new()
|
||||||
{
|
{
|
||||||
FullName = "/music/SOMEARTIST/song 1.mp3",
|
FullName = "/music/SOMEARTIST/song 1.mp3",
|
||||||
Exists = true
|
Exists = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var fileSystemMock = new Mock<IFileSystem>();
|
Mock<IFileSystem> fileSystemMock = new();
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == path))).Returns(cachedFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == path))).Returns(cachedFileSystemMetadata);
|
||||||
var directoryService = new DirectoryService(fileSystemMock.Object);
|
DirectoryService directoryService = new(fileSystemMock.Object);
|
||||||
|
|
||||||
var result = directoryService.GetFile(path);
|
FileSystemMetadata? result = directoryService.GetFile(path);
|
||||||
fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == path))).Returns(newFileSystemMetadata);
|
_ = fileSystemMock.Setup(f => f.GetFileSystemInfo(It.Is<string>(x => x == path))).Returns(newFileSystemMetadata);
|
||||||
var secondResult = directoryService.GetFile(path);
|
FileSystemMetadata? secondResult = directoryService.GetFile(path);
|
||||||
|
|
||||||
Assert.Equivalent(cachedFileSystemMetadata, result);
|
Assert.Equivalent(cachedFileSystemMetadata, result);
|
||||||
Assert.Equivalent(cachedFileSystemMetadata, secondResult);
|
Assert.Equivalent(cachedFileSystemMetadata, secondResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that GetFilePaths returns only cached paths when clear is not called.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetFilePaths_GivenCachedFilePathWithoutClear_ReturnsOnlyCachedPaths()
|
public void GetFilePaths_GivenCachedFilePathWithoutClear_ReturnsOnlyCachedPaths()
|
||||||
{
|
{
|
||||||
const string path = "/music/someartist";
|
const string path = "/music/someartist";
|
||||||
|
|
||||||
var cachedPaths = new[]
|
string[] cachedPaths =
|
||||||
{
|
[
|
||||||
"/music/someartist/song 1.mp3",
|
"/music/someartist/song 1.mp3",
|
||||||
"/music/someartist/song 2.mp3",
|
"/music/someartist/song 2.mp3",
|
||||||
"/music/someartist/song 3.mp3",
|
"/music/someartist/song 3.mp3",
|
||||||
"/music/someartist/song 4.mp3",
|
"/music/someartist/song 4.mp3",
|
||||||
};
|
];
|
||||||
var newPaths = new[]
|
string[] newPaths =
|
||||||
{
|
[
|
||||||
"/music/someartist/song 5.mp3",
|
"/music/someartist/song 5.mp3",
|
||||||
"/music/someartist/song 6.mp3",
|
"/music/someartist/song 6.mp3",
|
||||||
"/music/someartist/song 7.mp3",
|
"/music/someartist/song 7.mp3",
|
||||||
"/music/someartist/song 8.mp3",
|
"/music/someartist/song 8.mp3",
|
||||||
};
|
];
|
||||||
|
|
||||||
var fileSystemMock = new Mock<IFileSystem>();
|
Mock<IFileSystem> fileSystemMock = new();
|
||||||
fileSystemMock.Setup(f => f.GetFilePaths(It.Is<string>(x => x == path), false)).Returns(cachedPaths);
|
_ = fileSystemMock.Setup(f => f.GetFilePaths(It.Is<string>(x => x == path), false)).Returns(cachedPaths);
|
||||||
var directoryService = new DirectoryService(fileSystemMock.Object);
|
DirectoryService directoryService = new(fileSystemMock.Object);
|
||||||
|
|
||||||
var result = directoryService.GetFilePaths(path);
|
IReadOnlyList<string> result = directoryService.GetFilePaths(path);
|
||||||
fileSystemMock.Setup(f => f.GetFilePaths(It.Is<string>(x => x == path), false)).Returns(newPaths);
|
_ = fileSystemMock.Setup(f => f.GetFilePaths(It.Is<string>(x => x == path), false)).Returns(newPaths);
|
||||||
var secondResult = directoryService.GetFilePaths(path);
|
IReadOnlyList<string> secondResult = directoryService.GetFilePaths(path);
|
||||||
|
|
||||||
Assert.Equal(cachedPaths, result);
|
Assert.Equal(cachedPaths, result);
|
||||||
Assert.Equal(cachedPaths, secondResult);
|
Assert.Equal(cachedPaths, secondResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that GetFilePaths returns new paths when clear is called.
|
||||||
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetFilePaths_GivenCachedFilePathWithClear_ReturnsNewPaths()
|
public void GetFilePaths_GivenCachedFilePathWithClear_ReturnsNewPaths()
|
||||||
{
|
{
|
||||||
const string path = "/music/someartist";
|
const string path = "/music/someartist";
|
||||||
|
|
||||||
var cachedPaths = new[]
|
string[] cachedPaths =
|
||||||
{
|
[
|
||||||
"/music/someartist/song 1.mp3",
|
"/music/someartist/song 1.mp3",
|
||||||
"/music/someartist/song 2.mp3",
|
"/music/someartist/song 2.mp3",
|
||||||
"/music/someartist/song 3.mp3",
|
"/music/someartist/song 3.mp3",
|
||||||
"/music/someartist/song 4.mp3",
|
"/music/someartist/song 4.mp3",
|
||||||
};
|
];
|
||||||
var newPaths = new[]
|
string[] newPaths =
|
||||||
{
|
[
|
||||||
"/music/someartist/song 5.mp3",
|
"/music/someartist/song 5.mp3",
|
||||||
"/music/someartist/song 6.mp3",
|
"/music/someartist/song 6.mp3",
|
||||||
"/music/someartist/song 7.mp3",
|
"/music/someartist/song 7.mp3",
|
||||||
"/music/someartist/song 8.mp3",
|
"/music/someartist/song 8.mp3",
|
||||||
};
|
];
|
||||||
|
|
||||||
var fileSystemMock = new Mock<IFileSystem>();
|
Mock<IFileSystem> fileSystemMock = new();
|
||||||
fileSystemMock.Setup(f => f.GetFilePaths(It.Is<string>(x => x == path), false)).Returns(cachedPaths);
|
_ = fileSystemMock.Setup(f => f.GetFilePaths(It.Is<string>(x => x == path), false)).Returns(cachedPaths);
|
||||||
var directoryService = new DirectoryService(fileSystemMock.Object);
|
DirectoryService directoryService = new(fileSystemMock.Object);
|
||||||
|
|
||||||
var result = directoryService.GetFilePaths(path);
|
IReadOnlyList<string> result = directoryService.GetFilePaths(path);
|
||||||
fileSystemMock.Setup(f => f.GetFilePaths(It.Is<string>(x => x == path), false)).Returns(newPaths);
|
_ = fileSystemMock.Setup(f => f.GetFilePaths(It.Is<string>(x => x == path), false)).Returns(newPaths);
|
||||||
var secondResult = directoryService.GetFilePaths(path, true);
|
IReadOnlyList<string> secondResult = directoryService.GetFilePaths(path, true);
|
||||||
|
|
||||||
Assert.Equal(cachedPaths, result);
|
Assert.Equal(cachedPaths, result);
|
||||||
Assert.Equal(newPaths, secondResult);
|
Assert.Equal(newPaths, secondResult);
|
||||||
|
|||||||
@@ -2,49 +2,67 @@
|
|||||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||||
// </copyright>
|
// </copyright>
|
||||||
|
|
||||||
namespace Jellyfin.Controller.Tests.Entities;
|
namespace Jellyfin.Controller.Tests.Entities
|
||||||
|
|
||||||
using MediaBrowser.Controller.Entities;
|
|
||||||
using MediaBrowser.Controller.Library;
|
|
||||||
using MediaBrowser.Model.MediaInfo;
|
|
||||||
using Moq;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
public class BaseItemTests
|
|
||||||
{
|
{
|
||||||
[Theory]
|
using MediaBrowser.Controller.Entities;
|
||||||
[InlineData("", "")]
|
using MediaBrowser.Controller.Library;
|
||||||
[InlineData("1", "0000000001")]
|
using MediaBrowser.Model.MediaInfo;
|
||||||
[InlineData("t", "t")]
|
using Moq;
|
||||||
[InlineData("test", "test")]
|
using Xunit;
|
||||||
[InlineData("test1", "test0000000001")]
|
|
||||||
[InlineData("1test 2", "0000000001test 0000000002")]
|
|
||||||
public void BaseItem_ModifySortChunks_Valid(string input, string expected)
|
|
||||||
=> Assert.Equal(expected, BaseItem.ModifySortChunks(input));
|
|
||||||
|
|
||||||
[Theory]
|
/// <summary>
|
||||||
[InlineData("/Movies/Ted/Ted.mp4", "/Movies/Ted/Ted - Unrated Edition.mp4", "Ted", "Unrated Edition")]
|
/// Tests for <see cref="BaseItem"/>.
|
||||||
[InlineData("/Movies/Deadpool 2 (2018)/Deadpool 2 (2018).mkv", "/Movies/Deadpool 2 (2018)/Deadpool 2 (2018) - Super Duper Cut.mkv", "Deadpool 2 (2018)", "Super Duper Cut")]
|
/// </summary>
|
||||||
public void GetMediaSourceName_Valid(string primaryPath, string altPath, string name, string altName)
|
public class BaseItemTests
|
||||||
{
|
{
|
||||||
var mediaSourceManager = new Mock<IMediaSourceManager>();
|
/// <summary>
|
||||||
mediaSourceManager.Setup(x => x.GetPathProtocol(It.IsAny<string>()))
|
/// Tests that ModifySortChunks correctly modifies sort chunks.
|
||||||
.Returns((string x) => MediaProtocol.File);
|
/// </summary>
|
||||||
BaseItem.MediaSourceManager = mediaSourceManager.Object;
|
/// <param name="input">The input string.</param>
|
||||||
|
/// <param name="expected">The expected output string.</param>
|
||||||
var video = new Video()
|
[Theory]
|
||||||
|
[InlineData("", "")]
|
||||||
|
[InlineData("1", "0000000001")]
|
||||||
|
[InlineData("t", "t")]
|
||||||
|
[InlineData("test", "test")]
|
||||||
|
[InlineData("test1", "test0000000001")]
|
||||||
|
[InlineData("1test 2", "0000000001test 0000000002")]
|
||||||
|
public void BaseItem_ModifySortChunks_Valid(string input, string expected)
|
||||||
{
|
{
|
||||||
Path = primaryPath
|
Assert.Equal(expected, BaseItem.ModifySortChunks(input));
|
||||||
};
|
}
|
||||||
|
|
||||||
var videoAlt = new Video()
|
/// <summary>
|
||||||
|
/// Tests that GetMediaSourceName returns the correct name for media sources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="primaryPath">The primary path.</param>
|
||||||
|
/// <param name="altPath">The alternate path.</param>
|
||||||
|
/// <param name="name">The expected name for the primary path.</param>
|
||||||
|
/// <param name="altName">The expected name for the alternate path.</param>
|
||||||
|
[Theory]
|
||||||
|
[InlineData("/Movies/Ted/Ted.mp4", "/Movies/Ted/Ted - Unrated Edition.mp4", "Ted", "Unrated Edition")]
|
||||||
|
[InlineData("/Movies/Deadpool 2 (2018)/Deadpool 2 (2018).mkv", "/Movies/Deadpool 2 (2018)/Deadpool 2 (2018) - Super Duper Cut.mkv", "Deadpool 2 (2018)", "Super Duper Cut")]
|
||||||
|
public void GetMediaSourceName_Valid(string primaryPath, string altPath, string name, string altName)
|
||||||
{
|
{
|
||||||
Path = altPath,
|
Mock<IMediaSourceManager> mediaSourceManager = new();
|
||||||
};
|
_ = mediaSourceManager.Setup(x => x.GetPathProtocol(It.IsAny<string>()))
|
||||||
|
.Returns((string x) => MediaProtocol.File);
|
||||||
|
BaseItem.MediaSourceManager = mediaSourceManager.Object;
|
||||||
|
|
||||||
video.LocalAlternateVersions = [videoAlt.Path];
|
Video video = new()
|
||||||
|
{
|
||||||
|
Path = primaryPath,
|
||||||
|
};
|
||||||
|
|
||||||
Assert.Equal(name, video.GetMediaSourceName(video));
|
Video videoAlt = new()
|
||||||
Assert.Equal(altName, video.GetMediaSourceName(videoAlt));
|
{
|
||||||
|
Path = altPath,
|
||||||
|
};
|
||||||
|
|
||||||
|
video.LocalAlternateVersions = [videoAlt.Path];
|
||||||
|
|
||||||
|
Assert.Equal(name, video.GetMediaSourceName(video));
|
||||||
|
Assert.Equal(altName, video.GetMediaSourceName(videoAlt));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ProjectGuid>{462584F7-5023-4019-9EAC-B98CA458C0A0}</ProjectGuid>
|
<ProjectGuid>{462584F7-5023-4019-9EAC-B98CA458C0A0}</ProjectGuid>
|
||||||
<TargetFramework>net11.0</TargetFramework>
|
<TargetFramework>net11.0</TargetFramework>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user