using System; using System.Globalization; using System.IO; using Jellyfin.Drawing; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; namespace Jellyfin.Server.Integration.Tests; public sealed class ImageProcessorTests : IDisposable { private const string CacheRoot = "image-cache"; private const string OriginalPath = "/media/poster.jpg"; private const string NoOverlayCacheKey = "/media/poster.jpg,quality=90,datemodified=638800000000000000,f=Jpg,width=200,height=300,maxwidth=400,maxheight=500,fillwidth=600,fillheight=700,blur=2,b=000000,fl=layer,v=4"; private static readonly DateTime _dateModified = new(638800000000000000, DateTimeKind.Utc); private readonly ImageProcessor _imageProcessor; public ImageProcessorTests() { var applicationPaths = new Mock(); applicationPaths.SetupGet(paths => paths.ImageCachePath).Returns(CacheRoot); var configurationManager = new Mock(); configurationManager .SetupGet(manager => manager.Configuration) .Returns(new ServerConfiguration { ParallelImageEncodingLimit = 1 }); _imageProcessor = new ImageProcessor( NullLogger.Instance, applicationPaths.Object, Mock.Of(), Mock.Of(), configurationManager.Object); } [Fact] public void GetCacheFilePath_DifferentOverlayTypes_ReturnDifferentPaths() { var percentPlayedPath = GetCacheFilePath(percentPlayed: 1); var unwatchedCountPath = GetCacheFilePath(unwatchedCount: 1); Assert.NotEqual(percentPlayedPath, unwatchedCountPath); } [Fact] public void GetCacheFilePath_DifferentPercentPlayedValues_ReturnDifferentPaths() { var firstPath = GetCacheFilePath(percentPlayed: 12.5); var secondPath = GetCacheFilePath(percentPlayed: 75.5); Assert.NotEqual(firstPath, secondPath); } [Fact] public void GetCacheFilePath_DifferentUnwatchedCountValues_ReturnDifferentPaths() { var firstPath = GetCacheFilePath(unwatchedCount: 1); var secondPath = GetCacheFilePath(unwatchedCount: 2); Assert.NotEqual(firstPath, secondPath); } [Fact] public void GetCacheFilePath_DifferentCultures_ReturnSamePath() { var originalCulture = CultureInfo.CurrentCulture; try { CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US"); var expectedPath = GetCacheFilePath(percentPlayed: 12.5); CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("fr-FR"); var actualPath = GetCacheFilePath(percentPlayed: 12.5); Assert.Equal(expectedPath, actualPath); } finally { CultureInfo.CurrentCulture = originalCulture; } } [Fact] public void GetCacheFilePath_NoOverlay_UsesVersionFourWithExistingSerialization() { var expectedPath = _imageProcessor.GetCachePath( Path.Combine(CacheRoot, "resized-images"), NoOverlayCacheKey, ".jpg"); Assert.Equal(expectedPath, GetCacheFilePath()); } public void Dispose() { _imageProcessor.Dispose(); } private string GetCacheFilePath(double percentPlayed = 0, int? unwatchedCount = null) { var options = new ImageProcessingOptions { Width = 200, Height = 300, MaxWidth = 400, MaxHeight = 500, FillWidth = 600, FillHeight = 700, Quality = 90, PercentPlayed = percentPlayed, UnplayedCount = unwatchedCount, Blur = 2, BackgroundColor = "000000", ForegroundLayer = "layer" }; return _imageProcessor.GetCacheFilePath( OriginalPath, _dateModified, ImageFormat.Jpg, options); } }