aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs2
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj6
-rw-r--r--tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj2
-rw-r--r--tests/Jellyfin.Controller.Tests/BaseItemManagerTests.cs89
-rw-r--r--tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj2
-rw-r--r--tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj2
-rw-r--r--tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj2
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj2
-rw-r--r--tests/Jellyfin.Model.Tests/Cryptography/PasswordHashTests.cs (renamed from tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs)4
-rw-r--r--tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj2
-rw-r--r--tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj2
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs2
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SeriesPathParserTest.cs28
-rw-r--r--tests/Jellyfin.Naming.Tests/TV/SeriesResolverTests.cs28
-rw-r--r--tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj2
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs2
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkParseTests.cs50
-rw-r--r--tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj8
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs597
-rw-r--r--tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs268
-rw-r--r--tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs31
-rw-r--r--tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs160
-rw-r--r--tests/Jellyfin.Providers.Tests/Test Data/Images/blank0.jpg0
-rw-r--r--tests/Jellyfin.Providers.Tests/Test Data/Images/blank1.jpg0
-rw-r--r--tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs13
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj2
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs14
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs32
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs4
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs2
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj6
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs10
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs12
-rw-r--r--tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj6
-rw-r--r--tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj2
36 files changed, 1072 insertions, 324 deletions
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
index cd03958b6..6f5c0ed0c 100644
--- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
@@ -132,6 +132,8 @@ namespace Jellyfin.Api.Tests.Auth
authorizationInfo.User.AddDefaultPreferences();
authorizationInfo.User.SetPermission(PermissionKind.IsAdministrator, isAdmin);
authorizationInfo.IsApiKey = false;
+ authorizationInfo.HasToken = true;
+ authorizationInfo.Token = "fake-token";
_jellyfinAuthServiceMock.Setup(
a => a.Authenticate(
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index 922b3d94f..2aced0669 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -15,9 +15,9 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
index 1fe4e2565..ce607b2ec 100644
--- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
+++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj
@@ -12,7 +12,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
diff --git a/tests/Jellyfin.Controller.Tests/BaseItemManagerTests.cs b/tests/Jellyfin.Controller.Tests/BaseItemManagerTests.cs
new file mode 100644
index 000000000..edceef4a7
--- /dev/null
+++ b/tests/Jellyfin.Controller.Tests/BaseItemManagerTests.cs
@@ -0,0 +1,89 @@
+using System;
+using MediaBrowser.Controller.BaseItemManager;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Model.Configuration;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Controller.Tests
+{
+ public class BaseItemManagerTests
+ {
+ [Theory]
+ [InlineData(typeof(Book), "LibraryEnabled", true)]
+ [InlineData(typeof(Book), "LibraryDisabled", false)]
+ [InlineData(typeof(MusicArtist), "Enabled", true)]
+ [InlineData(typeof(MusicArtist), "ServerDisabled", false)]
+ public void IsMetadataFetcherEnabled_ChecksOptions_ReturnsExpected(Type itemType, string fetcherName, bool expected)
+ {
+ BaseItem item = (BaseItem)Activator.CreateInstance(itemType)!;
+
+ var libraryOptions = new LibraryOptions
+ {
+ TypeOptions = new[]
+ {
+ new TypeOptions
+ {
+ Type = "Book",
+ MetadataFetchers = new[] { "LibraryEnabled" }
+ }
+ }
+ };
+
+ var serverConfiguration = new ServerConfiguration();
+ foreach (var typeConfig in serverConfiguration.MetadataOptions)
+ {
+ typeConfig.DisabledMetadataFetchers = new[] { "ServerDisabled" };
+ }
+
+ var serverConfigurationManager = new Mock<IServerConfigurationManager>();
+ serverConfigurationManager.Setup(scm => scm.Configuration)
+ .Returns(serverConfiguration);
+
+ var baseItemManager = new BaseItemManager(serverConfigurationManager.Object);
+ var actual = baseItemManager.IsMetadataFetcherEnabled(item, libraryOptions, fetcherName);
+
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(typeof(Book), "LibraryEnabled", true)]
+ [InlineData(typeof(Book), "LibraryDisabled", false)]
+ [InlineData(typeof(MusicArtist), "Enabled", true)]
+ [InlineData(typeof(MusicArtist), "ServerDisabled", false)]
+ public void IsImageFetcherEnabled_ChecksOptions_ReturnsExpected(Type itemType, string fetcherName, bool expected)
+ {
+ BaseItem item = (BaseItem)Activator.CreateInstance(itemType)!;
+
+ var libraryOptions = new LibraryOptions
+ {
+ TypeOptions = new[]
+ {
+ new TypeOptions
+ {
+ Type = "Book",
+ ImageFetchers = new[] { "LibraryEnabled" }
+ }
+ }
+ };
+
+ var serverConfiguration = new ServerConfiguration();
+ foreach (var typeConfig in serverConfiguration.MetadataOptions)
+ {
+ typeConfig.DisabledImageFetchers = new[] { "ServerDisabled" };
+ }
+
+ var serverConfigurationManager = new Mock<IServerConfigurationManager>();
+ serverConfigurationManager.Setup(scm => scm.Configuration)
+ .Returns(serverConfiguration);
+
+ var baseItemManager = new BaseItemManager(serverConfigurationManager.Object);
+ var actual = baseItemManager.IsImageFetcherEnabled(item, libraryOptions, fetcherName);
+
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
index e9a951571..0ffc19833 100644
--- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
+++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj
@@ -12,7 +12,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
index 1fb95aab4..098166001 100644
--- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
+++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj
@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
index 2dc4ac19a..ee3af7559 100644
--- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
+++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj
@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
index 201f63a2d..dc4a42c19 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
+++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj
@@ -22,7 +22,7 @@
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
diff --git a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs b/tests/Jellyfin.Model.Tests/Cryptography/PasswordHashTests.cs
index bfece97b6..6948280a3 100644
--- a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs
+++ b/tests/Jellyfin.Model.Tests/Cryptography/PasswordHashTests.cs
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
-using MediaBrowser.Common.Cryptography;
+using MediaBrowser.Model.Cryptography;
using Xunit;
-namespace Jellyfin.Common.Tests.Cryptography
+namespace Jellyfin.Model.Tests.Cryptography
{
public static class PasswordHashTests
{
diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
index a37e5ac92..7e8397d9f 100644
--- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
+++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj
@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
index 75d466198..4096873a3 100644
--- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
+++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj
@@ -12,7 +12,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
index 2873f6161..1e7fedb36 100644
--- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
+++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs
@@ -71,9 +71,9 @@ namespace Jellyfin.Naming.Tests.TV
[InlineData("Season 1/seriesname 05.mkv", 5)] // no hyphen between series name and episode number
[InlineData("[BBT-RMX] Ranma ½ - 154 [50AC421A].mkv", 154)] // hyphens in the pre-name info, triple digit episode number
[InlineData("Season 2/Episode 21 - 94 Meetings.mp4", 21)] // Title starts with a number
+ [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)]
// [InlineData("Case Closed (1996-2007)/Case Closed - 317.mkv", 317)] // triple digit episode number
// TODO: [InlineData("Season 2/16 12 Some Title.avi", 16)]
- // TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", 7)]
// TODO: [InlineData("Season 4/Uchuu.Senkan.Yamato.2199.E03.avi", 3)]
// TODO: [InlineData("Season 2/7 12 Angry Men.avi", 7)]
// TODO: [InlineData("Season 02/02x03x04x15 - Ep Name.mp4", 2)]
diff --git a/tests/Jellyfin.Naming.Tests/TV/SeriesPathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/SeriesPathParserTest.cs
new file mode 100644
index 000000000..ceb5f8b73
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/SeriesPathParserTest.cs
@@ -0,0 +1,28 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class SeriesPathParserTest
+ {
+ [Theory]
+ [InlineData("The.Show.S01", "The.Show")]
+ [InlineData("/The.Show.S01", "The.Show")]
+ [InlineData("/some/place/The.Show.S01", "The.Show")]
+ [InlineData("/something/The.Show.S01", "The.Show")]
+ [InlineData("The Show Season 10", "The Show")]
+ [InlineData("The Show S01E01", "The Show")]
+ [InlineData("The Show S01E01 Episode", "The Show")]
+ [InlineData("/something/The Show/Season 1", "The Show")]
+ [InlineData("/something/The Show/S01", "The Show")]
+ public void SeriesPathParserParseTest(string path, string name)
+ {
+ NamingOptions o = new NamingOptions();
+ var res = SeriesPathParser.Parse(o, path);
+
+ Assert.Equal(name, res.SeriesName);
+ Assert.True(res.Success);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Naming.Tests/TV/SeriesResolverTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeriesResolverTests.cs
new file mode 100644
index 000000000..97f4b4058
--- /dev/null
+++ b/tests/Jellyfin.Naming.Tests/TV/SeriesResolverTests.cs
@@ -0,0 +1,28 @@
+using Emby.Naming.Common;
+using Emby.Naming.TV;
+using Xunit;
+
+namespace Jellyfin.Naming.Tests.TV
+{
+ public class SeriesResolverTests
+ {
+ [Theory]
+ [InlineData("The.Show.S01", "The Show")]
+ [InlineData("The.Show.S01.COMPLETE", "The Show")]
+ [InlineData("S.H.O.W.S01", "S.H.O.W")]
+ [InlineData("The.Show.P.I.S01", "The Show P.I")]
+ [InlineData("The_Show_Season_1", "The Show")]
+ [InlineData("/something/The_Show/Season 10", "The Show")]
+ [InlineData("The Show", "The Show")]
+ [InlineData("/some/path/The Show", "The Show")]
+ [InlineData("/some/path/The Show s02e10 720p hdtv", "The Show")]
+ [InlineData("/some/path/The Show s02e10 the episode 720p hdtv", "The Show")]
+ public void SeriesResolverResolveTest(string path, string name)
+ {
+ NamingOptions o = new NamingOptions();
+ var res = SeriesResolver.Resolve(o, path);
+
+ Assert.Equal(name, res.Name);
+ }
+ }
+}
diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
index 75d9b9ea9..78556ee67 100644
--- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
+++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj
@@ -12,7 +12,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
diff --git a/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs b/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs
index 1cad625b7..61f913252 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs
@@ -34,7 +34,7 @@ namespace Jellyfin.Networking.Tests
}
/// <summary>
- /// Checks that thge given IP address is not in the network provided.
+ /// Checks that the given IP address is not in the network provided.
/// </summary>
/// <param name="network">Network address(es).</param>
/// <param name="value">The IP to check.</param>
diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
index a24eee693..6b9397437 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
@@ -35,9 +35,9 @@ namespace Jellyfin.Networking.Tests
// eth16 only
[InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")]
// All interfaces excluded. (including loopbacks)
- [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[127.0.0.1/8,::1/128]")]
+ [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")]
// vEthernet1 and vEthernet212 should be excluded.
- [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24,127.0.0.1/8,::1/128]")]
+ [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")]
// Overlapping interface,
[InlineData("192.168.1.110/24,-20,br0|192.168.1.10/24,-16,br0|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.110/24,192.168.1.10/24]")]
public void IgnoreVirtualInterfaces(string interfaces, string lan, string value)
@@ -476,5 +476,51 @@ namespace Jellyfin.Networking.Tests
Assert.NotEqual(nm.HasRemoteAccess(IPAddress.Parse(remoteIp)), denied);
}
+
+ [Theory]
+ [InlineData("192.168.1.209/24,-16,eth16", "192.168.1.0/24", "", "192.168.1.209")] // Only 1 address so use it.
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "192.168.1.208")] // LAN address is specified by default.
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "10.0.0.1")] // return bind address
+
+ public void GetBindInterface_NoSourceGiven_Success(string interfaces, string lan, string bind, string result)
+ {
+ var conf = new NetworkConfiguration
+ {
+ EnableIPV4 = true,
+ LocalNetworkSubnets = lan.Split(','),
+ LocalNetworkAddresses = bind.Split(',')
+ };
+
+ NetworkManager.MockNetworkSettings = interfaces;
+ using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
+
+ var interfaceToUse = nm.GetBindInterface(string.Empty, out _);
+
+ Assert.Equal(result, interfaceToUse);
+ }
+
+ [Theory]
+ [InlineData("192.168.1.209/24,-16,eth16", "192.168.1.0/24", "", "192.168.1.210", "192.168.1.209")] // Source on LAN
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "192.168.1.209", "192.168.1.208")] // Source on LAN
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "8.8.8.8", "10.0.0.1")] // Source external.
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "192.168.1.209", "10.0.0.1")] // LAN not bound, so return external.
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "8.8.8.8", "10.0.0.1")] // return external bind address
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "192.168.1.210", "192.168.1.208")] // return LAN bind address
+ public void GetBindInterface_ValidSourceGiven_Success(string interfaces, string lan, string bind, string source, string result)
+ {
+ var conf = new NetworkConfiguration
+ {
+ EnableIPV4 = true,
+ LocalNetworkSubnets = lan.Split(','),
+ LocalNetworkAddresses = bind.Split(',')
+ };
+
+ NetworkManager.MockNetworkSettings = interfaces;
+ using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
+
+ var interfaceToUse = nm.GetBindInterface(source, out _);
+
+ Assert.Equal(result, interfaceToUse);
+ }
}
}
diff --git a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj
index 0b2db64b0..10767ae23 100644
--- a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj
+++ b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj
@@ -7,7 +7,13 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <None Include="Test Data\**\*.*">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
diff --git a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
new file mode 100644
index 000000000..2ba5c47d7
--- /dev/null
+++ b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
@@ -0,0 +1,597 @@
+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 class ItemImageProviderTests
+ {
+ private const string TestDataImagePath = "Test Data/Images/blank{0}.jpg";
+
+ [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()
+ {
+ var itemImageProvider = GetItemImageProvider(null, null);
+ var changed = itemImageProvider.ValidateImages(new Video(), Enumerable.Empty<ILocalImageProvider>(), null);
+
+ Assert.False(changed);
+ }
+
+ private static TheoryData<ImageType, int> GetImageTypesWithCount()
+ {
+ var theoryTypes = new TheoryData<ImageType, int>
+ {
+ // minimal test cases that hit different handling
+ { ImageType.Primary, 1 },
+ { ImageType.Backdrop, 1 },
+ { ImageType.Backdrop, 2 }
+ };
+
+ return theoryTypes;
+ }
+
+ [Theory]
+ [MemberData(nameof(GetImageTypesWithCount))]
+ public void ValidateImages_EmptyItemAndPopulatedProviders_AddsImages(ImageType imageType, int imageCount)
+ {
+ // 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 imageProvider = GetImageProvider(imageType, imageCount, true);
+
+ var itemImageProvider = GetItemImageProvider(null, null);
+ var changed = itemImageProvider.ValidateImages(item, new[] { imageProvider }, null);
+
+ Assert.True(changed);
+ Assert.Equal(imageCount, item.GetImages(imageType).Count());
+ }
+
+ [Theory]
+ [MemberData(nameof(GetImageTypesWithCount))]
+ public void ValidateImages_PopulatedItemWithGoodPathsAndEmptyProviders_NoChange(ImageType imageType, int imageCount)
+ {
+ var item = GetItemWithImages(imageType, imageCount, true);
+
+ var itemImageProvider = GetItemImageProvider(null, null);
+ var changed = itemImageProvider.ValidateImages(item, Enumerable.Empty<ILocalImageProvider>(), null);
+
+ Assert.False(changed);
+ Assert.Equal(imageCount, item.GetImages(imageType).Count());
+ }
+
+ [Theory]
+ [MemberData(nameof(GetImageTypesWithCount))]
+ public void ValidateImages_PopulatedItemWithBadPathsAndEmptyProviders_RemovesImage(ImageType imageType, int imageCount)
+ {
+ var item = GetItemWithImages(imageType, imageCount, false);
+
+ var itemImageProvider = GetItemImageProvider(null, null);
+ var changed = itemImageProvider.ValidateImages(item, Enumerable.Empty<ILocalImageProvider>(), null);
+
+ Assert.True(changed);
+ Assert.Empty(item.GetImages(imageType));
+ }
+
+ [Fact]
+ public void MergeImages_EmptyItemNewImagesEmpty_NoChange()
+ {
+ var itemImageProvider = GetItemImageProvider(null, null);
+ var changed = itemImageProvider.MergeImages(new Video(), Array.Empty<LocalImageInfo>());
+
+ 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);
+
+ 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]
+ [MemberData(nameof(GetImageTypesWithCount))]
+ public void MergeImages_PopulatedItemWithGoodPathsAndSameNewImages_NoChange(ImageType imageType, int imageCount)
+ {
+ var oldTime = new DateTime(1970, 1, 1);
+
+ // match update time with time added to item images (unix epoch)
+ var fileSystem = new Mock<IFileSystem>();
+ fileSystem.Setup(fs => fs.GetLastWriteTimeUtc(It.IsAny<FileSystemMetadata>()))
+ .Returns(oldTime);
+ 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 updates 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);
+
+ Assert.False(changed);
+ }
+
+ [Theory]
+ [MemberData(nameof(GetImageTypesWithCount))]
+ public void MergeImages_PopulatedItemWithGoodPathsAndSameNewImagesWithNewTimestamps_ResetsImageSizes(ImageType imageType, int imageCount)
+ {
+ var oldTime = new DateTime(1970, 1, 1);
+ var updatedTime = new DateTime(2021, 1, 1);
+
+ 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);
+
+ Assert.True(changed);
+ // before and after paths are the same, verify updated by size reset to 0
+ Assert.Equal(imageCount, item.GetImages(imageType).Count());
+ foreach (var image in item.GetImages(imageType))
+ {
+ Assert.Equal(updatedTime, image.DateModified);
+ Assert.Equal(0, image.Height);
+ Assert.Equal(0, image.Width);
+ }
+ }
+
+ [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,
+ Width = 1 // min width is set to 0, this will always pass
+ };
+ }
+
+ 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,
+ Width = 1 // min width is set to 0, this will always pass
+ };
+ }
+
+ 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,
+ Width = 1 // min width is set to 0, this will always pass
+ };
+ }
+
+ 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(Regex.Match(image.Path, @"[0-9]+").Value, 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());
+ }
+
+ 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,
+ MinWidth = 0
+ }
+ }
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs
index b194e3885..558321810 100644
--- a/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs
+++ b/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -5,11 +6,14 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Providers.MediaInfo;
+using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
@@ -17,200 +21,136 @@ namespace Jellyfin.Providers.Tests.MediaInfo
{
public class EmbeddedImageProviderTests
{
- private static TheoryData<BaseItem> GetSupportedImages_UnsupportedBaseItems_ReturnsEmpty_TestData()
- {
- return new ()
- {
- new AudioBook(),
- new BoxSet(),
- new Series(),
- new Season(),
- };
- }
-
[Theory]
- [MemberData(nameof(GetSupportedImages_UnsupportedBaseItems_ReturnsEmpty_TestData))]
- public void GetSupportedImages_UnsupportedBaseItems_ReturnsEmpty(BaseItem item)
+ [InlineData(typeof(AudioBook))]
+ [InlineData(typeof(BoxSet))]
+ [InlineData(typeof(Series))]
+ [InlineData(typeof(Season))]
+ [InlineData(typeof(Episode), ImageType.Primary)]
+ [InlineData(typeof(Movie), ImageType.Logo, ImageType.Backdrop, ImageType.Primary)]
+ public void GetSupportedImages_AnyBaseItem_ReturnsExpected(Type type, params ImageType[] expected)
{
- var embeddedImageProvider = GetEmbeddedImageProvider(null);
- Assert.Empty(embeddedImageProvider.GetSupportedImages(item));
- }
-
- private static TheoryData<BaseItem, IEnumerable<ImageType>> GetSupportedImages_SupportedBaseItems_ReturnsPopulated_TestData()
- {
- return new TheoryData<BaseItem, IEnumerable<ImageType>>
- {
- { new Episode(), new List<ImageType> { ImageType.Primary } },
- { new Movie(), new List<ImageType> { ImageType.Logo, ImageType.Backdrop, ImageType.Primary } },
- };
- }
-
- [Theory]
- [MemberData(nameof(GetSupportedImages_SupportedBaseItems_ReturnsPopulated_TestData))]
- public void GetSupportedImages_SupportedBaseItems_ReturnsPopulated(BaseItem item, IEnumerable<ImageType> expected)
- {
- var embeddedImageProvider = GetEmbeddedImageProvider(null);
+ BaseItem item = (BaseItem)Activator.CreateInstance(type)!;
+ var embeddedImageProvider = new EmbeddedImageProvider(Mock.Of<IMediaSourceManager>(), Mock.Of<IMediaEncoder>(), new NullLogger<EmbeddedImageProvider>());
var actual = embeddedImageProvider.GetSupportedImages(item);
Assert.Equal(expected.OrderBy(i => i.ToString()), actual.OrderBy(i => i.ToString()));
}
[Fact]
- public async void GetImage_InputWithNoStreams_ReturnsNoImage()
+ public async void GetImage_NoStreams_ReturnsNoImage()
{
- var embeddedImageProvider = GetEmbeddedImageProvider(null);
+ var input = new Movie();
- var input = GetMovie(new List<MediaAttachment>(), new List<MediaStream>());
+ var mediaSourceManager = GetMediaSourceManager(input, new List<MediaAttachment>(), new List<MediaStream>());
+ var embeddedImageProvider = new EmbeddedImageProvider(mediaSourceManager, null, new NullLogger<EmbeddedImageProvider>());
var actual = await embeddedImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
Assert.NotNull(actual);
Assert.False(actual.HasImage);
}
- [Fact]
- public async void GetImage_InputWithUnlabeledAttachments_ReturnsNoImage()
+ [Theory]
+ [InlineData("chapter", null, 1, ImageType.Chapter, null)] // unexpected type, nothing found
+ [InlineData("unmatched", null, 1, ImageType.Primary, null)] // doesn't default on no match
+ [InlineData("clearlogo.png", null, 1, ImageType.Logo, ImageFormat.Png)] // extract extension from name
+ [InlineData("backdrop", "image/bmp", 2, ImageType.Backdrop, ImageFormat.Bmp)] // extract extension from mimetype
+ [InlineData("poster", null, 3, ImageType.Primary, ImageFormat.Jpg)] // default extension to jpg
+ public async void GetImage_Attachment_ReturnsCorrectSelection(string filename, string mimetype, int targetIndex, ImageType type, ImageFormat? expectedFormat)
{
- var embeddedImageProvider = GetEmbeddedImageProvider(null);
-
- // add an attachment without a filename - has a list to look through but finds nothing
- var input = GetMovie(
- new List<MediaAttachment> { new () },
- new List<MediaStream>());
-
- var actual = await embeddedImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
- Assert.NotNull(actual);
- Assert.False(actual.HasImage);
- }
+ var attachments = new List<MediaAttachment>();
+ string pathPrefix = "path";
+ for (int i = 1; i <= targetIndex; i++)
+ {
+ var name = i == targetIndex ? filename : "unmatched";
+ attachments.Add(new ()
+ {
+ FileName = name,
+ MimeType = mimetype,
+ Index = i
+ });
+ }
- [Fact]
- public async void GetImage_InputWithLabeledAttachments_ReturnsCorrectSelection()
- {
- // first tests file extension detection, second uses mimetype, third defaults to jpg
- MediaAttachment sampleAttachment1 = new () { FileName = "clearlogo.png", Index = 1 };
- MediaAttachment sampleAttachment2 = new () { FileName = "backdrop", MimeType = "image/bmp", Index = 2 };
- MediaAttachment sampleAttachment3 = new () { FileName = "poster", Index = 3 };
- string targetPath1 = "path1.png";
- string targetPath2 = "path2.bmp";
- string targetPath3 = "path2.jpg";
+ var input = new Movie();
var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), 1, ".png", CancellationToken.None))
- .Returns(Task.FromResult(targetPath1));
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), 2, ".bmp", CancellationToken.None))
- .Returns(Task.FromResult(targetPath2));
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), 3, ".jpg", CancellationToken.None))
- .Returns(Task.FromResult(targetPath3));
- var embeddedImageProvider = GetEmbeddedImageProvider(mediaEncoder.Object);
-
- var input = GetMovie(
- new List<MediaAttachment> { sampleAttachment1, sampleAttachment2, sampleAttachment3 },
- new List<MediaStream>());
-
- var actualLogo = await embeddedImageProvider.GetImage(input, ImageType.Logo, CancellationToken.None);
- Assert.NotNull(actualLogo);
- Assert.True(actualLogo.HasImage);
- Assert.Equal(targetPath1, actualLogo.Path);
- Assert.Equal(ImageFormat.Png, actualLogo.Format);
-
- var actualBackdrop = await embeddedImageProvider.GetImage(input, ImageType.Backdrop, CancellationToken.None);
- Assert.NotNull(actualBackdrop);
- Assert.True(actualBackdrop.HasImage);
- Assert.Equal(targetPath2, actualBackdrop.Path);
- Assert.Equal(ImageFormat.Bmp, actualBackdrop.Format);
-
- var actualPrimary = await embeddedImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
- Assert.NotNull(actualPrimary);
- Assert.True(actualPrimary.HasImage);
- Assert.Equal(targetPath3, actualPrimary.Path);
- Assert.Equal(ImageFormat.Jpg, actualPrimary.Format);
- }
-
- [Fact]
- public async void GetImage_InputWithUnlabeledEmbeddedImages_BackdropReturnsNoImage()
- {
- var embeddedImageProvider = GetEmbeddedImageProvider(null);
+ mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), It.IsAny<int>(), It.IsAny<ImageFormat>(), It.IsAny<CancellationToken>()))
+ .Returns<string, string, MediaSourceInfo, MediaStream, int, ImageFormat, CancellationToken>((_, _, _, _, index, ext, _) => Task.FromResult(pathPrefix + index + "." + ext));
+ var mediaSourceManager = GetMediaSourceManager(input, attachments, new List<MediaStream>());
+ var embeddedImageProvider = new EmbeddedImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger<EmbeddedImageProvider>());
- var input = GetMovie(
- new List<MediaAttachment>(),
- new List<MediaStream> { new () { Type = MediaStreamType.EmbeddedImage } });
-
- var actual = await embeddedImageProvider.GetImage(input, ImageType.Backdrop, CancellationToken.None);
+ var actual = await embeddedImageProvider.GetImage(input, type, CancellationToken.None);
Assert.NotNull(actual);
- Assert.False(actual.HasImage);
+ if (expectedFormat == null)
+ {
+ Assert.False(actual.HasImage);
+ }
+ else
+ {
+ Assert.True(actual.HasImage);
+ Assert.Equal(pathPrefix + targetIndex + "." + expectedFormat, actual.Path, StringComparer.OrdinalIgnoreCase);
+ Assert.Equal(expectedFormat, actual.Format);
+ }
}
- [Fact]
- public async void GetImage_InputWithUnlabeledEmbeddedImages_PrimaryReturnsImage()
+ [Theory]
+ [InlineData("chapter", null, 1, ImageType.Chapter, null)] // unexpected type, nothing found
+ [InlineData(null, null, 1, ImageType.Backdrop, null)] // no label, can only find primary
+ [InlineData(null, null, 1, ImageType.Primary, ImageFormat.Jpg)] // no label, finds primary
+ [InlineData("backdrop", null, 2, ImageType.Backdrop, ImageFormat.Jpg)] // uses label to find index 2, not just pulling first stream
+ [InlineData("cover", null, 2, ImageType.Primary, ImageFormat.Jpg)] // uses label to find index 2, not just pulling first stream
+ [InlineData(null, "mjpeg", 1, ImageType.Primary, ImageFormat.Jpg)]
+ [InlineData(null, "png", 1, ImageType.Primary, ImageFormat.Png)]
+ [InlineData(null, "gif", 1, ImageType.Primary, ImageFormat.Gif)]
+ public async void GetImage_Embedded_ReturnsCorrectSelection(string label, string? codec, int targetIndex, ImageType type, ImageFormat? expectedFormat)
{
- MediaStream sampleStream = new () { Type = MediaStreamType.EmbeddedImage, Index = 1 };
- string targetPath = "path";
-
+ var streams = new List<MediaStream>();
+ for (int i = 1; i <= targetIndex; i++)
+ {
+ var comment = i == targetIndex ? label : "unmatched";
+ streams.Add(new ()
+ {
+ Type = MediaStreamType.EmbeddedImage,
+ Index = i,
+ Comment = comment,
+ Codec = codec
+ });
+ }
+
+ var input = new Movie();
+
+ var pathPrefix = "path";
var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), sampleStream, 1, ".jpg", CancellationToken.None))
- .Returns(Task.FromResult(targetPath));
- var embeddedImageProvider = GetEmbeddedImageProvider(mediaEncoder.Object);
-
- var input = GetMovie(
- new List<MediaAttachment>(),
- new List<MediaStream> { sampleStream });
-
- var actual = await embeddedImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
+ mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), It.IsAny<int>(), It.IsAny<ImageFormat>(), It.IsAny<CancellationToken>()))
+ .Returns<string, string, MediaSourceInfo, MediaStream, int, ImageFormat, CancellationToken>((_, _, _, stream, index, ext, _) =>
+ {
+ Assert.Equal(streams[index - 1], stream);
+ return Task.FromResult(pathPrefix + index + "." + ext);
+ });
+ var mediaSourceManager = GetMediaSourceManager(input, new List<MediaAttachment>(), streams);
+ var embeddedImageProvider = new EmbeddedImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger<EmbeddedImageProvider>());
+
+ var actual = await embeddedImageProvider.GetImage(input, type, CancellationToken.None);
Assert.NotNull(actual);
- Assert.True(actual.HasImage);
- Assert.Equal(targetPath, actual.Path);
- Assert.Equal(ImageFormat.Jpg, actual.Format);
- }
-
- [Fact]
- public async void GetImage_InputWithLabeledEmbeddedImages_ReturnsCorrectSelection()
- {
- // primary is second stream to ensure it's not defaulting, backdrop is first
- MediaStream sampleStream1 = new () { Type = MediaStreamType.EmbeddedImage, Index = 1, Comment = "backdrop" };
- MediaStream sampleStream2 = new () { Type = MediaStreamType.EmbeddedImage, Index = 2, Comment = "cover" };
- string targetPath1 = "path1.jpg";
- string targetPath2 = "path2.jpg";
-
- var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), sampleStream1, 1, ".jpg", CancellationToken.None))
- .Returns(Task.FromResult(targetPath1));
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), sampleStream2, 2, ".jpg", CancellationToken.None))
- .Returns(Task.FromResult(targetPath2));
- var embeddedImageProvider = GetEmbeddedImageProvider(mediaEncoder.Object);
-
- var input = GetMovie(
- new List<MediaAttachment>(),
- new List<MediaStream> { sampleStream1, sampleStream2 });
-
- var actualPrimary = await embeddedImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
- Assert.NotNull(actualPrimary);
- Assert.True(actualPrimary.HasImage);
- Assert.Equal(targetPath2, actualPrimary.Path);
- Assert.Equal(ImageFormat.Jpg, actualPrimary.Format);
-
- var actualBackdrop = await embeddedImageProvider.GetImage(input, ImageType.Backdrop, CancellationToken.None);
- Assert.NotNull(actualBackdrop);
- Assert.True(actualBackdrop.HasImage);
- Assert.Equal(targetPath1, actualBackdrop.Path);
- Assert.Equal(ImageFormat.Jpg, actualBackdrop.Format);
- }
-
- private static EmbeddedImageProvider GetEmbeddedImageProvider(IMediaEncoder? mediaEncoder)
- {
- return new EmbeddedImageProvider(mediaEncoder);
+ if (expectedFormat == null)
+ {
+ Assert.False(actual.HasImage);
+ }
+ else
+ {
+ Assert.True(actual.HasImage);
+ Assert.Equal(pathPrefix + targetIndex + "." + expectedFormat, actual.Path, StringComparer.OrdinalIgnoreCase);
+ Assert.Equal(expectedFormat, actual.Format);
+ }
}
- private static Movie GetMovie(List<MediaAttachment> mediaAttachments, List<MediaStream> mediaStreams)
+ private static IMediaSourceManager GetMediaSourceManager(BaseItem item, List<MediaAttachment> mediaAttachments, List<MediaStream> mediaStreams)
{
- // Mocking IMediaSourceManager GetMediaAttachments and GetMediaStreams instead of mocking Movie works, but
- // has concurrency problems between this and VideoImageProviderTests due to BaseItem.MediaSourceManager
- // being static
- var movie = new Mock<Movie>();
-
- movie.Setup(item => item.GetMediaSources(It.IsAny<bool>()))
- .Returns(new List<MediaSourceInfo> { new () { MediaAttachments = mediaAttachments } } );
- movie.Setup(item => item.GetMediaStreams())
+ var mediaSourceManager = new Mock<IMediaSourceManager>(MockBehavior.Strict);
+ mediaSourceManager.Setup(i => i.GetMediaAttachments(item.Id))
+ .Returns(mediaAttachments);
+ mediaSourceManager.Setup(i => i.GetMediaStreams(It.Is<MediaStreamQuery>(q => q.ItemId == item.Id && q.Type == MediaStreamType.EmbeddedImage)))
.Returns(mediaStreams);
-
- return movie.Object;
+ return mediaSourceManager.Object;
}
}
}
diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs
index c289a7112..33da277e3 100644
--- a/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs
+++ b/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs
@@ -80,6 +80,37 @@ namespace Jellyfin.Providers.Tests.MediaInfo
}
}
+ [Theory]
+ [InlineData("/video/My Video.mkv", "/video/My Video.srt", "srt", null, false, false)]
+ [InlineData("/video/My.Video.mkv", "/video/My.Video.srt", "srt", null, false, false)]
+ [InlineData("/video/My.Video.mkv", "/video/My.Video.foreign.srt", "srt", null, true, false)]
+ [InlineData("/video/My Video.mkv", "/video/My Video.forced.srt", "srt", null, true, false)]
+ [InlineData("/video/My.Video.mkv", "/video/My.Video.default.srt", "srt", null, false, true)]
+ [InlineData("/video/My.Video.mkv", "/video/My.Video.forced.default.srt", "srt", null, true, true)]
+ [InlineData("/video/My.Video.mkv", "/video/My.Video.en.srt", "srt", "en", false, false)]
+ [InlineData("/video/My.Video.mkv", "/video/My.Video.default.en.srt", "srt", "en", false, true)]
+ [InlineData("/video/My.Video.mkv", "/video/My.Video.default.forced.en.srt", "srt", "en", true, true)]
+ [InlineData("/video/My.Video.mkv", "/video/My.Video.en.default.forced.srt", "srt", "en", true, true)]
+ public void AddExternalSubtitleStreams_GivenSingleFile_ReturnsExpectedSubtitle(string videoPath, string file, string codec, string? language, bool isForced, bool isDefault)
+ {
+ var streams = new List<MediaStream>();
+ var expected = CreateMediaStream(file, codec, language, 0, isForced, isDefault);
+
+ new SubtitleResolver(Mock.Of<ILocalizationManager>()).AddExternalSubtitleStreams(streams, videoPath, 0, new[] { file });
+
+ Assert.Single(streams);
+
+ var actual = streams[0];
+
+ Assert.Equal(expected.Index, actual.Index);
+ Assert.Equal(expected.Type, actual.Type);
+ Assert.Equal(expected.IsExternal, actual.IsExternal);
+ Assert.Equal(expected.Path, actual.Path);
+ Assert.Equal(expected.IsDefault, actual.IsDefault);
+ Assert.Equal(expected.IsForced, actual.IsForced);
+ Assert.Equal(expected.Language, actual.Language);
+ }
+
private static MediaStream CreateMediaStream(string path, string codec, string? language, int index, bool isForced = false, bool isDefault = false)
{
return new ()
diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs
index 0f51a2b8f..839925dd1 100644
--- a/tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs
+++ b/tests/Jellyfin.Providers.Tests/MediaInfo/VideoImageProviderTests.cs
@@ -2,8 +2,11 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -16,85 +19,56 @@ namespace Jellyfin.Providers.Tests.MediaInfo
{
public class VideoImageProviderTests
{
- [Fact]
- public async void GetImage_InputIsPlaceholder_ReturnsNoImage()
+ private static TheoryData<Video> GetImage_UnsupportedInput_ReturnsNoImage_TestData()
{
- var videoImageProvider = GetVideoImageProvider(null);
-
- var input = new Movie
+ return new ()
{
- IsPlaceHolder = true
- };
-
- var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
- Assert.NotNull(actual);
- Assert.False(actual.HasImage);
- }
+ new Movie { IsPlaceHolder = true },
- [Fact]
- public async void GetImage_NoDefaultVideoStream_ReturnsNoImage()
- {
- var videoImageProvider = GetVideoImageProvider(null);
+ new Movie { DefaultVideoStreamIndex = null },
- var input = new Movie
- {
- DefaultVideoStreamIndex = null
+ // set a default index but don't put anything there (invalid input, but provider shouldn't break)
+ new Movie { DefaultVideoStreamIndex = 0 }
};
-
- var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
- Assert.NotNull(actual);
- Assert.False(actual.HasImage);
}
- [Fact]
- public async void GetImage_DefaultSetButNoVideoStream_ReturnsNoImage()
+ [Theory]
+ [MemberData(nameof(GetImage_UnsupportedInput_ReturnsNoImage_TestData))]
+ public async void GetImage_UnsupportedInput_ReturnsNoImage(Video input)
{
- var videoImageProvider = GetVideoImageProvider(null);
-
- // set a default index but don't put anything there (invalid input, but provider shouldn't break)
- var input = GetMovie(0, null, new List<MediaStream>());
+ var mediaSourceManager = GetMediaSourceManager(input, null, new List<MediaStream>());
+ var videoImageProvider = new VideoImageProvider(mediaSourceManager, Mock.Of<IMediaEncoder>(), new NullLogger<VideoImageProvider>());
var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
Assert.NotNull(actual);
Assert.False(actual.HasImage);
}
- [Fact]
- public async void GetImage_DefaultSetMultipleVideoStreams_ReturnsDefaultStreamImage()
+ [Theory]
+ [InlineData(1, 1)] // default not first stream
+ [InlineData(5, 0)] // default out of valid range
+ public async void GetImage_DefaultVideoStreams_ReturnsCorrectStreamImage(int defaultIndex, int targetIndex)
{
- MediaStream firstStream = new () { Type = MediaStreamType.Video, Index = 0 };
- MediaStream targetStream = new () { Type = MediaStreamType.Video, Index = 1 };
- string targetPath = "path.jpg";
+ var input = new Movie { DefaultVideoStreamIndex = defaultIndex };
+ string targetPath = "path.jpg";
+ var mediaStreams = new List<MediaStream>();
var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), firstStream, It.IsAny<Video3DFormat?>(), It.IsAny<TimeSpan?>(), CancellationToken.None))
- .Returns(Task.FromResult("wrong stream called!"));
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), targetStream, It.IsAny<Video3DFormat?>(), It.IsAny<TimeSpan?>(), CancellationToken.None))
- .Returns(Task.FromResult(targetPath));
- var videoImageProvider = GetVideoImageProvider(mediaEncoder.Object);
- var input = GetMovie(1, targetStream, new List<MediaStream> { firstStream, targetStream } );
-
- var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
- Assert.NotNull(actual);
- Assert.True(actual.HasImage);
- Assert.Equal(targetPath, actual.Path);
- Assert.Equal(ImageFormat.Jpg, actual.Format);
- }
+ for (int i = 0; i <= targetIndex; i++)
+ {
+ var mediaStream = new MediaStream { Type = MediaStreamType.Video, Index = i };
+ mediaStreams.Add(mediaStream);
- [Fact]
- public async void GetImage_InvalidDefaultSingleVideoStream_ReturnsFirstVideoStreamImage()
- {
- MediaStream targetStream = new () { Type = MediaStreamType.Video, Index = 0 };
- string targetPath = "path.jpg";
+ var path = i == targetIndex ? targetPath : "wrong stream called!";
+ mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), mediaStream, It.IsAny<Video3DFormat?>(), It.IsAny<TimeSpan?>(), It.IsAny<CancellationToken>()))
+ .Returns(Task.FromResult(path));
+ }
- var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), targetStream, It.IsAny<Video3DFormat?>(), It.IsAny<TimeSpan?>(), CancellationToken.None))
- .Returns(Task.FromResult(targetPath));
- var videoImageProvider = GetVideoImageProvider(mediaEncoder.Object);
+ var defaultStream = defaultIndex < mediaStreams.Count ? mediaStreams[targetIndex] : null;
+ var mediaSourceManager = GetMediaSourceManager(input, defaultStream, mediaStreams);
- // provide query results for default (empty) and all streams (populated)
- var input = GetMovie(5, null, new List<MediaStream> { targetStream });
+ var videoImageProvider = new VideoImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger<VideoImageProvider>());
var actual = await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
Assert.NotNull(actual);
@@ -103,10 +77,19 @@ namespace Jellyfin.Providers.Tests.MediaInfo
Assert.Equal(ImageFormat.Jpg, actual.Format);
}
- [Fact]
- public async void GetImage_NoTimeSpanSet_CallsEncoderWithDefaultTime()
+ [Theory]
+ [InlineData(null, 10)] // default time
+ [InlineData(500, 50)] // calculated time
+ public async void GetImage_TimeSpan_SelectsCorrectTime(int? runTimeSeconds, long expectedSeconds)
{
MediaStream targetStream = new () { Type = MediaStreamType.Video, Index = 0 };
+ var input = new Movie
+ {
+ DefaultVideoStreamIndex = 0,
+ RunTimeTicks = runTimeSeconds * TimeSpan.TicksPerSecond
+ };
+
+ var mediaSourceManager = GetMediaSourceManager(input, targetStream, new List<MediaStream> { targetStream });
// use a callback to catch the actual value
// provides more information on failure than verifying a specific input was called on the mock
@@ -115,62 +98,29 @@ namespace Jellyfin.Providers.Tests.MediaInfo
mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), It.IsAny<Video3DFormat?>(), It.IsAny<TimeSpan?>(), CancellationToken.None))
.Callback<string, string, MediaSourceInfo, MediaStream, Video3DFormat?, TimeSpan?, CancellationToken>((_, _, _, _, _, timeSpan, _) => actualTimeSpan = timeSpan)
.Returns(Task.FromResult("path"));
- var videoImageProvider = GetVideoImageProvider(mediaEncoder.Object);
- var input = GetMovie(0, targetStream, new List<MediaStream> { targetStream });
+ var videoImageProvider = new VideoImageProvider(mediaSourceManager, mediaEncoder.Object, new NullLogger<VideoImageProvider>());
// not testing return, just verifying what gets requested for time span
await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
- Assert.Equal(TimeSpan.FromSeconds(10), actualTimeSpan);
+ Assert.Equal(TimeSpan.FromSeconds(expectedSeconds), actualTimeSpan);
}
- [Fact]
- public async void GetImage_TimeSpanSet_CallsEncoderWithCalculatedTime()
+ private static IMediaSourceManager GetMediaSourceManager(Video item, MediaStream? defaultStream, List<MediaStream> mediaStreams)
{
- MediaStream targetStream = new () { Type = MediaStreamType.Video, Index = 0 };
-
- TimeSpan? actualTimeSpan = null;
- var mediaEncoder = new Mock<IMediaEncoder>(MockBehavior.Strict);
- mediaEncoder.Setup(encoder => encoder.ExtractVideoImage(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MediaSourceInfo>(), It.IsAny<MediaStream>(), It.IsAny<Video3DFormat?>(), It.IsAny<TimeSpan?>(), CancellationToken.None))
- .Callback<string, string, MediaSourceInfo, MediaStream, Video3DFormat?, TimeSpan?, CancellationToken>((_, _, _, _, _, timeSpan, _) => actualTimeSpan = timeSpan)
- .Returns(Task.FromResult("path"));
- var videoImageProvider = GetVideoImageProvider(mediaEncoder.Object);
-
- var input = GetMovie(0, targetStream, new List<MediaStream> { targetStream });
- input.RunTimeTicks = 5000;
-
- // not testing return, just verifying what gets requested for time span
- await videoImageProvider.GetImage(input, ImageType.Primary, CancellationToken.None);
-
- Assert.Equal(TimeSpan.FromTicks(500), actualTimeSpan);
- }
-
- private static VideoImageProvider GetVideoImageProvider(IMediaEncoder? mediaEncoder)
- {
- // strict to ensure this isn't accidentally used where a prepared mock is intended
- mediaEncoder ??= new Mock<IMediaEncoder>(MockBehavior.Strict).Object;
- return new VideoImageProvider(mediaEncoder, new NullLogger<VideoImageProvider>());
- }
-
- private static Movie GetMovie(int defaultVideoStreamIndex, MediaStream? defaultStream, List<MediaStream> mediaStreams)
- {
- // Mocking IMediaSourceManager GetMediaStreams instead of mocking Movie works, but has concurrency problems
- // between this and EmbeddedImageProviderTests due to BaseItem.MediaSourceManager being static
- var movie = new Mock<Movie>
+ var defaultStreamList = new List<MediaStream>();
+ if (defaultStream != null)
{
- Object =
- {
- DefaultVideoStreamIndex = defaultVideoStreamIndex
- }
- };
+ defaultStreamList.Add(defaultStream);
+ }
- movie.Setup(item => item.GetDefaultVideoStream())
- .Returns(defaultStream!);
- movie.Setup(item => item.GetMediaStreams())
+ var mediaSourceManager = new Mock<IMediaSourceManager>(MockBehavior.Strict);
+ mediaSourceManager.Setup(i => i.GetMediaStreams(It.Is<MediaStreamQuery>(q => q.ItemId == item.Id && q.Index == item.DefaultVideoStreamIndex)))
+ .Returns(defaultStreamList);
+ mediaSourceManager.Setup(i => i.GetMediaStreams(It.Is<MediaStreamQuery>(q => q.ItemId == item.Id && q.Type == MediaStreamType.Video)))
.Returns(mediaStreams);
-
- return movie.Object;
+ return mediaSourceManager.Object;
}
}
}
diff --git a/tests/Jellyfin.Providers.Tests/Test Data/Images/blank0.jpg b/tests/Jellyfin.Providers.Tests/Test Data/Images/blank0.jpg
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/Jellyfin.Providers.Tests/Test Data/Images/blank0.jpg
diff --git a/tests/Jellyfin.Providers.Tests/Test Data/Images/blank1.jpg b/tests/Jellyfin.Providers.Tests/Test Data/Images/blank1.jpg
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/Jellyfin.Providers.Tests/Test Data/Images/blank1.jpg
diff --git a/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs b/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
index f6a7c676f..efd2d9553 100644
--- a/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
@@ -23,5 +23,18 @@ namespace Jellyfin.Providers.Tests.Tmdb
{
Assert.Equal(expected, TmdbUtils.NormalizeLanguage(input!));
}
+
+ [Theory]
+ [InlineData(null, null, null)]
+ [InlineData(null, "en-US", null)]
+ [InlineData("en", null, "en")]
+ [InlineData("en", "en-US", "en-US")]
+ [InlineData("fr-CA", "fr-BE", "fr-CA")]
+ [InlineData("fr-CA", "fr", "fr-CA")]
+ [InlineData("de", "en-US", "de")]
+ public static void AdjustImageLanguage_Valid_Success(string imageLanguage, string requestLanguage, string expected)
+ {
+ Assert.Equal(expected, TmdbUtils.AdjustImageLanguage(imageLanguage, requestLanguage));
+ }
}
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
index 5ecd84604..028ebdf55 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs
index c393742eb..a0fe4a5cf 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs
@@ -1,5 +1,4 @@
-using System;
-using Emby.Server.Implementations.Library.Resolvers.TV;
+using Emby.Server.Implementations.Library.Resolvers.TV;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@@ -17,19 +16,16 @@ namespace Jellyfin.Server.Implementations.Tests.Library
[Fact]
public void Resolve_GivenVideoInExtrasFolder_DoesNotResolveToEpisode()
{
- var season = new Season { Name = "Season 1" };
var parent = new Folder { Name = "extras" };
- var libraryManagerMock = new Mock<ILibraryManager>();
- libraryManagerMock.Setup(x => x.GetItemById(It.IsAny<Guid>())).Returns(season);
- var episodeResolver = new EpisodeResolver(libraryManagerMock.Object);
+ var episodeResolver = new EpisodeResolver(null);
var itemResolveArgs = new ItemResolveArgs(
Mock.Of<IServerApplicationPaths>(),
Mock.Of<IDirectoryService>())
{
Parent = parent,
CollectionType = CollectionType.TvShows,
- FileInfo = new FileSystemMetadata()
+ FileInfo = new FileSystemMetadata
{
FullName = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
}
@@ -45,7 +41,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library
// Have to create a mock because of moq proxies not being castable to a concrete implementation
// https://github.com/jellyfin/jellyfin/blob/ab0cff8556403e123642dc9717ba778329554634/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs#L48
- var episodeResolver = new EpisodeResolverMock(Mock.Of<ILibraryManager>());
+ var episodeResolver = new EpisodeResolverMock();
var itemResolveArgs = new ItemResolveArgs(
Mock.Of<IServerApplicationPaths>(),
Mock.Of<IDirectoryService>())
@@ -62,7 +58,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library
private class EpisodeResolverMock : EpisodeResolver
{
- public EpisodeResolverMock(ILibraryManager libraryManager) : base(libraryManager)
+ public EpisodeResolverMock() : base(null)
{
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs
new file mode 100644
index 000000000..8ed3d8b94
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs
@@ -0,0 +1,32 @@
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Emby.Server.Implementations.IO;
+using Emby.Server.Implementations.Library;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.MediaInfo;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Library
+{
+ public class MediaSourceManagerTests
+ {
+ private readonly MediaSourceManager _mediaSourceManager;
+
+ public MediaSourceManagerTests()
+ {
+ IFixture fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
+ fixture.Inject<IFileSystem>(fixture.Create<ManagedFileSystem>());
+ _mediaSourceManager = fixture.Create<MediaSourceManager>();
+ }
+
+ [Theory]
+ [InlineData(@"C:\mydir\myfile.ext", MediaProtocol.File)]
+ [InlineData("/mydir/myfile.ext", MediaProtocol.File)]
+ [InlineData("file:///mydir/myfile.ext", MediaProtocol.File)]
+ [InlineData("http://example.com/stream.m3u8", MediaProtocol.Http)]
+ [InlineData("https://example.com/stream.m3u8", MediaProtocol.Http)]
+ [InlineData("rtsp://media.example.com:554/twister/audiotrack", MediaProtocol.Rtsp)]
+ public void GetPathProtocol_ValidArg_Correct(string path, MediaProtocol expected)
+ => Assert.Equal(expected, _mediaSourceManager.GetPathProtocol(path));
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs
index 976afe195..09aec82b0 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs
@@ -61,7 +61,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
{
Name = "The Big Bang Theory",
IsProgramSeries = true,
- OriginalAirDate = new DateTime(2018, 12, 6)
+ OriginalAirDate = new DateTime(2018, 12, 6, 0, 0, 0, DateTimeKind.Local)
});
data.Add(
@@ -70,7 +70,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
{
Name = "The Big Bang Theory",
IsProgramSeries = true,
- OriginalAirDate = new DateTime(2018, 12, 6),
+ OriginalAirDate = new DateTime(2018, 12, 6, 0, 0, 0, DateTimeKind.Local),
EpisodeTitle = "The VCR Illumination"
});
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index 143020d43..3e7d6ed1d 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -40,7 +40,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
await localizationManager.LoadAll();
var cultures = localizationManager.GetCultures().ToList();
- Assert.Equal(189, cultures.Count);
+ Assert.Equal(190, cultures.Count);
var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName.Equals("de", StringComparison.Ordinal));
Assert.NotNull(germany);
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs
index 827365363..3396a94e5 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs
@@ -40,7 +40,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(MediaTypeNames.Text.Html, response.Content.Headers.ContentType?.MediaType);
StreamReader reader = new StreamReader(typeof(TestPlugin).Assembly.GetManifestResourceStream("Jellyfin.Server.Integration.Tests.TestPage.html")!);
- Assert.Equal(await response.Content.ReadAsStringAsync(), reader.ReadToEnd());
+ Assert.Equal(await response.Content.ReadAsStringAsync().ConfigureAwait(false), await reader.ReadToEndAsync().ConfigureAwait(false));
}
[Fact]
diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
index 9d7b447ed..5b884cddf 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
+++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj
@@ -9,9 +9,9 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="Xunit.Priority" Version="1.1.6" />
diff --git a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
index 976e19d46..3d34a18e7 100644
--- a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
@@ -3,7 +3,6 @@ using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using Emby.Server.Implementations;
-using Emby.Server.Implementations.IO;
using MediaBrowser.Common;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
@@ -67,7 +66,7 @@ namespace Jellyfin.Server.Integration.Tests
var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths);
ILoggerFactory loggerFactory = new SerilogLoggerFactory();
- var serviceCollection = new ServiceCollection();
+
_disposableComponents.Add(loggerFactory);
// Create the app host and initialize it
@@ -75,11 +74,10 @@ namespace Jellyfin.Server.Integration.Tests
appPaths,
loggerFactory,
commandLineOpts,
- new ConfigurationBuilder().Build(),
- new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
- serviceCollection);
+ new ConfigurationBuilder().Build());
_disposableComponents.Add(appHost);
- appHost.Init();
+ var serviceCollection = new ServiceCollection();
+ appHost.Init(serviceCollection);
// Configure the web host builder
Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths);
diff --git a/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs b/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs
index 0a463cfa3..bf74efa09 100644
--- a/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/TestAppHost.cs
@@ -2,9 +2,7 @@ using System.Collections.Generic;
using System.Reflection;
using Emby.Server.Implementations;
using MediaBrowser.Controller;
-using MediaBrowser.Model.IO;
using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Integration.Tests
@@ -21,22 +19,16 @@ namespace Jellyfin.Server.Integration.Tests
/// <param name="loggerFactory">The <see cref="ILoggerFactory" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="startup">The <see cref="IConfiguration" /> to be used by the <see cref="CoreAppHost" />.</param>
- /// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
- /// <param name="collection">The <see cref="IServiceCollection"/> to be used by the <see cref="CoreAppHost"/>.</param>
public TestAppHost(
IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
IStartupOptions options,
- IConfiguration startup,
- IFileSystem fileSystem,
- IServiceCollection collection)
+ IConfiguration startup)
: base(
applicationPaths,
loggerFactory,
options,
- startup,
- fileSystem,
- collection)
+ startup)
{
}
diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
index 67ae0e080..29d7646a6 100644
--- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
+++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj
@@ -10,9 +10,9 @@
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
index 94294c8bf..edf9e0fef 100644
--- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
+++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj
@@ -13,7 +13,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />