123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649 |
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading;
- using System.Threading.Tasks;
- using MediaBrowser.Controller.Entities;
- using MediaBrowser.Controller.Library;
- using MediaBrowser.Controller.Providers;
- using MediaBrowser.Model.Configuration;
- using MediaBrowser.Model.Drawing;
- using MediaBrowser.Model.Entities;
- using MediaBrowser.Model.IO;
- using MediaBrowser.Model.MediaInfo;
- using MediaBrowser.Model.Providers;
- using MediaBrowser.Providers.Manager;
- using Microsoft.Extensions.Logging.Abstractions;
- using Moq;
- using Xunit;
- namespace Jellyfin.Providers.Tests.Manager
- {
- public partial class ItemImageProviderTests
- {
- private const string TestDataImagePath = "Test Data/Images/blank{0}.jpg";
- [GeneratedRegex("[0-9]+")]
- private static partial Regex NumbersRegex();
- [Fact]
- public void ValidateImages_PhotoEmptyProviders_NoChange()
- {
- var itemImageProvider = GetItemImageProvider(null, null);
- var changed = itemImageProvider.ValidateImages(new Photo(), Enumerable.Empty<ILocalImageProvider>(), null);
- Assert.False(changed);
- }
- [Fact]
- public void ValidateImages_EmptyItemEmptyProviders_NoChange()
- {
- ValidateImages_Test(ImageType.Primary, 0, true, 0, false, 0);
- }
- public static TheoryData<ImageType, int> GetImageTypesWithCount()
- {
- var theoryTypes = new TheoryData<ImageType, int>
- {
- // minimal test cases that hit different handling
- { ImageType.Primary, 1 },
- { ImageType.Backdrop, 2 }
- };
- return theoryTypes;
- }
- [Theory]
- [MemberData(nameof(GetImageTypesWithCount))]
- public void ValidateImages_EmptyItemAndPopulatedProviders_AddsImages(ImageType imageType, int imageCount)
- {
- ValidateImages_Test(imageType, 0, true, imageCount, true, imageCount);
- }
- [Theory]
- [MemberData(nameof(GetImageTypesWithCount))]
- public void ValidateImages_PopulatedItemWithGoodPathsAndEmptyProviders_NoChange(ImageType imageType, int imageCount)
- {
- ValidateImages_Test(imageType, imageCount, true, 0, false, imageCount);
- }
- [Theory]
- [MemberData(nameof(GetImageTypesWithCount))]
- public void ValidateImages_PopulatedItemWithBadPathsAndEmptyProviders_RemovesImage(ImageType imageType, int imageCount)
- {
- ValidateImages_Test(imageType, imageCount, false, 0, true, 0);
- }
- private void ValidateImages_Test(ImageType imageType, int initialImageCount, bool initialPathsValid, int providerImageCount, bool expectedChange, int expectedImageCount)
- {
- var item = GetItemWithImages(imageType, initialImageCount, initialPathsValid);
- var imageProvider = GetImageProvider(imageType, providerImageCount, true);
- var itemImageProvider = GetItemImageProvider(null, null);
- var actualChange = itemImageProvider.ValidateImages(item, new[] { imageProvider }, null);
- Assert.Equal(expectedChange, actualChange);
- Assert.Equal(expectedImageCount, item.GetImages(imageType).Count());
- }
- [Fact]
- public void MergeImages_EmptyItemNewImagesEmpty_NoChange()
- {
- var itemImageProvider = GetItemImageProvider(null, null);
- var changed = itemImageProvider.MergeImages(new Video(), Array.Empty<LocalImageInfo>(), new ImageRefreshOptions(Mock.Of<IDirectoryService>()));
- Assert.False(changed);
- }
- [Theory]
- [MemberData(nameof(GetImageTypesWithCount))]
- public void MergeImages_PopulatedItemWithGoodPathsAndPopulatedNewImages_AddsUpdatesImages(ImageType imageType, int imageCount)
- {
- // valid and not valid paths - should replace the valid paths with the invalid ones
- var item = GetItemWithImages(imageType, imageCount, true);
- var images = GetImages(imageType, imageCount, false);
- var itemImageProvider = GetItemImageProvider(null, null);
- var changed = itemImageProvider.MergeImages(item, images, new ImageRefreshOptions(Mock.Of<IDirectoryService>()));
- Assert.True(changed);
- // adds for types that allow multiple, replaces singular type images
- if (item.AllowsMultipleImages(imageType))
- {
- Assert.Equal(imageCount * 2, item.GetImages(imageType).Count());
- }
- else
- {
- Assert.Single(item.GetImages(imageType));
- Assert.Same(images[0].FileInfo.FullName, item.GetImages(imageType).First().Path);
- }
- }
- [Theory]
- [InlineData(ImageType.Primary, 1, false)]
- [InlineData(ImageType.Backdrop, 2, false)]
- [InlineData(ImageType.Primary, 1, true)]
- [InlineData(ImageType.Backdrop, 2, true)]
- public void MergeImages_PopulatedItemWithGoodPathsAndSameNewImages_ResetIfTimeChanges(ImageType imageType, int imageCount, bool updateTime)
- {
- var oldTime = new DateTime(1970, 1, 1);
- var updatedTime = updateTime ? new DateTime(2021, 1, 1) : oldTime;
- var fileSystem = new Mock<IFileSystem>();
- fileSystem.Setup(fs => fs.GetLastWriteTimeUtc(It.IsAny<FileSystemMetadata>()))
- .Returns(updatedTime);
- BaseItem.FileSystem = fileSystem.Object;
- // all valid paths - matching for strictly updating
- var item = GetItemWithImages(imageType, imageCount, true);
- // set size to non-zero to allow for image size reset to occur
- foreach (var image in item.GetImages(imageType))
- {
- image.DateModified = oldTime;
- image.Height = 1;
- image.Width = 1;
- }
- var images = GetImages(imageType, imageCount, true);
- var itemImageProvider = GetItemImageProvider(null, fileSystem);
- var changed = itemImageProvider.MergeImages(item, images, new ImageRefreshOptions(Mock.Of<IDirectoryService>()));
- if (updateTime)
- {
- Assert.True(changed);
- // before and after paths are the same, verify updated by size reset to 0
- var typedImages = item.GetImages(imageType).ToArray();
- Assert.Equal(imageCount, typedImages.Length);
- foreach (var image in typedImages)
- {
- Assert.Equal(updatedTime, image.DateModified);
- Assert.Equal(0, image.Height);
- Assert.Equal(0, image.Width);
- }
- }
- else
- {
- Assert.False(changed);
- }
- }
- [Theory]
- [InlineData(ImageType.Primary, 0)]
- [InlineData(ImageType.Primary, 1)]
- [InlineData(ImageType.Backdrop, 2)]
- public void RemoveImages_DeletesImages_WhenFound(ImageType imageType, int imageCount)
- {
- var item = GetItemWithImages(imageType, imageCount, false);
- var mockFileSystem = new Mock<IFileSystem>(MockBehavior.Strict);
- if (imageCount > 0)
- {
- mockFileSystem.Setup(fs => fs.DeleteFile("invalid path 0"))
- .Verifiable();
- }
- if (imageCount > 1)
- {
- mockFileSystem.Setup(fs => fs.DeleteFile("invalid path 1"))
- .Verifiable();
- }
- var itemImageProvider = GetItemImageProvider(Mock.Of<IProviderManager>(), mockFileSystem);
- var result = itemImageProvider.RemoveImages(item);
- Assert.Equal(imageCount != 0, result);
- Assert.Empty(item.GetImages(imageType));
- mockFileSystem.Verify();
- }
- [Theory]
- [InlineData(ImageType.Primary, 1, false)]
- [InlineData(ImageType.Backdrop, 2, false)]
- [InlineData(ImageType.Primary, 1, true)]
- [InlineData(ImageType.Backdrop, 2, true)]
- public async void RefreshImages_PopulatedItemPopulatedProviderDynamic_UpdatesImagesIfForced(ImageType imageType, int imageCount, bool forceRefresh)
- {
- var item = GetItemWithImages(imageType, imageCount, false);
- var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
- var imageResponse = new DynamicImageResponse
- {
- HasImage = true,
- Format = ImageFormat.Jpg,
- Path = "url path",
- Protocol = MediaProtocol.Http
- };
- var dynamicProvider = new Mock<IDynamicImageProvider>(MockBehavior.Strict);
- dynamicProvider.Setup(rp => rp.Name).Returns("MockDynamicProvider");
- dynamicProvider.Setup(rp => rp.GetSupportedImages(item))
- .Returns(new[] { imageType });
- dynamicProvider.Setup(rp => rp.GetImage(item, imageType, It.IsAny<CancellationToken>()))
- .ReturnsAsync(imageResponse);
- var refreshOptions = forceRefresh
- ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
- {
- ImageRefreshMode = MetadataRefreshMode.FullRefresh,
- ReplaceAllImages = true
- }
- : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
- var itemImageProvider = GetItemImageProvider(null, new Mock<IFileSystem>());
- var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { dynamicProvider.Object }, refreshOptions, CancellationToken.None);
- Assert.Equal(forceRefresh, result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
- if (forceRefresh)
- {
- // replaces multi-types
- Assert.Single(item.GetImages(imageType));
- }
- else
- {
- // adds to multi-types if room
- Assert.Equal(imageCount, item.GetImages(imageType).Count());
- }
- }
- [Theory]
- [InlineData(ImageType.Primary, 1, true, MediaProtocol.Http)]
- [InlineData(ImageType.Backdrop, 2, true, MediaProtocol.Http)]
- [InlineData(ImageType.Primary, 1, true, MediaProtocol.File)]
- [InlineData(ImageType.Backdrop, 2, true, MediaProtocol.File)]
- [InlineData(ImageType.Primary, 1, false, MediaProtocol.File)]
- [InlineData(ImageType.Backdrop, 2, false, MediaProtocol.File)]
- public async void RefreshImages_EmptyItemPopulatedProviderDynamic_AddsImages(ImageType imageType, int imageCount, bool responseHasPath, MediaProtocol protocol)
- {
- // Has to exist for querying DateModified time on file, results stored but not checked so not populating
- BaseItem.FileSystem = Mock.Of<IFileSystem>();
- var item = new Video();
- var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
- // Path must exist if set: is read in as a stream by AsyncFile.OpenRead
- var imageResponse = new DynamicImageResponse
- {
- HasImage = true,
- Format = ImageFormat.Jpg,
- Path = responseHasPath ? string.Format(CultureInfo.InvariantCulture, TestDataImagePath, 0) : null,
- Protocol = protocol
- };
- var dynamicProvider = new Mock<IDynamicImageProvider>(MockBehavior.Strict);
- dynamicProvider.Setup(rp => rp.Name).Returns("MockDynamicProvider");
- dynamicProvider.Setup(rp => rp.GetSupportedImages(item))
- .Returns(new[] { imageType });
- dynamicProvider.Setup(rp => rp.GetImage(item, imageType, It.IsAny<CancellationToken>()))
- .ReturnsAsync(imageResponse);
- var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
- var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
- providerManager.Setup(pm => pm.SaveImage(item, It.IsAny<Stream>(), It.IsAny<string>(), imageType, null, It.IsAny<CancellationToken>()))
- .Callback<BaseItem, Stream, string, ImageType, int?, CancellationToken>((callbackItem, _, _, callbackType, _, _) => callbackItem.SetImagePath(callbackType, 0, new FileSystemMetadata()))
- .Returns(Task.CompletedTask);
- var itemImageProvider = GetItemImageProvider(providerManager.Object, null);
- var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { dynamicProvider.Object }, refreshOptions, CancellationToken.None);
- Assert.True(result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
- // dynamic provider unable to return multiple images
- Assert.Single(item.GetImages(imageType));
- if (protocol == MediaProtocol.Http)
- {
- Assert.Equal(imageResponse.Path, item.GetImagePath(imageType, 0));
- }
- }
- [Theory]
- [InlineData(ImageType.Primary, 1, false)]
- [InlineData(ImageType.Backdrop, 1, false)]
- [InlineData(ImageType.Backdrop, 2, false)]
- [InlineData(ImageType.Primary, 1, true)]
- [InlineData(ImageType.Backdrop, 1, true)]
- [InlineData(ImageType.Backdrop, 2, true)]
- public async void RefreshImages_PopulatedItemPopulatedProviderRemote_UpdatesImagesIfForced(ImageType imageType, int imageCount, bool forceRefresh)
- {
- var item = GetItemWithImages(imageType, imageCount, false);
- var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
- var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
- remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
- remoteProvider.Setup(rp => rp.GetSupportedImages(item))
- .Returns(new[] { imageType });
- var refreshOptions = forceRefresh
- ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
- {
- ImageRefreshMode = MetadataRefreshMode.FullRefresh,
- ReplaceAllImages = true
- }
- : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
- var remoteInfo = new RemoteImageInfo[imageCount];
- for (int i = 0; i < imageCount; i++)
- {
- remoteInfo[i] = new RemoteImageInfo
- {
- Type = imageType,
- Url = "image url " + i
- };
- }
- var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
- providerManager.Setup(pm => pm.GetAvailableRemoteImages(It.IsAny<BaseItem>(), It.IsAny<RemoteImageQuery>(), It.IsAny<CancellationToken>()))
- .ReturnsAsync(remoteInfo);
- var itemImageProvider = GetItemImageProvider(providerManager.Object, new Mock<IFileSystem>());
- var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
- Assert.Equal(forceRefresh, result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
- Assert.Equal(imageCount, item.GetImages(imageType).Count());
- foreach (var image in item.GetImages(imageType))
- {
- if (forceRefresh)
- {
- Assert.Matches("image url [0-9]", image.Path);
- }
- else
- {
- Assert.DoesNotMatch("image url [0-9]", image.Path);
- }
- }
- }
- [Theory]
- [InlineData(ImageType.Primary, 0, false)] // singular type only fetches if type is missing from item, no caching
- [InlineData(ImageType.Backdrop, 0, false)] // empty item, no cache to check
- [InlineData(ImageType.Backdrop, 1, false)] // populated item, cached so no download
- [InlineData(ImageType.Backdrop, 1, true)] // populated item, forced to download
- public async void RefreshImages_NonStubItemPopulatedProviderRemote_DownloadsIfNecessary(ImageType imageType, int initialImageCount, bool fullRefresh)
- {
- var targetImageCount = 1;
- // Set path and media source manager so images will be downloaded (EnableImageStub will return false)
- var item = GetItemWithImages(imageType, initialImageCount, false);
- item.Path = "non-empty path";
- BaseItem.MediaSourceManager = Mock.Of<IMediaSourceManager>();
- // seek 2 so it won't short-circuit out of downloading when populated
- var libraryOptions = GetLibraryOptions(item, imageType, 2);
- const string Content = "Content";
- var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
- remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
- remoteProvider.Setup(rp => rp.GetSupportedImages(item))
- .Returns(new[] { imageType });
- remoteProvider.Setup(rp => rp.GetImageResponse(It.IsAny<string>(), It.IsAny<CancellationToken>()))
- .ReturnsAsync((string url, CancellationToken _) => new HttpResponseMessage
- {
- ReasonPhrase = url,
- StatusCode = HttpStatusCode.OK,
- Content = new StringContent(Content, Encoding.UTF8, "image/jpeg")
- });
- var refreshOptions = fullRefresh
- ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
- {
- ImageRefreshMode = MetadataRefreshMode.FullRefresh,
- ReplaceAllImages = true
- }
- : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
- var remoteInfo = new RemoteImageInfo[targetImageCount];
- for (int i = 0; i < targetImageCount; i++)
- {
- remoteInfo[i] = new RemoteImageInfo
- {
- Type = imageType,
- Url = "image url " + i
- };
- }
- var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
- providerManager.Setup(pm => pm.GetAvailableRemoteImages(It.IsAny<BaseItem>(), It.IsAny<RemoteImageQuery>(), It.IsAny<CancellationToken>()))
- .ReturnsAsync(remoteInfo);
- providerManager.Setup(pm => pm.SaveImage(item, It.IsAny<Stream>(), It.IsAny<string>(), imageType, null, It.IsAny<CancellationToken>()))
- .Callback<BaseItem, Stream, string, ImageType, int?, CancellationToken>((callbackItem, _, _, callbackType, _, _) =>
- callbackItem.SetImagePath(callbackType, callbackItem.AllowsMultipleImages(callbackType) ? callbackItem.GetImages(callbackType).Count() : 0, new FileSystemMetadata()))
- .Returns(Task.CompletedTask);
- var fileSystem = new Mock<IFileSystem>();
- // match reported file size to image content length - condition for skipping already downloaded multi-images
- fileSystem.Setup(fs => fs.GetFileInfo(It.IsAny<string>()))
- .Returns(new FileSystemMetadata { Length = Content.Length });
- var itemImageProvider = GetItemImageProvider(providerManager.Object, fileSystem);
- var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
- Assert.Equal(initialImageCount == 0 || fullRefresh, result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
- Assert.Equal(targetImageCount, item.GetImages(imageType).Count());
- }
- [Theory]
- [MemberData(nameof(GetImageTypesWithCount))]
- public async void RefreshImages_EmptyItemPopulatedProviderRemoteExtras_LimitsImages(ImageType imageType, int imageCount)
- {
- var item = new Video();
- var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
- var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
- remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
- remoteProvider.Setup(rp => rp.GetSupportedImages(item))
- .Returns(new[] { imageType });
- var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
- // populate remote with double the required images to verify count is trimmed to the library option count
- var remoteInfoCount = imageCount * 2;
- var remoteInfo = new RemoteImageInfo[remoteInfoCount];
- for (int i = 0; i < remoteInfoCount; i++)
- {
- remoteInfo[i] = new RemoteImageInfo
- {
- Type = imageType,
- Url = "image url " + i
- };
- }
- var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
- providerManager.Setup(pm => pm.GetAvailableRemoteImages(It.IsAny<BaseItem>(), It.IsAny<RemoteImageQuery>(), It.IsAny<CancellationToken>()))
- .ReturnsAsync(remoteInfo);
- var itemImageProvider = GetItemImageProvider(providerManager.Object, null);
- var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
- Assert.True(result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
- var actualImages = item.GetImages(imageType).ToList();
- Assert.Equal(imageCount, actualImages.Count);
- // images from the provider manager are sorted by preference (earlier images are higher priority) so we can verify that low url numbers are chosen
- foreach (var image in actualImages)
- {
- var index = int.Parse(NumbersRegex().Match(image.Path).ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture);
- Assert.True(index < imageCount);
- }
- }
- [Theory]
- [MemberData(nameof(GetImageTypesWithCount))]
- public async void RefreshImages_PopulatedItemEmptyProviderRemoteFullRefresh_DoesntClearImages(ImageType imageType, int imageCount)
- {
- var item = GetItemWithImages(imageType, imageCount, false);
- var libraryOptions = GetLibraryOptions(item, imageType, imageCount);
- var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
- remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
- remoteProvider.Setup(rp => rp.GetSupportedImages(item))
- .Returns(new[] { imageType });
- var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>())
- {
- ImageRefreshMode = MetadataRefreshMode.FullRefresh,
- ReplaceAllImages = true
- };
- var itemImageProvider = GetItemImageProvider(Mock.Of<IProviderManager>(), null);
- var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
- Assert.False(result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
- Assert.Equal(imageCount, item.GetImages(imageType).Count());
- }
- [Theory]
- [InlineData(9, false)]
- [InlineData(10, true)]
- [InlineData(null, true)]
- public async void RefreshImages_ProviderRemote_FiltersByWidth(int? remoteImageWidth, bool expectedToUpdate)
- {
- var imageType = ImageType.Primary;
- var item = new Video();
- var libraryOptions = new LibraryOptions
- {
- TypeOptions = new[]
- {
- new TypeOptions
- {
- Type = item.GetType().Name,
- ImageOptions = new[]
- {
- new ImageOption
- {
- Type = imageType,
- MinWidth = 10
- }
- }
- }
- }
- };
- var remoteProvider = new Mock<IRemoteImageProvider>(MockBehavior.Strict);
- remoteProvider.Setup(rp => rp.Name).Returns("MockRemoteProvider");
- remoteProvider.Setup(rp => rp.GetSupportedImages(item))
- .Returns(new[] { imageType });
- var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
- // set width on image from remote
- var remoteInfo = new[]
- {
- new RemoteImageInfo()
- {
- Type = imageType,
- Url = "image url",
- Width = remoteImageWidth
- }
- };
- var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
- providerManager.Setup(pm => pm.GetAvailableRemoteImages(It.IsAny<BaseItem>(), It.IsAny<RemoteImageQuery>(), It.IsAny<CancellationToken>()))
- .ReturnsAsync(remoteInfo);
- var itemImageProvider = GetItemImageProvider(providerManager.Object, null);
- var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { remoteProvider.Object }, refreshOptions, CancellationToken.None);
- Assert.Equal(expectedToUpdate, result.UpdateType.HasFlag(ItemUpdateType.ImageUpdate));
- }
- private static ItemImageProvider GetItemImageProvider(IProviderManager? providerManager, Mock<IFileSystem>? mockFileSystem)
- {
- // strict to ensure this isn't accidentally used where a prepared mock is intended
- providerManager ??= Mock.Of<IProviderManager>(MockBehavior.Strict);
- // BaseItem.ValidateImages depends on the directory service being able to list directory contents, give it the expected valid file paths
- mockFileSystem ??= new Mock<IFileSystem>(MockBehavior.Strict);
- mockFileSystem.Setup(fs => fs.GetFilePaths(It.IsAny<string>(), It.IsAny<bool>()))
- .Returns(new[]
- {
- string.Format(CultureInfo.InvariantCulture, TestDataImagePath, 0),
- string.Format(CultureInfo.InvariantCulture, TestDataImagePath, 1)
- });
- return new ItemImageProvider(new NullLogger<ItemImageProvider>(), providerManager, mockFileSystem.Object);
- }
- private static BaseItem GetItemWithImages(ImageType type, int count, bool validPaths)
- {
- // Has to exist for querying DateModified time on file, results stored but not checked so not populating
- BaseItem.FileSystem ??= Mock.Of<IFileSystem>();
- var item = new Video();
- var path = validPaths ? TestDataImagePath : "invalid path {0}";
- for (int i = 0; i < count; i++)
- {
- item.SetImagePath(type, i, new FileSystemMetadata
- {
- FullName = string.Format(CultureInfo.InvariantCulture, path, i),
- });
- }
- return item;
- }
- private static ILocalImageProvider GetImageProvider(ImageType type, int count, bool validPaths)
- {
- var images = GetImages(type, count, validPaths);
- var imageProvider = new Mock<ILocalImageProvider>();
- imageProvider.Setup(ip => ip.GetImages(It.IsAny<BaseItem>(), It.IsAny<IDirectoryService>()))
- .Returns(images);
- return imageProvider.Object;
- }
- /// <summary>
- /// Creates a list of <see cref="LocalImageInfo"/> references of the specified type and size, optionally pointing to files that exist.
- /// </summary>
- private static LocalImageInfo[] GetImages(ImageType type, int count, bool validPaths)
- {
- var path = validPaths ? TestDataImagePath : "invalid path {0}";
- var images = new LocalImageInfo[count];
- for (int i = 0; i < count; i++)
- {
- images[i] = new LocalImageInfo
- {
- Type = type,
- FileInfo = new FileSystemMetadata
- {
- FullName = string.Format(CultureInfo.InvariantCulture, path, i)
- }
- };
- }
- return images;
- }
- /// <summary>
- /// Generates a <see cref="LibraryOptions"/> object that will allow for the requested number of images for the target type.
- /// </summary>
- private static LibraryOptions GetLibraryOptions(BaseItem item, ImageType type, int count)
- {
- return new LibraryOptions
- {
- TypeOptions = new[]
- {
- new TypeOptions
- {
- Type = item.GetType().Name,
- ImageOptions = new[]
- {
- new ImageOption
- {
- Type = type,
- Limit = count,
- }
- }
- }
- }
- };
- }
- }
- }
|