diff options
Diffstat (limited to 'tests')
82 files changed, 2391 insertions, 498 deletions
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs index de03aa5f5..cd03958b6 100644 --- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs @@ -136,7 +136,7 @@ namespace Jellyfin.Api.Tests.Auth _jellyfinAuthServiceMock.Setup( a => a.Authenticate( It.IsAny<HttpRequest>())) - .Returns(authorizationInfo); + .Returns(Task.FromResult(authorizationInfo)); return authorizationInfo; } diff --git a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs index a62fd8d5a..23c51999f 100644 --- a/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandlerTests.cs @@ -4,6 +4,7 @@ using AutoFixture; using AutoFixture.AutoMoq; using Jellyfin.Api.Auth.DefaultAuthorizationPolicy; using Jellyfin.Api.Constants; +using Jellyfin.Server.Implementations.Security; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; @@ -49,5 +50,61 @@ namespace Jellyfin.Api.Tests.Auth.DefaultAuthorizationPolicy await _sut.HandleAsync(context); Assert.True(context.HasSucceeded); } + + [Theory] + [MemberData(nameof(GetParts_ValidAuthHeader_Success_Data))] + public void GetParts_ValidAuthHeader_Success(string input, Dictionary<string, string> parts) + { + var dict = AuthorizationContext.GetParts(input); + foreach (var (key, value) in parts) + { + Assert.Equal(dict[key], value); + } + } + + private static TheoryData<string, Dictionary<string, string>> GetParts_ValidAuthHeader_Success_Data() + { + var data = new TheoryData<string, Dictionary<string, string>>(); + + data.Add( + "x=\"123,123\",y=\"123\"", + new Dictionary<string, string> + { + { "x", "123,123" }, + { "y", "123" } + }); + + data.Add( + "x=\"123,123\", y=\"123\",z=\"'hi'\"", + new Dictionary<string, string> + { + { "x", "123,123" }, + { "y", "123" }, + { "z", "'hi'" } + }); + + data.Add( + "x=\"ab\"", + new Dictionary<string, string> + { + { "x", "ab" } + }); + + data.Add( + "param=Hörbücher", + new Dictionary<string, string> + { + { "param", "Hörbücher" } + }); + + data.Add( + "param=%22%Hörbücher", + new Dictionary<string, string> + { + { "param", "\"%Hörbücher" } + }); + + return data; + } } } diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 839cfb280..0c36e81cc 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -8,9 +8,6 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> @@ -18,12 +15,12 @@ <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="5.0.6" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.9" /> <PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> <PackageReference Include="Moq" Version="4.16.1" /> </ItemGroup> diff --git a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs b/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs index e6c325bac..18d3f9763 100644 --- a/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs +++ b/tests/Jellyfin.Common.Tests/Cryptography/PasswordHashTests.cs @@ -171,11 +171,11 @@ namespace Jellyfin.Common.Tests.Cryptography [InlineData("$PBKDF2$=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter [InlineData("$PBKDF2$=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter - [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $ - [InlineData("$PBKDF2$iterations=$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Extra segment - [InlineData("$PBKDF2$iterations=$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment - [InlineData("$PBKDF2$iterations=$invalidstalt$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid salt - [InlineData("$PBKDF2$iterations=$69F420$invalid hash")] // Invalid hash + [InlineData("$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $ + [InlineData("$PBKDF2$iterations=1000$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Extra segment + [InlineData("$PBKDF2$iterations=1000$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment + [InlineData("$PBKDF2$iterations=1000$invalidstalt$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid salt + [InlineData("$PBKDF2$iterations=1000$69F420$invalid hash")] // Invalid hash [InlineData("$PBKDF2$69F420$")] // Empty hash public static void Parse_InvalidFormat_ThrowsFormatException(string passwordHash) { diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 8018b2966..8e6b07716 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -8,17 +8,15 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> + <PackageReference Include="FsCheck.Xunit" Version="2.16.1" /> </ItemGroup> <!-- Code Analyzers --> diff --git a/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs deleted file mode 100644 index 9ded01f2b..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Text.Json; -using MediaBrowser.Common.Json.Converters; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public static class JsonBoolNumberTests - { - [Theory] - [InlineData("1", true)] - [InlineData("0", false)] - [InlineData("2", true)] - [InlineData("true", true)] - [InlineData("false", false)] - public static void Deserialize_Number_Valid_Success(string input, bool? output) - { - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonBoolNumberConverter()); - var value = JsonSerializer.Deserialize<bool>(input, options); - Assert.Equal(value, output); - } - - [Theory] - [InlineData(true, "true")] - [InlineData(false, "false")] - public static void Serialize_Bool_Success(bool input, string output) - { - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonBoolNumberConverter()); - var value = JsonSerializer.Serialize(input, options); - Assert.Equal(value, output); - } - } -}
\ No newline at end of file diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index ad1627698..a5778b59c 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -8,18 +8,15 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="Moq" Version="4.16.1" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> </ItemGroup> <!-- Code Analyzers --> diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index f7c21f072..5a48631c2 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -3,18 +3,15 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="Moq" Version="4.16.1" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> </ItemGroup> <!-- Code Analyzers --> diff --git a/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs b/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs index 0adf098c3..7730841a1 100644 --- a/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs +++ b/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs @@ -1,11 +1,10 @@ using System; using System.Linq; -using MediaBrowser.Controller.Sorting; using Xunit; -namespace Jellyfin.Controller.Tests +namespace Jellyfin.Extensions.Tests { - public class AlphanumComparatorTests + public class AlphanumericComparatorTests { // InlineData is pre-sorted [Theory] @@ -20,10 +19,10 @@ namespace Jellyfin.Controller.Tests [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567892")] [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891a")] [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891b")] - public void AlphanumComparatorTest(params string?[] strings) + public void AlphanumericComparatorTest(params string?[] strings) { var copy = strings.Reverse().ToArray(); - Array.Sort(copy, new AlphanumComparator()); + Array.Sort(copy, new AlphanumericComparator()); Assert.True(strings.SequenceEqual(copy)); } } diff --git a/tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs index 9903409fa..6fdca4694 100644 --- a/tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs +++ b/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; -using MediaBrowser.Common.Extensions; using Xunit; -namespace Jellyfin.Common.Tests.Extensions +namespace Jellyfin.Extensions.Tests { public static class CopyToExtensionsTests { diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj new file mode 100644 index 000000000..72cd9aa45 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj @@ -0,0 +1,35 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + <IsPackable>false</IsPackable> + <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.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> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="coverlet.collector" Version="3.1.0"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="FsCheck.Xunit" Version="2.16.1" /> + </ItemGroup> + + <!-- Code Analyzers --> + <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> + <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="../../MediaBrowser.Model/MediaBrowser.Model.csproj" /> + <ProjectReference Include="../../src/Jellyfin.Extensions/Jellyfin.Extensions.csproj" /> + </ItemGroup> + +</Project> diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolNumberTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolNumberTests.cs new file mode 100644 index 000000000..d0e3e9456 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolNumberTests.cs @@ -0,0 +1,45 @@ +using System.Globalization; +using System.Text.Json; +using FsCheck; +using FsCheck.Xunit; +using Jellyfin.Extensions.Json.Converters; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public class JsonBoolNumberTests + { + private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() + { + Converters = + { + new JsonBoolNumberConverter() + } + }; + + [Theory] + [InlineData("1", true)] + [InlineData("0", false)] + [InlineData("2", true)] + [InlineData("true", true)] + [InlineData("false", false)] + public void Deserialize_Number_Valid_Success(string input, bool? output) + { + var value = JsonSerializer.Deserialize<bool>(input, _jsonOptions); + Assert.Equal(value, output); + } + + [Theory] + [InlineData(true, "true")] + [InlineData(false, "false")] + public void Serialize_Bool_Success(bool input, string output) + { + var value = JsonSerializer.Serialize(input, _jsonOptions); + Assert.Equal(value, output); + } + + [Property] + public Property Deserialize_NonZeroInt_True(NonZeroInt input) + => JsonSerializer.Deserialize<bool>(input.ToString(), _jsonOptions).ToProperty(); + } +} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs index ca300401d..f2ca2ff08 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Text.Json; using System.Text.Json.Serialization; -using Jellyfin.Common.Tests.Models; +using Jellyfin.Extensions.Tests.Json.Models; using MediaBrowser.Model.Session; using Xunit; -namespace Jellyfin.Common.Tests.Json +namespace Jellyfin.Extensions.Tests.Json.Converters { public static class JsonCommaDelimitedArrayTests { diff --git a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedIReadOnlyListTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedIReadOnlyListTests.cs index 34ad9bac7..92886dcd2 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedIReadOnlyListTests.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedIReadOnlyListTests.cs @@ -1,10 +1,10 @@ -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; -using Jellyfin.Common.Tests.Models; +using Jellyfin.Extensions.Tests.Json.Models; using MediaBrowser.Model.Session; using Xunit; -namespace Jellyfin.Common.Tests.Json +namespace Jellyfin.Extensions.Tests.Json.Converters { public static class JsonCommaDelimitedIReadOnlyListTests { diff --git a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonGuidConverterTests.cs index dbfad3c2f..8465d465a 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonGuidConverterTests.cs @@ -1,9 +1,9 @@ using System; using System.Text.Json; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json.Converters; using Xunit; -namespace Jellyfin.Common.Tests.Json +namespace Jellyfin.Extensions.Tests.Json.Converters { public class JsonGuidConverterTests { diff --git a/tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs index 955d296cc..af9227de2 100644 --- a/tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs @@ -1,9 +1,10 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Model.Entities; using Xunit; -namespace Jellyfin.Model.Tests.Entities +namespace Jellyfin.Extensions.Tests.Json.Converters { public class JsonLowerCaseConverterTests { diff --git a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonNullableGuidConverterTests.cs index cb3b66c4c..b0dbc09e4 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonNullableGuidConverterTests.cs @@ -1,9 +1,9 @@ using System; using System.Text.Json; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json.Converters; using Xunit; -namespace Jellyfin.Common.Tests.Json +namespace Jellyfin.Extensions.Tests.Json.Converters { public class JsonNullableGuidConverterTests { diff --git a/tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonStringConverterTests.cs index fd77694b3..655e07074 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonStringConverterTests.cs @@ -1,19 +1,18 @@ -using System.Text.Json; -using MediaBrowser.Common.Json.Converters; +using System.Text.Json; +using Jellyfin.Extensions.Json.Converters; using Xunit; -namespace Jellyfin.Common.Tests.Json +namespace Jellyfin.Extensions.Tests.Json.Converters { public class JsonStringConverterTests { - private readonly JsonSerializerOptions _jsonSerializerOptions - = new () + private readonly JsonSerializerOptions _jsonSerializerOptions = new () + { + Converters = { - Converters = - { - new JsonStringConverter() - } - }; + new JsonStringConverter() + } + }; [Theory] [InlineData("\"test\"", "test")] @@ -36,4 +35,4 @@ namespace Jellyfin.Common.Tests.Json Assert.Equal(deserialized, output); } } -}
\ No newline at end of file +} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonVersionConverterTests.cs index f2cefdbf8..5fbac7eab 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonVersionConverterTests.cs @@ -1,9 +1,9 @@ -using System; +using System; using System.Text.Json; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json.Converters; using Xunit; -namespace Jellyfin.Common.Tests.Json +namespace Jellyfin.Extensions.Tests.Json.Converters { public class JsonVersionConverterTests { diff --git a/tests/Jellyfin.Common.Tests/Models/GenericBodyArrayModel.cs b/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyArrayModel.cs index 276e1bfbe..ef135278f 100644 --- a/tests/Jellyfin.Common.Tests/Models/GenericBodyArrayModel.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyArrayModel.cs @@ -1,8 +1,8 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json.Converters; -namespace Jellyfin.Common.Tests.Models +namespace Jellyfin.Extensions.Tests.Json.Models { /// <summary> /// The generic body model. diff --git a/tests/Jellyfin.Common.Tests/Models/GenericBodyIReadOnlyListModel.cs b/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyIReadOnlyListModel.cs index 627454b25..8e7b5a35b 100644 --- a/tests/Jellyfin.Common.Tests/Models/GenericBodyIReadOnlyListModel.cs +++ b/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyIReadOnlyListModel.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json.Converters; -namespace Jellyfin.Common.Tests.Models +namespace Jellyfin.Extensions.Tests.Json.Models { /// <summary> /// The generic body <c>IReadOnlyList</c> model. diff --git a/tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/ShuffleExtensionsTests.cs index cbdbcf112..c72216d94 100644 --- a/tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs +++ b/tests/Jellyfin.Extensions.Tests/ShuffleExtensionsTests.cs @@ -1,8 +1,7 @@ using System; -using MediaBrowser.Common.Extensions; using Xunit; -namespace Jellyfin.Common.Tests.Extensions +namespace Jellyfin.Extensions.Tests { public static class ShuffleExtensionsTests { diff --git a/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs new file mode 100644 index 000000000..d1aa2e476 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs @@ -0,0 +1,18 @@ +using System; +using Xunit; + +namespace Jellyfin.Extensions.Tests +{ + public class StringExtensionsTests + { + [Theory] + [InlineData("", '_', 0)] + [InlineData("___", '_', 3)] + [InlineData("test\x00", '\x00', 1)] + [InlineData("Imdb=tt0119567|Tmdb=330|TmdbCollection=328", '|', 2)] + public void ReadOnlySpan_Count_Success(string str, char needle, int count) + { + Assert.Equal(count, str.AsSpan().Count(needle)); + } + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index 39fd8afda..d1854a3c8 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -9,15 +9,18 @@ namespace Jellyfin.MediaEncoding.Tests { public class EncoderValidatorTests { + private readonly EncoderValidator _encoderValidator = new EncoderValidator(new NullLogger<EncoderValidatorTests>(), "ffmpeg"); + [Theory] [ClassData(typeof(GetFFmpegVersionTestData))] public void GetFFmpegVersionTest(string versionOutput, Version? version) { - var val = new EncoderValidator(new NullLogger<EncoderValidatorTests>()); - Assert.Equal(version, val.GetFFmpegVersion(versionOutput)); + Assert.Equal(version, _encoderValidator.GetFFmpegVersionInternal(versionOutput)); } [Theory] + [InlineData(EncoderValidatorTestsData.FFmpegV44Output, true)] + [InlineData(EncoderValidatorTestsData.FFmpegV432Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV431Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV43Output, true)] [InlineData(EncoderValidatorTestsData.FFmpegV421Output, true)] @@ -28,14 +31,15 @@ namespace Jellyfin.MediaEncoding.Tests [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] public void ValidateVersionInternalTest(string versionOutput, bool valid) { - var val = new EncoderValidator(new NullLogger<EncoderValidatorTests>()); - Assert.Equal(valid, val.ValidateVersionInternal(versionOutput)); + Assert.Equal(valid, _encoderValidator.ValidateVersionInternal(versionOutput)); } private class GetFFmpegVersionTestData : IEnumerable<object?[]> { public IEnumerator<object?[]> GetEnumerator() { + yield return new object?[] { EncoderValidatorTestsData.FFmpegV44Output, new Version(4, 4) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegV432Output, new Version(4, 3, 2) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV431Output, new Version(4, 3, 1) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV43Output, new Version(4, 3) }; yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index 9f5bef9a8..02bf046ed 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -2,6 +2,30 @@ namespace Jellyfin.MediaEncoding.Tests { internal static class EncoderValidatorTestsData { + public const string FFmpegV44Output = @"ffmpeg version 4.4-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers +built with gcc 10.3.0 (Rev5, Built by MSYS2 project) +configuration: --disable-static --enable-shared --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls +libavutil 56. 70.100 / 56. 70.100 +libavcodec 58.134.100 / 58.134.100 +libavformat 58. 76.100 / 58. 76.100 +libavdevice 58. 13.100 / 58. 13.100 +libavfilter 7.110.100 / 7.110.100 +libswscale 5. 9.100 / 5. 9.100 +libswresample 3. 9.100 / 3. 9.100 +libpostproc 55. 9.100 / 55. 9.100"; + + public const string FFmpegV432Output = @"ffmpeg version n4.3.2-Jellyfin Copyright (c) 2000-2021 the FFmpeg developers +built with gcc 10.2.0 (Rev9, Built by MSYS2 project) +configuration: --disable-static --enable-shared --cc='ccache gcc' --cxx='ccache g++' --extra-version=Jellyfin --disable-ffplay --disable-debug --enable-lto --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-zlib --enable-sdl2 --enable-fontconfig --enable-gmp --enable-libass --enable-libzimg --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libdav1d --enable-opencl --enable-dxva2 --enable-d3d11va --enable-amf --enable-libmfx --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvenc --enable-nvdec --enable-ffnvcodec --enable-gnutls +libavutil 56. 51.100 / 56. 51.100 +libavcodec 58. 91.100 / 58. 91.100 +libavformat 58. 45.100 / 58. 45.100 +libavdevice 58. 10.100 / 58. 10.100 +libavfilter 7. 85.100 / 7. 85.100 +libswscale 5. 7.100 / 5. 7.100 +libswresample 3. 7.100 / 3. 7.100 +libpostproc 55. 7.100 / 55. 7.100"; + public const string FFmpegV431Output = @"ffmpeg version n4.3.1 Copyright (c) 2000-2020 the FFmpeg developers built with gcc 10.1.0 (GCC) configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-avisynth --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmfx --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librav1e --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3 diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs index 415682e85..2955104a2 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs @@ -1,7 +1,7 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.MediaEncoding.Probing; using Xunit; @@ -14,9 +14,10 @@ namespace Jellyfin.MediaEncoding.Tests public async Task Test(string fileName) { var path = Path.Join("Test Data", fileName); - using (var stream = File.OpenRead(path)) + await using (var stream = File.OpenRead(path)) { - await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false); + var res = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false); + Assert.NotNull(res); } } } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index 8321d0255..7ea503913 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -8,9 +8,6 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> @@ -21,10 +18,10 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> </ItemGroup> <!-- Code Analyzers --> diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index 69e2aa437..fcb85a3ac 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -1,7 +1,10 @@ +using System; +using System.Globalization; using System.IO; using System.Text.Json; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.MediaEncoding.Probing; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging.Abstractions; @@ -17,9 +20,9 @@ namespace Jellyfin.MediaEncoding.Tests.Probing [Fact] public void GetMediaInfo_MetaData_Success() { - var bytes = File.ReadAllBytes("Test Data/Probing/some_matadata.json"); + var bytes = File.ReadAllBytes("Test Data/Probing/video_metadata.json"); var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions); - MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/some_matadata.mkv", MediaProtocol.File); + MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_metadata.mkv", MediaProtocol.File); Assert.Single(res.MediaStreams); @@ -52,5 +55,75 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Assert.Empty(res.Chapters); Assert.Equal("Just color bars", res.Overview); } + + [Fact] + public void GetMediaInfo_MusicVideo_Success() + { + var bytes = File.ReadAllBytes("Test Data/Probing/music_video_metadata.json"); + var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions); + MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/music_video.mkv", MediaProtocol.File); + + Assert.Equal("The Title", res.Name); + Assert.Equal("Title, The", res.ForcedSortName); + Assert.Single(res.Artists); + Assert.Equal("The Artist", res.Artists[0]); + Assert.Equal("Album", res.Album); + Assert.Equal(2021, res.ProductionYear); + Assert.True(res.PremiereDate.HasValue); + Assert.Equal(DateTime.Parse("2021-01-01T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate); + } + + [Fact] + public void GetMediaInfo_GivenOriginalDateContainsOnlyYear_Success() + { + var bytes = File.ReadAllBytes("Test Data/Probing/music_year_only_metadata.json"); + var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions); + MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, null, true, "Test Data/Probing/music.flac", MediaProtocol.File); + + Assert.Equal("Baker Street", res.Name); + Assert.Single(res.Artists); + Assert.Equal("Gerry Rafferty", res.Artists[0]); + Assert.Equal("City to City", res.Album); + Assert.Equal(1978, res.ProductionYear); + Assert.True(res.PremiereDate.HasValue); + Assert.Equal(DateTime.Parse("1978-01-01T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate); + Assert.Contains("Electronic", res.Genres); + Assert.Contains("Ambient", res.Genres); + Assert.Contains("Pop", res.Genres); + Assert.Contains("Jazz", res.Genres); + } + + [Fact] + public void GetMediaInfo_Music_Success() + { + var bytes = File.ReadAllBytes("Test Data/Probing/music_metadata.json"); + var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions); + MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, null, true, "Test Data/Probing/music.flac", MediaProtocol.File); + + Assert.Equal("UP NO MORE", res.Name); + Assert.Single(res.Artists); + Assert.Equal("TWICE", res.Artists[0]); + Assert.Equal("Eyes wide open", res.Album); + Assert.Equal(2020, res.ProductionYear); + Assert.True(res.PremiereDate.HasValue); + Assert.Equal(DateTime.Parse("2020-10-26T00:00Z", DateTimeFormatInfo.CurrentInfo).ToUniversalTime(), res.PremiereDate); + Assert.Equal(22, res.People.Length); + Assert.Equal("Krysta Youngs", res.People[0].Name); + Assert.Equal(PersonType.Composer, res.People[0].Type); + Assert.Equal("Julia Ross", res.People[1].Name); + Assert.Equal(PersonType.Composer, res.People[1].Type); + Assert.Equal("Yiwoomin", res.People[2].Name); + Assert.Equal(PersonType.Composer, res.People[2].Type); + Assert.Equal("Ji-hyo Park", res.People[3].Name); + Assert.Equal(PersonType.Lyricist, res.People[3].Type); + Assert.Equal("Yiwoomin", res.People[4].Name); + Assert.Equal(PersonType.Actor, res.People[4].Type); + Assert.Equal("Electric Piano", res.People[4].Role); + Assert.Equal(4, res.Genres.Length); + Assert.Contains("Electronic", res.Genres); + Assert.Contains("Trance", res.Genres); + Assert.Contains("Dance", res.Genres); + Assert.Contains("Jazz", res.Genres); + } } } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_metadata.json new file mode 100644 index 000000000..6530629fe --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_metadata.json @@ -0,0 +1,144 @@ +{ + "streams": [ + { + "index": 0, + "codec_name": "flac", + "codec_long_name": "FLAC (Free Lossless Audio Codec)", + "codec_type": "audio", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "sample_fmt": "s16", + "sample_rate": "44100", + "channels": 2, + "channel_layout": "stereo", + "bits_per_sample": 0, + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/44100", + "start_pts": 0, + "start_time": "0.000000", + "duration_ts": 9447984, + "duration": "214.240000", + "bits_per_raw_sample": "16", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + } + }, + { + "index": 1, + "codec_name": "mjpeg", + "codec_long_name": "Motion JPEG", + "profile": "Baseline", + "codec_type": "video", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "width": 500, + "height": 500, + "coded_width": 500, + "coded_height": 500, + "closed_captions": 0, + "has_b_frames": 0, + "sample_aspect_ratio": "1:1", + "display_aspect_ratio": "1:1", + "pix_fmt": "yuvj420p", + "level": -99, + "color_range": "pc", + "color_space": "bt470bg", + "chroma_location": "center", + "refs": 1, + "r_frame_rate": "90000/1", + "avg_frame_rate": "0/0", + "time_base": "1/90000", + "start_pts": 0, + "start_time": "0.000000", + "duration_ts": 19281600, + "duration": "214.240000", + "bits_per_raw_sample": "8", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 1, + "timed_thumbnails": 0 + }, + "tags": { + "comment": "Cover (front)" + } + } + ], + "format": { + "filename": "03 UP NO MORE.flac", + "nb_streams": 2, + "nb_programs": 0, + "format_name": "flac", + "format_long_name": "raw FLAC", + "start_time": "0.000000", + "duration": "214.240000", + "size": "28714641", + "bit_rate": "1072242", + "probe_score": 100, + "tags": { + "MUSICBRAINZ_RELEASEGROUPID": "aa05ff10-8589-4c9c-a0d4-6b024f4e4556", + "ORIGINALDATE": "2020-10-26", + "ORIGINALYEAR": "2020", + "RELEASETYPE": "album", + "MUSICBRAINZ_ALBUMID": "222e6610-75c9-400e-8dc3-bb61f9fc5ca7", + "SCRIPT": "Latn", + "ALBUM": "Eyes wide open", + "RELEASECOUNTRY": "JP", + "BARCODE": "190295105280", + "LABEL": "JYP Entertainment", + "RELEASESTATUS": "official", + "DATE": "2020-10-26", + "MUSICBRAINZ_ALBUMARTISTID": "8da127cc-c432-418f-b356-ef36210d82ac", + "album_artist": "TWICE", + "ALBUMARTISTSORT": "TWICE", + "TOTALDISCS": "1", + "TOTALTRACKS": "13", + "MEDIA": "Digital Media", + "disc": "1", + "MUSICBRAINZ_TRACKID": "7d1a1044-b564-480d-9df3-22f9656fdb97", + "TITLE": "UP NO MORE", + "ISRC": "US5TA2000136", + "PERFORMER": "Yiwoomin (electric piano);Yiwoomin (synthesizer);Yiwoomin (bass);Yiwoomin (guitar);TWICE;Tzu-yu Chou (vocals);Momo Hirai (vocals);Na-yeon Im (vocals);Da-hyun Kim (vocals);Sana Minatozaki (vocals);Mina Myoui (vocals);Ji-hyo Park (vocals);Chae-young Son (vocals);Jeong-yeon Yoo (vocals);Perrie (background vocals)", + "MIXER": "Bong Won Shin", + "ARRANGER": "Krysta Youngs;Julia Ross;Yiwoomin", + "MUSICBRAINZ_WORKID": "02b37083-0337-4721-9f17-bf31971043e8", + "LANGUAGE": "kor;eng", + "WORK": "Up No More", + "COMPOSER": "Krysta Youngs;Julia Ross;Yiwoomin", + "COMPOSERSORT": "Krysta Youngs;Ross, Julia;Yiwoomin", + "LYRICIST": "Ji-hyo Park", + "MUSICBRAINZ_ARTISTID": "8da127cc-c432-418f-b356-ef36210d82ac", + "ARTIST": "TWICE", + "ARTISTSORT": "TWICE", + "ARTISTS": "TWICE", + "MUSICBRAINZ_RELEASETRACKID": "ad49b840-da9e-4e7c-924b-29fdee187052", + "track": "3", + "GENRE": "Electronic;Trance;Dance;Jazz", + "WEBSITE": "http://twice.jype.com/;http://www.twicejapan.com/", + "ACOUSTID_ID": "aae2e972-108c-4d0c-8e31-9d078283e3dc", + "MOOD": "Not acoustic;Not aggressive;Electronic;Happy;Party;Not relaxed;Not sad", + "TRACKTOTAL": "13", + "DISCTOTAL": "1" + } + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_video_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_video_metadata.json new file mode 100644 index 000000000..97d6600a4 --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_video_metadata.json @@ -0,0 +1,111 @@ +{ + "streams": [ + { + "index": 0, + "codec_name": "h264", + "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", + "profile": "High", + "codec_type": "video", + "codec_time_base": "1001/48000", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "width": 1920, + "height": 1080, + "coded_width": 1920, + "coded_height": 1088, + "closed_captions": 0, + "has_b_frames": 0, + "sample_aspect_ratio": "1:1", + "display_aspect_ratio": "16:9", + "pix_fmt": "yuv420p", + "level": 42, + "chroma_location": "left", + "field_order": "progressive", + "refs": 1, + "is_avc": "true", + "nal_length_size": "4", + "r_frame_rate": "24000/1001", + "avg_frame_rate": "24000/1001", + "time_base": "1/1000", + "start_pts": 0, + "start_time": "0.000000", + "bits_per_raw_sample": "8", + "disposition": { + "default": 1, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + }, + "tags": { + "language": "eng" + } + }, + { + "index": 1, + "codec_name": "aac", + "codec_long_name": "AAC (Advanced Audio Coding)", + "profile": "LC", + "codec_type": "audio", + "codec_time_base": "1/48000", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "sample_fmt": "fltp", + "sample_rate": "48000", + "channels": 2, + "channel_layout": "stereo", + "bits_per_sample": 0, + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/1000", + "start_pts": 0, + "start_time": "0.000000", + "disposition": { + "default": 1, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + }, + "tags": { + "language": "eng" + } + } + ], + "chapters": [ + ], + "format": { + "filename": "music_video.mkv", + "nb_streams": 2, + "nb_programs": 0, + "format_name": "matroska,webm", + "format_long_name": "Matroska / WebM", + "start_time": "0.000000", + "duration": "180.000000", + "size": "500000000", + "bit_rate": "22222222", + "probe_score": 100, + "tags": { + "TITLE-eng": "The Title", + "TITLESORT": "Title, The", + "ARTIST": "The Artist", + "ARTISTSORT": "Artist, The", + "ALBUM": "Album", + "DATE_RELEASED": "2021-01-01" + } + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_year_only_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_year_only_metadata.json new file mode 100644 index 000000000..ddf890c45 --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/music_year_only_metadata.json @@ -0,0 +1,147 @@ +{ + "streams": [ + { + "index": 0, + "codec_name": "flac", + "codec_long_name": "FLAC (Free Lossless Audio Codec)", + "codec_type": "audio", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "sample_fmt": "s16", + "sample_rate": "44100", + "channels": 2, + "channel_layout": "stereo", + "bits_per_sample": 0, + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/44100", + "start_pts": 0, + "start_time": "0.000000", + "duration_ts": 16394616, + "duration": "371.760000", + "bits_per_raw_sample": "16", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + } + }, + { + "index": 1, + "codec_name": "mjpeg", + "codec_long_name": "Motion JPEG", + "profile": "Baseline", + "codec_type": "video", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "width": 500, + "height": 498, + "coded_width": 500, + "coded_height": 498, + "closed_captions": 0, + "has_b_frames": 0, + "sample_aspect_ratio": "1:1", + "display_aspect_ratio": "250:249", + "pix_fmt": "yuvj420p", + "level": -99, + "color_range": "pc", + "color_space": "bt470bg", + "chroma_location": "center", + "refs": 1, + "r_frame_rate": "90000/1", + "avg_frame_rate": "0/0", + "time_base": "1/90000", + "start_pts": 0, + "start_time": "0.000000", + "duration_ts": 33458400, + "duration": "371.760000", + "bits_per_raw_sample": "8", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 1, + "timed_thumbnails": 0 + }, + "tags": { + "comment": "Cover (front)" + } + } + ], + "format": { + "filename": "02 Baker Street.flac", + "nb_streams": 2, + "nb_programs": 0, + "format_name": "flac", + "format_long_name": "raw FLAC", + "start_time": "0.000000", + "duration": "371.760000", + "size": "37072649", + "bit_rate": "797775", + "probe_score": 100, + "tags": { + "MUSICBRAINZ_RELEASEGROUPID": "238c3fb4-5792-342b-b217-02f66298b424", + "ORIGINALDATE": "1978", + "ORIGINALYEAR": "1978", + "RELEASETYPE": "album", + "MUSICBRAINZ_ALBUMID": "30156786-e511-3106-ac95-66f0e880b24b", + "ASIN": "B000007O5H", + "MUSICBRAINZ_ALBUMARTISTID": "563201cb-721c-4cfb-acca-c1ba69e3d1fb", + "album_artist": "Gerry Rafferty", + "ALBUMARTISTSORT": "Rafferty, Gerry", + "LABEL": "Liberty EMI Records UK", + "CATALOGNUMBER": "CDP 7 46049 2", + "DATE": "1989-07-26", + "RELEASECOUNTRY": "GB", + "BARCODE": "077774604925", + "ALBUM": "City to City", + "SCRIPT": "Latn", + "RELEASESTATUS": "official", + "TOTALDISCS": "1", + "disc": "1", + "MEDIA": "CD", + "TOTALTRACKS": "10", + "MUSICBRAINZ_TRACKID": "9235e22e-afbd-48f7-b329-21dae6da2810", + "ISRC": "GBAYE1100924;GBAYE7800619", + "PERFORMER": "Hugh Burns (electric guitar);Nigel Jenkins (electric guitar);Tommy Eyre (keyboard and Moog);Glen LeFleur (percussion);Raphael Ravenscroft (saxophone);Henry Spinetti (drums (drum set));Gary Taylor (bass);Gerry Rafferty (lead vocals)", + "ARRANGER": "Graham Preskett", + "MIXER": "Declan O’Doherty", + "PRODUCER": "Hugh Murphy;Gerry Rafferty", + "MUSICBRAINZ_WORKID": "a9eb3c45-784c-3c32-860c-4b406f03961b", + "LANGUAGE": "eng", + "WORK": "Baker Street", + "COMPOSER": "Gerry Rafferty", + "COMPOSERSORT": "Rafferty, Gerry", + "LYRICIST": "Gerry Rafferty", + "TITLE": "Baker Street", + "MUSICBRAINZ_ARTISTID": "563201cb-721c-4cfb-acca-c1ba69e3d1fb", + "ARTIST": "Gerry Rafferty", + "ARTISTSORT": "Rafferty, Gerry", + "ARTISTS": "Gerry Rafferty", + "MUSICBRAINZ_RELEASETRACKID": "407cf7f7-440d-3e76-8b89-8686198868ea", + "track": "2", + "GENRE": "Electronic;Ambient;Pop;Jazz", + "WEBSITE": "http://www.gerryrafferty.com/", + "ACOUSTID_ID": "68f8d979-a659-4aa0-a216-eb3721a951eb", + "MOOD": "Acoustic;Not aggressive;Not electronic;Not happy;Party;Relaxed;Not sad", + "TRACKTOTAL": "10", + "DISCTOTAL": "1" + } + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/some_matadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json index 720fc5c8f..720fc5c8f 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/some_matadata.json +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs new file mode 100644 index 000000000..ce9ecea6a --- /dev/null +++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; +using MediaBrowser.Model.Entities; +using Xunit; + +namespace Jellyfin.Model.Tests.Entities +{ + public class MediaStreamTests + { + public static IEnumerable<object[]> Get_DisplayTitle_TestData() + { + return new List<object[]> + { + new object[] + { + new MediaStream + { + Type = MediaStreamType.Subtitle, + Title = "English", + Language = string.Empty, + IsForced = false, + IsDefault = false, + Codec = "ASS" + }, + "English - Und - ASS" + }, + new object[] + { + new MediaStream + { + Type = MediaStreamType.Subtitle, + Title = "English", + Language = string.Empty, + IsForced = false, + IsDefault = false, + Codec = string.Empty + }, + "English - Und" + }, + new object[] + { + new MediaStream + { + Type = MediaStreamType.Subtitle, + Title = "English", + Language = "EN", + IsForced = false, + IsDefault = false, + Codec = string.Empty + }, + "English" + }, + new object[] + { + new MediaStream + { + Type = MediaStreamType.Subtitle, + Title = "English", + Language = "EN", + IsForced = true, + IsDefault = true, + Codec = "SRT" + }, + "English - Default - Forced - SRT" + }, + new object[] + { + new MediaStream + { + Type = MediaStreamType.Subtitle, + Title = null, + Language = null, + IsForced = false, + IsDefault = false, + Codec = null + }, + "Und" + } + }; + } + + [Theory] + [MemberData(nameof(Get_DisplayTitle_TestData))] + public void Get_DisplayTitle_should_return_valid_title(MediaStream mediaStream, string expected) + { + Assert.Equal(expected, mediaStream.DisplayTitle); + } + + [Theory] + [InlineData(null, null, false, null)] + [InlineData(null, 0, false, null)] + [InlineData(0, null, false, null)] + [InlineData(640, 480, false, "480p")] + [InlineData(640, 480, true, "480i")] + [InlineData(720, 576, false, "576p")] + [InlineData(720, 576, true, "576i")] + [InlineData(960, 540, false, "540p")] + [InlineData(960, 540, true, "540i")] + [InlineData(1280, 720, false, "720p")] + [InlineData(1280, 720, true, "720i")] + [InlineData(1920, 1080, false, "1080p")] + [InlineData(1920, 1080, true, "1080i")] + [InlineData(4096, 3072, false, "4K")] + [InlineData(8192, 6144, false, "8K")] + [InlineData(512, 384, false, "480p")] + [InlineData(576, 336, false, "480p")] + [InlineData(624, 352, false, "480p")] + [InlineData(640, 352, false, "480p")] + [InlineData(704, 396, false, "480p")] + [InlineData(720, 404, false, "480p")] + [InlineData(720, 480, false, "480p")] + [InlineData(768, 576, false, "576p")] + [InlineData(960, 720, false, "720p")] + [InlineData(1280, 528, false, "720p")] + [InlineData(1280, 532, false, "720p")] + [InlineData(1280, 534, false, "720p")] + [InlineData(1280, 536, false, "720p")] + [InlineData(1280, 544, false, "720p")] + [InlineData(1280, 690, false, "720p")] + [InlineData(1280, 694, false, "720p")] + [InlineData(1280, 696, false, "720p")] + [InlineData(1280, 716, false, "720p")] + [InlineData(1280, 718, false, "720p")] + [InlineData(1912, 792, false, "1080p")] + [InlineData(1916, 1076, false, "1080p")] + [InlineData(1918, 1080, false, "1080p")] + [InlineData(1920, 796, false, "1080p")] + [InlineData(1920, 800, false, "1080p")] + [InlineData(1920, 802, false, "1080p")] + [InlineData(1920, 804, false, "1080p")] + [InlineData(1920, 808, false, "1080p")] + [InlineData(1920, 816, false, "1080p")] + [InlineData(1920, 856, false, "1080p")] + [InlineData(1920, 960, false, "1080p")] + [InlineData(1920, 1024, false, "1080p")] + [InlineData(1920, 1040, false, "1080p")] + [InlineData(1920, 1072, false, "1080p")] + [InlineData(1440, 1072, false, "1080p")] + [InlineData(1440, 1080, false, "1080p")] + [InlineData(3840, 1600, false, "4K")] + [InlineData(3840, 1606, false, "4K")] + [InlineData(3840, 1608, false, "4K")] + [InlineData(3840, 2160, false, "4K")] + [InlineData(7680, 4320, false, "8K")] + public void GetResolutionText_Valid(int? width, int? height, bool interlaced, string expected) + { + var mediaStream = new MediaStream() + { + Width = width, + Height = height, + IsInterlaced = interlaced + }; + + Assert.Equal(expected, mediaStream.GetResolutionText()); + } + } +} diff --git a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs index 5864a0509..0a4e060df 100644 --- a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs +++ b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs @@ -1,3 +1,6 @@ +using System; +using FsCheck; +using FsCheck.Xunit; using MediaBrowser.Model.Extensions; using Xunit; @@ -10,9 +13,20 @@ namespace Jellyfin.Model.Tests.Extensions [InlineData("banana", "Banana")] [InlineData("Banana", "Banana")] [InlineData("ä", "Ä")] + [InlineData("\027", "\027")] public void StringHelper_ValidArgs_Success(string input, string expectedResult) { Assert.Equal(expectedResult, StringHelper.FirstToUpper(input)); } + + [Property] + public Property FirstToUpper_RandomArg_Correct(NonEmptyString input) + { + var result = StringHelper.FirstToUpper(input.Item); + + // We check IsLower instead of IsUpper because both return false for non-letters + return (!char.IsLower(result[0])).Label("First char is uppercase") + .And(input.Item.Length == 1 || result[1..].Equals(input.Item[1..], StringComparison.Ordinal)).Label("Remaining chars are unmodified"); + } } } diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index c5b51ef76..e9b7b1850 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -3,17 +3,15 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> + <PackageReference Include="FsCheck.Xunit" Version="2.16.1" /> </ItemGroup> <!-- Code Analyzers --> diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index ebb134fc3..a4ebab141 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -8,17 +8,14 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index 921c2b1f5..2873f6161 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -70,7 +70,8 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("Log Horizon 2/[HorribleSubs] Log Horizon 2 - 03 [720p].mkv", 3)] // digit in series name [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 - // TODO: [InlineData("Case Closed (1996-2007)/Case Closed - 317.mkv", 317)] // triple digit episode number + [InlineData("Season 2/Episode 21 - 94 Meetings.mp4", 21)] // Title starts with a number + // [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)] diff --git a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs index 89579c037..6d49ac832 100644 --- a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs @@ -21,7 +21,8 @@ namespace Jellyfin.Naming.Tests.TV [InlineData("[Baz-Bar]Foo - [1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)] [InlineData(@"/Foo/The.Series.Name.S01E04.WEBRip.x264-Baz[Bar]/the.series.name.s01e04.webrip.x264-Baz[Bar].mkv", "The.Series.Name", 1, 4)] [InlineData(@"Love.Death.and.Robots.S01.1080p.NF.WEB-DL.DDP5.1.x264-NTG/Love.Death.and.Robots.S01E01.Sonnies.Edge.1080p.NF.WEB-DL.DDP5.1.x264-NTG.mkv", "Love.Death.and.Robots", 1, 1)] - // TODO: [InlineData("[Baz-Bar]Foo - 01 - 12[1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)] + [InlineData("[YuiSubs] Tensura Nikki - Tensei Shitara Slime Datta Ken/[YuiSubs] Tensura Nikki - Tensei Shitara Slime Datta Ken - 12 (NVENC H.265 1080p).mkv", "Tensura Nikki - Tensei Shitara Slime Datta Ken", null, 12)] + [InlineData("[Baz-Bar]Foo - 01 - 12[1080p][Multiple Subtitle]/[Baz-Bar] Foo - 05 [1080p][Multiple Subtitle].mkv", "Foo", null, 5)] // TODO: [InlineData("E:\\Anime\\Yahari Ore no Seishun Love Comedy wa Machigatteiru\\Yahari Ore no Seishun Love Comedy wa Machigatteiru. Zoku\\Oregairu Zoku 11 - Hayama Hayato Always Renconds to Everyone's Expectations..mkv", "Yahari Ore no Seishun Love Comedy wa Machigatteiru", null, 11)] // TODO: [InlineData(@"/Library/Series/The Grand Tour (2016)/Season 1/S01E01 The Holy Trinity.mkv", "The Grand Tour", 1, 1)] public void TestSimple(string path, string seriesName, int? seasonNumber, int? episodeNumber) diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs index 950899d7e..b1141df47 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs @@ -58,7 +58,7 @@ namespace Jellyfin.Naming.Tests.Video { input = Path.GetFileName(input); - var result = new VideoResolver(_namingOptions).CleanDateTime(input); + var result = VideoResolver.CleanDateTime(input, _namingOptions); Assert.Equal(expectedName, result.Name, true); Assert.Equal(expectedYear, result.Year); diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs index a720bdade..fb050cf5a 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs @@ -7,7 +7,7 @@ namespace Jellyfin.Naming.Tests.Video { public sealed class CleanStringTests { - private readonly VideoResolver _videoResolver = new VideoResolver(new NamingOptions()); + private readonly NamingOptions _namingOptions = new NamingOptions(); [Theory] [InlineData("Super movie 480p.mp4", "Super movie")] @@ -26,7 +26,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME: [InlineData("After The Sunset - [0004].mkv", "After The Sunset")] public void CleanStringTest_NeedsCleaning_Success(string input, string expectedName) { - Assert.True(_videoResolver.TryCleanString(input, out ReadOnlySpan<char> newName)); + Assert.True(VideoResolver.TryCleanString(input, _namingOptions, out ReadOnlySpan<char> newName)); // TODO: compare spans when XUnit supports it Assert.Equal(expectedName, newName.ToString()); } @@ -41,7 +41,7 @@ namespace Jellyfin.Naming.Tests.Video [InlineData("Run lola run (lola rennt) (2009).mp4")] public void CleanStringTest_DoesntNeedCleaning_False(string? input) { - Assert.False(_videoResolver.TryCleanString(input, out ReadOnlySpan<char> newName)); + Assert.False(VideoResolver.TryCleanString(input, _namingOptions, out ReadOnlySpan<char> newName)); Assert.True(newName.IsEmpty); } } diff --git a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs index 2f173b0ce..f872f94f8 100644 --- a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs @@ -104,13 +104,6 @@ namespace Jellyfin.Naming.Tests.Video Assert.Equal(rule, res.Rule); } - [Fact] - public void TestFlagsParser() - { - var flags = new FlagParser(_videoOptions).GetFlags(string.Empty); - Assert.Empty(flags); - } - private ExtraResolver GetExtraTypeParser(NamingOptions videoOptions) { return new ExtraResolver(videoOptions); diff --git a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs index 69de96a47..1762b91b9 100644 --- a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs @@ -22,8 +22,7 @@ namespace Jellyfin.Naming.Tests.Video [Fact] public void Test3DName() { - var result = - new VideoResolver(_namingOptions).ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv"); + var result = VideoResolver.ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv", _namingOptions); Assert.Equal("hsbs", result?.Format3D); Assert.Equal("Oblivion", result?.Name); @@ -58,15 +57,13 @@ namespace Jellyfin.Naming.Tests.Video private void Test(string input, bool is3D, string? format3D) { - var parser = new Format3DParser(_namingOptions); - - var result = parser.Parse(input); + var result = Format3DParser.Parse(input, _namingOptions); Assert.Equal(is3D, result.Is3D); if (format3D == null) { - Assert.Null(result.Format3D); + Assert.Null(result?.Format3D); } else { diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index 6e803593e..d02f8ae92 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -9,7 +9,7 @@ namespace Jellyfin.Naming.Tests.Video { public class MultiVersionTests { - private readonly VideoListResolver _videoListResolver = new VideoListResolver(new NamingOptions()); + private readonly NamingOptions _namingOptions = new NamingOptions(); [Fact] public void TestMultiEdition1() @@ -22,11 +22,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/X-Men Days of Future Past/X-Men Days of Future Past [hsbs].mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Single(result[0].Extras); @@ -43,11 +45,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/X-Men Days of Future Past/X-Men Days of Future Past [banana].mp4" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Single(result[0].Extras); @@ -63,11 +67,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1929 version.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Single(result[0].AlternateVersions); @@ -87,11 +93,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/M/Movie 7.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(7, result.Count); Assert.Empty(result[0].Extras); @@ -113,11 +121,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Movie/Movie-8.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Empty(result[0].Extras); @@ -140,11 +150,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Mo/Movie 9.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(9, result.Count); Assert.Empty(result[0].Extras); @@ -163,11 +175,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Movie/Movie 5.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(5, result.Count); Assert.Empty(result[0].Extras); @@ -188,11 +202,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man (2011).mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(5, result.Count); Assert.Empty(result[0].Extras); @@ -214,11 +230,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man[test].mkv", }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Empty(result[0].Extras); @@ -243,11 +261,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man [test].mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Empty(result[0].Extras); @@ -266,11 +286,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man - C (2007).mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(2, result.Count); } @@ -289,11 +311,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man_3d.hsbs.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(7, result.Count); Assert.Empty(result[0].Extras); @@ -314,11 +338,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man (2011).mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(5, result.Count); Assert.Empty(result[0].Extras); @@ -334,11 +360,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Blade Runner (1982)/Blade Runner (1982) [EE by ADM] [480p HEVC AAC,AAC,AAC].mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Empty(result[0].Extras); @@ -354,11 +382,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [2160p] Blu-ray.x265.AAC.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Empty(result[0].Extras); @@ -374,11 +404,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/John Wick - Kapitel 3 (2019) [imdbid=tt6146586]/John Wick - Kapitel 3 (2019) [imdbid=tt6146586] - Version 2.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); Assert.Empty(result[0].Extras); @@ -394,11 +426,13 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/John Wick - Chapter 3 (2019)/John Wick - Chapter 3 (2019) [Version 2.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(2, result.Count); } @@ -406,7 +440,7 @@ namespace Jellyfin.Naming.Tests.Video [Fact] public void TestEmptyList() { - var result = _videoListResolver.Resolve(new List<FileSystemMetadata>()).ToList(); + var result = VideoListResolver.Resolve(new List<FileSystemMetadata>(), _namingOptions).ToList(); Assert.Empty(result); } diff --git a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs index 6e759c6d6..1d50df7a6 100644 --- a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs @@ -29,8 +29,7 @@ namespace Jellyfin.Naming.Tests.Video [Fact] public void TestStubName() { - var result = - new VideoResolver(_namingOptions).ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc"); + var result = VideoResolver.ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc", _namingOptions); Assert.Equal("Oblivion", result?.Name); } diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs index 08af76669..9e0776c3c 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs @@ -9,7 +9,7 @@ namespace Jellyfin.Naming.Tests.Video { public class VideoListResolverTests { - private readonly VideoListResolver _videoListResolver = new VideoListResolver(new NamingOptions()); + private readonly NamingOptions _namingOptions = new NamingOptions(); [Fact] public void TestStackAndExtras() @@ -40,11 +40,13 @@ namespace Jellyfin.Naming.Tests.Video "WillyWonka-trailer.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(5, result.Count); var batman = result.FirstOrDefault(x => string.Equals(x.Name, "Batman", StringComparison.Ordinal)); @@ -67,11 +69,13 @@ namespace Jellyfin.Naming.Tests.Video "300.nfo" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -85,11 +89,13 @@ namespace Jellyfin.Naming.Tests.Video "300 trailer.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -103,11 +109,13 @@ namespace Jellyfin.Naming.Tests.Video "X-Men Days of Future Past-trailer.mp4" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -122,11 +130,13 @@ namespace Jellyfin.Naming.Tests.Video "X-Men Days of Future Past-trailer2.mp4" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -140,11 +150,13 @@ namespace Jellyfin.Naming.Tests.Video "Looper.2012.bluray.720p.x264.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -162,11 +174,13 @@ namespace Jellyfin.Naming.Tests.Video "My video 5.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(5, result.Count); } @@ -180,11 +194,13 @@ namespace Jellyfin.Naming.Tests.Video @"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 2" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = true, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = true, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -199,11 +215,13 @@ namespace Jellyfin.Naming.Tests.Video @"My movie #2.mp4" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = true, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = true, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(2, result.Count); } @@ -218,11 +236,13 @@ namespace Jellyfin.Naming.Tests.Video @"No (2012) part1-trailer.mp4" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -237,11 +257,13 @@ namespace Jellyfin.Naming.Tests.Video @"No (2012)-trailer.mp4" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -257,11 +279,13 @@ namespace Jellyfin.Naming.Tests.Video @"trailer.mp4" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -277,11 +301,13 @@ namespace Jellyfin.Naming.Tests.Video @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd2.avi" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(2, result.Count); } @@ -294,11 +320,13 @@ namespace Jellyfin.Naming.Tests.Video @"/nas-markrobbo78/Videos/INDEX HTPC/Movies/Watched/3 - ACTION/Argo (2012)/movie.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -311,11 +339,13 @@ namespace Jellyfin.Naming.Tests.Video @"The Colony.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -329,11 +359,13 @@ namespace Jellyfin.Naming.Tests.Video @"Four Sisters and a Wedding - B.avi" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -347,11 +379,13 @@ namespace Jellyfin.Naming.Tests.Video @"Four Rooms - A.mp4" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(2, result.Count); } @@ -365,11 +399,13 @@ namespace Jellyfin.Naming.Tests.Video @"/Server/Despicable Me/movie-trailer.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } @@ -385,11 +421,13 @@ namespace Jellyfin.Naming.Tests.Video @"/Server/Despicable Me/Baywatch (2017) - Trailer.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Equal(4, result.Count); } @@ -403,11 +441,13 @@ namespace Jellyfin.Naming.Tests.Video @"/Movies/Despicable Me/trailers/trailer.mkv" }; - var result = _videoListResolver.Resolve(files.Select(i => new FileSystemMetadata - { - IsDirectory = false, - FullName = i - }).ToList()).ToList(); + var result = VideoListResolver.Resolve( + files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + }).ToList(), + _namingOptions).ToList(); Assert.Single(result); } diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index c56046f03..ac5a7a21e 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -9,7 +9,7 @@ namespace Jellyfin.Naming.Tests.Video { public class VideoResolverTests { - private readonly VideoResolver _videoResolver = new VideoResolver(new NamingOptions()); + private static NamingOptions _namingOptions = new NamingOptions(); public static IEnumerable<object[]> ResolveFile_ValidFileNameTestData() { @@ -159,27 +159,27 @@ namespace Jellyfin.Naming.Tests.Video [MemberData(nameof(ResolveFile_ValidFileNameTestData))] public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult) { - var result = _videoResolver.ResolveFile(expectedResult.Path); + var result = VideoResolver.ResolveFile(expectedResult.Path, _namingOptions); Assert.NotNull(result); - Assert.Equal(result?.Path, expectedResult.Path); - Assert.Equal(result?.Container, expectedResult.Container); - Assert.Equal(result?.Name, expectedResult.Name); - Assert.Equal(result?.Year, expectedResult.Year); - Assert.Equal(result?.ExtraType, expectedResult.ExtraType); - Assert.Equal(result?.Format3D, expectedResult.Format3D); - Assert.Equal(result?.Is3D, expectedResult.Is3D); - Assert.Equal(result?.IsStub, expectedResult.IsStub); - Assert.Equal(result?.StubType, expectedResult.StubType); - Assert.Equal(result?.IsDirectory, expectedResult.IsDirectory); - Assert.Equal(result?.FileNameWithoutExtension, expectedResult.FileNameWithoutExtension); - Assert.Equal(result?.ToString(), expectedResult.ToString()); + Assert.Equal(result!.Path, expectedResult.Path); + Assert.Equal(result.Container, expectedResult.Container); + Assert.Equal(result.Name, expectedResult.Name); + Assert.Equal(result.Year, expectedResult.Year); + Assert.Equal(result.ExtraType, expectedResult.ExtraType); + Assert.Equal(result.Format3D, expectedResult.Format3D); + Assert.Equal(result.Is3D, expectedResult.Is3D); + Assert.Equal(result.IsStub, expectedResult.IsStub); + Assert.Equal(result.StubType, expectedResult.StubType); + Assert.Equal(result.IsDirectory, expectedResult.IsDirectory); + Assert.Equal(result.FileNameWithoutExtension.ToString(), expectedResult.FileNameWithoutExtension.ToString()); + Assert.Equal(result.ToString(), expectedResult.ToString()); } [Fact] public void ResolveFile_EmptyPath() { - var result = _videoResolver.ResolveFile(string.Empty); + var result = VideoResolver.ResolveFile(string.Empty, _namingOptions); Assert.Null(result); } @@ -194,7 +194,7 @@ namespace Jellyfin.Naming.Tests.Video string.Empty }; - var results = paths.Select(path => _videoResolver.ResolveDirectory(path)).ToList(); + var results = paths.Select(path => VideoResolver.ResolveDirectory(path, _namingOptions)).ToList(); Assert.Equal(3, results.Count); Assert.NotNull(results[0]); diff --git a/tests/Jellyfin.Networking.Tests/IPHostTests.cs b/tests/Jellyfin.Networking.Tests/IPHostTests.cs new file mode 100644 index 000000000..ec3a1300c --- /dev/null +++ b/tests/Jellyfin.Networking.Tests/IPHostTests.cs @@ -0,0 +1,53 @@ +using FsCheck; +using FsCheck.Xunit; +using MediaBrowser.Common.Net; +using Xunit; + +namespace Jellyfin.Networking.Tests +{ + public static class IPHostTests + { + /// <summary> + /// Checks IP address formats. + /// </summary> + /// <param name="address">IP Address.</param> + [Theory] + [InlineData("127.0.0.1")] + [InlineData("127.0.0.1:123")] + [InlineData("localhost")] + [InlineData("localhost:1345")] + [InlineData("www.google.co.uk")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")] + [InlineData("fe80::7add:12ff:febb:c67b%16")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] + [InlineData("fe80::7add:12ff:febb:c67b%16:123")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]")] + [InlineData("192.168.1.2/255.255.255.0")] + [InlineData("192.168.1.2/24")] + public static void TryParse_ValidHostStrings_True(string address) + => Assert.True(IPHost.TryParse(address, out _)); + + [Property] + public static Property TryParse_IPv4Address_True(IPv4Address address) + => IPHost.TryParse(address.Item.ToString(), out _).ToProperty(); + + [Property] + public static Property TryParse_IPv6Address_True(IPv6Address address) + => IPHost.TryParse(address.Item.ToString(), out _).ToProperty(); + + /// <summary> + /// All should be invalid address strings. + /// </summary> + /// <param name="address">Invalid address strings.</param> + [Theory] + [InlineData("256.128.0.0.0.1")] + [InlineData("127.0.0.1#")] + [InlineData("localhost!")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] + public static void TryParse_InvalidAddressString_False(string address) + => Assert.False(IPHost.TryParse(address, out _)); + } +} diff --git a/tests/Jellyfin.Networking.Tests/IPNetAddressTests.cs b/tests/Jellyfin.Networking.Tests/IPNetAddressTests.cs new file mode 100644 index 000000000..aa2dbc57a --- /dev/null +++ b/tests/Jellyfin.Networking.Tests/IPNetAddressTests.cs @@ -0,0 +1,49 @@ +using FsCheck; +using FsCheck.Xunit; +using MediaBrowser.Common.Net; +using Xunit; + +namespace Jellyfin.Networking.Tests +{ + public static class IPNetAddressTests + { + /// <summary> + /// Checks IP address formats. + /// </summary> + /// <param name="address">IP Address.</param> + [Theory] + [InlineData("127.0.0.1")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]")] + [InlineData("fe80::7add:12ff:febb:c67b%16")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] + [InlineData("fe80::7add:12ff:febb:c67b%16:123")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]")] + [InlineData("192.168.1.2/255.255.255.0")] + [InlineData("192.168.1.2/24")] + public static void TryParse_ValidIPStrings_True(string address) + => Assert.True(IPNetAddress.TryParse(address, out _)); + + [Property] + public static Property TryParse_IPv4Address_True(IPv4Address address) + => IPNetAddress.TryParse(address.Item.ToString(), out _).ToProperty(); + + [Property] + public static Property TryParse_IPv6Address_True(IPv6Address address) + => IPNetAddress.TryParse(address.Item.ToString(), out _).ToProperty(); + + /// <summary> + /// All should be invalid address strings. + /// </summary> + /// <param name="address">Invalid address strings.</param> + [Theory] + [InlineData("256.128.0.0.0.1")] + [InlineData("127.0.0.1#")] + [InlineData("localhost!")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] + public static void TryParse_InvalidAddressString_False(string address) + => Assert.False(IPNetAddress.TryParse(address, out _)); + } +} diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj index d5268facc..dd593c9e7 100644 --- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -8,17 +8,15 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> + <PackageReference Include="FsCheck.Xunit" Version="2.16.1" /> <PackageReference Include="Moq" Version="4.16.1" /> </ItemGroup> diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 671b8598d..97c14d463 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -57,66 +57,6 @@ namespace Jellyfin.Networking.Tests } /// <summary> - /// Checks IP address formats. - /// </summary> - /// <param name="address">IP Address.</param> - [Theory] - [InlineData("127.0.0.1")] - [InlineData("127.0.0.1:123")] - [InlineData("localhost")] - [InlineData("localhost:1345")] - [InlineData("www.google.co.uk")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] - [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")] - [InlineData("fe80::7add:12ff:febb:c67b%16")] - [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] - [InlineData("fe80::7add:12ff:febb:c67b%16:123")] - [InlineData("[fe80::7add:12ff:febb:c67b%16]")] - [InlineData("192.168.1.2/255.255.255.0")] - [InlineData("192.168.1.2/24")] - public void ValidHostStrings(string address) - { - Assert.True(IPHost.TryParse(address, out _)); - } - - /// <summary> - /// Checks IP address formats. - /// </summary> - /// <param name="address">IP Address.</param> - [Theory] - [InlineData("127.0.0.1")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] - [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]")] - [InlineData("fe80::7add:12ff:febb:c67b%16")] - [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] - [InlineData("fe80::7add:12ff:febb:c67b%16:123")] - [InlineData("[fe80::7add:12ff:febb:c67b%16]")] - [InlineData("192.168.1.2/255.255.255.0")] - [InlineData("192.168.1.2/24")] - public void ValidIPStrings(string address) - { - Assert.True(IPNetAddress.TryParse(address, out _)); - } - - /// <summary> - /// All should be invalid address strings. - /// </summary> - /// <param name="address">Invalid address strings.</param> - [Theory] - [InlineData("256.128.0.0.0.1")] - [InlineData("127.0.0.1#")] - [InlineData("localhost!")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] - [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] - public void InvalidAddressString(string address) - { - Assert.False(IPNetAddress.TryParse(address, out _)); - Assert.False(IPHost.TryParse(address, out _)); - } - - /// <summary> /// Test collection parsing. /// </summary> /// <param name="settings">Collection to parse.</param> diff --git a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj index b37515e78..d9e33617b 100644 --- a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj +++ b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj @@ -3,21 +3,18 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="Moq" Version="4.16.1" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.0.3"> + <PackageReference Include="coverlet.collector" Version="3.1.0"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> diff --git a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs b/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs index efe8063a0..25900bc09 100644 --- a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs +++ b/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs @@ -1,10 +1,9 @@ using System.Text.Json; using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; using MediaBrowser.Providers.Plugins.Omdb; using Xunit; -namespace Jellyfin.Common.Tests.Json +namespace Jellyfin.Providers.Tests.Omdb { public class JsonOmdbConverterTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs index 71f8c5181..a6e1dfe8f 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Data/SqliteItemRepositoryTests.cs @@ -109,6 +109,9 @@ namespace Jellyfin.Server.Implementations.Tests.Data [InlineData("")] [InlineData("*")] [InlineData("https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0")] + [InlineData("/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*6374520964785129080*WjQbtJtSO8nhNZ%L_Io#R/oaS<o}-;adXAoIn7j[%hW9s:WGw[nN")] // Invalid modified date + [InlineData("/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*-637452096478512963*WjQbtJtSO8nhNZ%L_Io#R/oaS<o}-;adXAoIn7j[%hW9s:WGw[nN")] // Negative modified date + [InlineData("/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Invalid*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN")] // Invalid type public void ItemImageInfoFromValueString_Invalid_Null(string value) { Assert.Null(_sqliteItemRepository.ItemImageInfoFromValueString(value)); @@ -166,6 +169,38 @@ namespace Jellyfin.Server.Implementations.Tests.Data }; } + public static IEnumerable<object[]> DeserializeImages_ValidAndInvalid_TestData() + { + yield return new object[] + { + string.Empty, + Array.Empty<ItemImageInfo>() + }; + + yield return new object[] + { + "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg*637452096478512963*Primary*1920*1080*WjQbtJtSO8nhNZ%L_Io#R/oaS6o}-;adXAoIn7j[%hW9s:WGw[nN|test|1234||ss", + new ItemImageInfo[] + { + new () + { + Path = "/mnt/series/Family Guy/Season 1/Family Guy - S01E01-thumb.jpg", + Type = ImageType.Primary, + DateModified = new DateTime(637452096478512963, DateTimeKind.Utc), + Width = 1920, + Height = 1080, + BlurHash = "WjQbtJtSO8nhNZ%L_Io#R*oaS6o}-;adXAoIn7j[%hW9s:WGw[nN" + } + } + }; + + yield return new object[] + { + "|", + Array.Empty<ItemImageInfo>() + }; + } + [Theory] [MemberData(nameof(DeserializeImages_Valid_TestData))] public void DeserializeImages_Valid_Success(string value, ItemImageInfo[] expected) @@ -184,6 +219,23 @@ namespace Jellyfin.Server.Implementations.Tests.Data } [Theory] + [MemberData(nameof(DeserializeImages_ValidAndInvalid_TestData))] + public void DeserializeImages_ValidAndInvalid_Success(string value, ItemImageInfo[] expected) + { + var result = _sqliteItemRepository.DeserializeImages(value); + Assert.Equal(expected.Length, result.Length); + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i].Path, result[i].Path); + Assert.Equal(expected[i].Type, result[i].Type); + Assert.Equal(expected[i].DateModified, result[i].DateModified); + Assert.Equal(expected[i].Width, result[i].Width); + Assert.Equal(expected[i].Height, result[i].Height); + Assert.Equal(expected[i].BlurHash, result[i].BlurHash); + } + } + + [Theory] [MemberData(nameof(DeserializeImages_Valid_TestData))] public void SerializeImages_Valid_Success(string expected, ItemImageInfo[] value) { diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 30e6542f9..d991f5574 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -1,10 +1,10 @@ +using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; using AutoFixture; using AutoFixture.AutoMoq; using Emby.Server.Implementations.IO; -using MediaBrowser.Model.System; using Xunit; namespace Jellyfin.Server.Implementations.Tests.IO @@ -31,7 +31,7 @@ namespace Jellyfin.Server.Implementations.Tests.IO { var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath); - if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) + if (OperatingSystem.IsWindows()) { var expectedWindowsPath = expectedAbsolutePath.Replace('/', '\\'); Assert.Equal(expectedWindowsPath, generatedPath.Split(':')[1]); @@ -55,7 +55,7 @@ namespace Jellyfin.Server.Implementations.Tests.IO [SkippableFact] public void GetFileInfo_DanglingSymlink_ExistsFalse() { - Skip.If(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + Skip.If(OperatingSystem.IsWindows()); string testFileDir = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); string testFileName = Path.Combine(testFileDir, Path.GetRandomFileName() + "-danglingsym.link"); 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 27713d58a..9b6ab7bdf 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -8,9 +8,6 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> <RootNamespace>Jellyfin.Server.Implementations.Tests</RootNamespace> </PropertyGroup> @@ -24,12 +21,12 @@ <ItemGroup> <PackageReference Include="AutoFixture" Version="4.17.0" /> <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="Moq" Version="4.16.1" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> <PackageReference Include="Xunit.SkippableFact" Version="1.4.13" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> </ItemGroup> <!-- Code Analyzers --> @@ -42,6 +39,7 @@ <ItemGroup> <ProjectReference Include="..\..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" /> <ProjectReference Include="..\..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" /> + <ProjectReference Include="..\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj" /> </ItemGroup> </Project> diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs new file mode 100644 index 000000000..143020d43 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -0,0 +1,179 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Emby.Server.Implementations.Localization; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Localization +{ + public class LocalizationManagerTests + { + [Fact] + public void GetCountries_All_Success() + { + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "de-DE" + }); + var countries = localizationManager.GetCountries().ToList(); + + Assert.Equal(139, countries.Count); + + var germany = countries.FirstOrDefault(x => x.Name.Equals("DE", StringComparison.Ordinal)); + Assert.NotNull(germany); + Assert.Equal("Germany", germany!.DisplayName); + Assert.Equal("DEU", germany.ThreeLetterISORegionName); + Assert.Equal("DE", germany.TwoLetterISORegionName); + } + + [Fact] + public async Task GetCultures_All_Success() + { + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "de-DE" + }); + await localizationManager.LoadAll(); + var cultures = localizationManager.GetCultures().ToList(); + + Assert.Equal(189, cultures.Count); + + var germany = cultures.FirstOrDefault(x => x.TwoLetterISOLanguageName.Equals("de", StringComparison.Ordinal)); + Assert.NotNull(germany); + Assert.Equal("ger", germany!.ThreeLetterISOLanguageName); + Assert.Equal("German", germany.DisplayName); + Assert.Equal("German", germany.Name); + Assert.Contains("deu", germany.ThreeLetterISOLanguageNames); + Assert.Contains("ger", germany.ThreeLetterISOLanguageNames); + } + + [Theory] + [InlineData("de")] + [InlineData("ger")] + [InlineData("german")] + public async Task FindLanguageInfo_Valid_Success(string identifier) + { + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "de-DE" + }); + await localizationManager.LoadAll(); + + var germany = localizationManager.FindLanguageInfo(identifier); + Assert.NotNull(germany); + + Assert.Equal("ger", germany!.ThreeLetterISOLanguageName); + Assert.Equal("German", germany.DisplayName); + Assert.Equal("German", germany.Name); + Assert.Contains("deu", germany.ThreeLetterISOLanguageNames); + Assert.Contains("ger", germany.ThreeLetterISOLanguageNames); + } + + [Fact] + public async Task GetParentalRatings_Default_Success() + { + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "de-DE" + }); + await localizationManager.LoadAll(); + var ratings = localizationManager.GetParentalRatings().ToList(); + + Assert.Equal(23, ratings.Count); + + var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); + Assert.NotNull(tvma); + Assert.Equal(9, tvma!.Value); + } + + [Fact] + public async Task GetParentalRatings_ConfiguredCountryCode_Success() + { + var localizationManager = Setup(new ServerConfiguration() + { + MetadataCountryCode = "DE" + }); + await localizationManager.LoadAll(); + var ratings = localizationManager.GetParentalRatings().ToList(); + + Assert.Equal(10, ratings.Count); + + var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); + Assert.NotNull(fsk); + Assert.Equal(7, fsk!.Value); + } + + [Theory] + [InlineData("CA-R", "CA", 10)] + [InlineData("FSK-16", "DE", 8)] + [InlineData("FSK-18", "DE", 9)] + [InlineData("FSK-18", "US", 9)] + [InlineData("TV-MA", "US", 9)] + [InlineData("XXX", "asdf", 100)] + [InlineData("Germany: FSK-18", "DE", 9)] + public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int expectedLevel) + { + var localizationManager = Setup(new ServerConfiguration() + { + MetadataCountryCode = countryCode + }); + await localizationManager.LoadAll(); + var level = localizationManager.GetRatingLevel(value); + Assert.NotNull(level); + Assert.Equal(expectedLevel, level!); + } + + [Fact] + public async Task GetRatingLevel_GivenUnratedString_Success() + { + var localizationManager = Setup(new ServerConfiguration() + { + UICulture = "de-DE" + }); + await localizationManager.LoadAll(); + Assert.Null(localizationManager.GetRatingLevel("n/a")); + } + + [Theory] + [InlineData("Default", "Default")] + [InlineData("HeaderLiveTV", "Live TV")] + public void GetLocalizedString_Valid_Success(string key, string expected) + { + var localizationManager = Setup(new ServerConfiguration() + { + UICulture = "en-US" + }); + + var translated = localizationManager.GetLocalizedString(key); + Assert.NotNull(translated); + Assert.Equal(expected, translated); + } + + [Fact] + public void GetLocalizedString_Invalid_Success() + { + var localizationManager = Setup(new ServerConfiguration() + { + UICulture = "en-US" + }); + + var key = "SuperInvalidTranslationKeyThatWillNeverBeAdded"; + + var translated = localizationManager.GetLocalizedString(key); + Assert.NotNull(translated); + Assert.Equal(key, translated); + } + + private LocalizationManager Setup(ServerConfiguration config) + { + var mockConfiguration = new Mock<IServerConfigurationManager>(); + mockConfiguration.SetupGet(x => x.Configuration).Returns(config); + + return new LocalizationManager(mockConfiguration.Object, new NullLogger<LocalizationManager>()); + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs new file mode 100644 index 000000000..043363ae3 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs @@ -0,0 +1,90 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; +using Emby.Server.Implementations.QuickConnect; +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Configuration; +using Moq; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.QuickConnect +{ + public class QuickConnectManagerTests + { + private static readonly AuthorizationInfo _quickConnectAuthInfo = new AuthorizationInfo + { + Device = "Device", + DeviceId = "DeviceId", + Client = "Client", + Version = "1.0.0" + }; + + private readonly Fixture _fixture; + private readonly ServerConfiguration _config; + private readonly QuickConnectManager _quickConnectManager; + + public QuickConnectManagerTests() + { + _config = new ServerConfiguration(); + var configManager = new Mock<IServerConfigurationManager>(); + configManager.Setup(x => x.Configuration).Returns(_config); + + _fixture = new Fixture(); + _fixture.Customize(new AutoMoqCustomization + { + ConfigureMembers = true + }).Inject(configManager.Object); + + // User object contains circular references. + _fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + + _quickConnectManager = _fixture.Create<QuickConnectManager>(); + } + + [Fact] + public void IsEnabled_QuickConnectUnavailable_False() + => Assert.False(_quickConnectManager.IsEnabled); + + [Fact] + public void TryConnect_QuickConnectUnavailable_ThrowsAuthenticationException() + => Assert.Throws<AuthenticationException>(() => _quickConnectManager.TryConnect(_quickConnectAuthInfo)); + + [Fact] + public void CheckRequestStatus_QuickConnectUnavailable_ThrowsAuthenticationException() + => Assert.Throws<AuthenticationException>(() => _quickConnectManager.CheckRequestStatus(string.Empty)); + + [Fact] + public void AuthorizeRequest_QuickConnectUnavailable_ThrowsAuthenticationException() + => Assert.ThrowsAsync<AuthenticationException>(() => _quickConnectManager.AuthorizeRequest(Guid.Empty, string.Empty)); + + [Fact] + public void IsEnabled_QuickConnectAvailable_True() + { + _config.QuickConnectAvailable = true; + Assert.True(_quickConnectManager.IsEnabled); + } + + [Fact] + public void CheckRequestStatus_QuickConnectAvailable_Success() + { + _config.QuickConnectAvailable = true; + var res1 = _quickConnectManager.TryConnect(_quickConnectAuthInfo); + var res2 = _quickConnectManager.CheckRequestStatus(res1.Secret); + Assert.Equal(res1, res2); + } + + [Fact] + public async Task AuthorizeRequest_QuickConnectAvailable_Success() + { + _config.QuickConnectAvailable = true; + var res = _quickConnectManager.TryConnect(_quickConnectAuthInfo); + Assert.True(await _quickConnectManager.AuthorizeRequest(Guid.Empty, res.Code)); + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs new file mode 100644 index 000000000..d9b206f66 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/AiredEpisodeOrderComparerTests.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Emby.Server.Implementations.Sorting; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Sorting +{ + public class AiredEpisodeOrderComparerTests + { + [Theory] + [ClassData(typeof(EpisodeBadData))] + public void Compare_GivenNull_ThrowsArgumentNullException(BaseItem x, BaseItem y) + { + var cmp = new AiredEpisodeOrderComparer(); + Assert.Throws<ArgumentNullException>(() => cmp.Compare(x, y)); + } + + [Theory] + [ClassData(typeof(EpisodeTestData))] + public void AiredEpisodeOrderCompareTest(BaseItem x, BaseItem y, int expected) + { + var cmp = new AiredEpisodeOrderComparer(); + + Assert.Equal(expected, cmp.Compare(x, y)); + Assert.Equal(-expected, cmp.Compare(y, x)); + } + + private class EpisodeBadData : IEnumerable<object?[]> + { + public IEnumerator<object?[]> GetEnumerator() + { + yield return new object?[] { null, new Episode() }; + yield return new object?[] { new Episode(), null }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + private class EpisodeTestData : IEnumerable<object?[]> + { + public IEnumerator<object?[]> GetEnumerator() + { + yield return new object?[] + { + new Movie(), + new Movie(), + 0 + }; + yield return new object?[] + { + new Movie(), + new Episode(), + 1 + }; + // Good cases + yield return new object?[] + { + new Episode(), + new Episode(), + 0 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + 0 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + 1 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 2, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + 1 + }; + // Good Specials + yield return new object?[] + { + new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, + 0 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, + 1 + }; + + // Specials to Episodes + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, + 1 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, + 1 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, + 1 + }; + + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1 }, + 1 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 2 }, + 1 + }; + + yield return new object?[] + { + new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + 1 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1 }, + 1 + }; + + yield return new object?[] + { + new Episode { ParentIndexNumber = 3, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsAfterSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, + 1 + }; + + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 1 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1 }, + 1 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 2 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, + 1 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 1 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, + 0 + }; + yield return new object?[] + { + new Episode { ParentIndexNumber = 1, IndexNumber = 3 }, + new Episode { ParentIndexNumber = 0, IndexNumber = 1, AirsBeforeSeasonNumber = 1, AirsBeforeEpisodeNumber = 2 }, + 1 + }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs b/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs new file mode 100644 index 000000000..31f33c682 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/TypedBaseItem/BaseItemKindTests.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using Jellyfin.Data.Enums; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.TypedBaseItem +{ + public class BaseItemKindTests + { + public static TheoryData<Type> BaseItemKind_TestData() + { + var data = new TheoryData<Type>(); + + var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var assembly in loadedAssemblies) + { + if (IsProjectAssemblyName(assembly.FullName)) + { + var baseItemTypes = assembly.GetTypes() + .Where(targetType => targetType.IsClass + && !targetType.IsAbstract + && targetType.IsSubclassOf(typeof(MediaBrowser.Controller.Entities.BaseItem))); + foreach (var baseItemType in baseItemTypes) + { + data.Add(baseItemType); + } + } + } + + return data; + } + + [Theory] + [MemberData(nameof(BaseItemKind_TestData))] + public void EnumParse_GivenValidBaseItemType_ReturnsEnumValue(Type baseItemDescendantType) + { + var enumValue = Enum.Parse<BaseItemKind>(baseItemDescendantType.Name); + Assert.True(Enum.IsDefined(typeof(BaseItemKind), enumValue)); + } + + [Theory] + [MemberData(nameof(BaseItemKind_TestData))] + public void GetBaseItemKind_WhenCalledAfterDefaultCtor_DoesNotThrow(Type baseItemDescendantType) + { + var defaultConstructor = baseItemDescendantType.GetConstructor(Type.EmptyTypes); + var instance = (MediaBrowser.Controller.Entities.BaseItem)defaultConstructor!.Invoke(null); + var exception = Record.Exception(() => instance.GetBaseItemKind()); + Assert.Null(exception); + } + + private static bool IsProjectAssemblyName(string? name) + { + if (name == null) + { + return false; + } + + return name.StartsWith("Jellyfin", StringComparison.OrdinalIgnoreCase) + || name.StartsWith("Emby", StringComparison.OrdinalIgnoreCase) + || name.StartsWith("MediaBrowser", StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs index 4fa64d8a2..70acbfc40 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System; using System.IO; +using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -46,12 +47,36 @@ namespace Jellyfin.Server.Implementations.Tests.Updates [Fact] public async Task GetPackages_Valid_Success() { - IList<PackageInfo> packages = await _installationManager.GetPackages( + PackageInfo[] packages = await _installationManager.GetPackages( "Jellyfin Stable", "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", false); - Assert.Equal(25, packages.Count); + Assert.Equal(25, packages.Length); + } + + [Fact] + public async Task FilterPackages_NameOnly_Success() + { + PackageInfo[] packages = await _installationManager.GetPackages( + "Jellyfin Stable", + "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", + false); + + packages = _installationManager.FilterPackages(packages, "Anime").ToArray(); + Assert.Single(packages); + } + + [Fact] + public async Task FilterPackages_GuidOnly_Success() + { + PackageInfo[] packages = await _installationManager.GetPackages( + "Jellyfin Stable", + "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", + false); + + packages = _installationManager.FilterPackages(packages, id: new Guid("a4df60c5-6ab4-412a-8f79-2cab93fb2bc5")).ToArray(); + Assert.Single(packages); } } } diff --git a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs index ea6838682..4ea05397d 100644 --- a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs +++ b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs @@ -7,7 +7,7 @@ using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models.StartupDtos; using Jellyfin.Api.Models.UserDtos; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Xunit; namespace Jellyfin.Server.Integration.Tests diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/BaseJellyfinTestController.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/BaseJellyfinTestController.cs new file mode 100644 index 000000000..9db8689a7 --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/BaseJellyfinTestController.cs @@ -0,0 +1,14 @@ +using Jellyfin.Api; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Server.Integration.Tests.Controllers +{ + /// <summary> + /// Base controller for testing infrastructure. + /// Automatically ignored in generated openapi spec. + /// </summary> + [ApiExplorerSettings(IgnoreApi = true)] + public class BaseJellyfinTestController : BaseJellyfinApiController + { + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs index f5411dcb8..827365363 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs @@ -5,7 +5,7 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Xunit; namespace Jellyfin.Server.Integration.Tests.Controllers diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs new file mode 100644 index 000000000..1a720c2f6 --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Server.Integration.Tests.Controllers +{ + /// <summary> + /// Controller for testing the encoded url. + /// </summary> + public class EncoderController : BaseJellyfinTestController + { + /// <summary> + /// Tests the url decoding. + /// </summary> + /// <param name="params">Parameters to echo back in the response.</param> + /// <returns>An <see cref="OkResult"/>.</returns> + /// <response code="200">Information retrieved.</response> + [HttpGet("UrlDecode")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ContentResult TestUrlDecoding([FromQuery] Dictionary<string, string>? @params = null) + { + return new ContentResult() + { + Content = (@params != null && @params.Count > 0) + ? string.Join("&", @params.Select(x => x.Key + "=" + x.Value)) + : string.Empty, + ContentType = "text/plain; charset=utf-8", + StatusCode = 200 + }; + } + + /// <summary> + /// Tests the url decoding. + /// </summary> + /// <param name="params">Parameters to echo back in the response.</param> + /// <returns>An <see cref="OkResult"/>.</returns> + /// <response code="200">Information retrieved.</response> + [HttpGet("UrlArrayDecode")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ContentResult TestUrlArrayDecoding([FromQuery] Dictionary<string, string[]>? @params = null) + { + return new ContentResult() + { + Content = (@params != null && @params.Count > 0) + ? string.Join("&", @params.Select(x => x.Key + "=" + string.Join(',', x.Value))) + : string.Empty, + ContentType = "text/plain; charset=utf-8", + StatusCode = 200 + }; + } + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaInfoControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaInfoControllerTests.cs new file mode 100644 index 000000000..34d26680a --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaInfoControllerTests.cs @@ -0,0 +1,61 @@ +using System.Globalization; +using System.Net; +using System.Net.Mime; +using System.Threading.Tasks; +using Xunit; + +namespace Jellyfin.Server.Integration.Tests.Controllers +{ + public sealed class MediaInfoControllerTests : IClassFixture<JellyfinApplicationFactory> + { + private readonly JellyfinApplicationFactory _factory; + private static string? _accessToken; + + public MediaInfoControllerTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task BitrateTest_Default_Ok() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + var response = await client.GetAsync("Playback/BitrateTest").ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(MediaTypeNames.Application.Octet, response.Content.Headers.ContentType?.MediaType); + Assert.NotNull(response.Content.Headers.ContentLength); + } + + [Theory] + [InlineData(102400)] + public async Task BitrateTest_WithValidParam_Ok(int size) + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + var response = await client.GetAsync("Playback/BitrateTest?size=" + size.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(MediaTypeNames.Application.Octet, response.Content.Headers.ContentType?.MediaType); + Assert.NotNull(response.Content.Headers.ContentLength); + Assert.InRange(response.Content.Headers.ContentLength!.Value, size, long.MaxValue); + } + + [Theory] + [InlineData(0)] // Zero + [InlineData(-102400)] // Negative value + [InlineData(1000000000)] // Too large + public async Task BitrateTest_InvalidValue_BadRequest(int size) + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + var response = await client.GetAsync("Playback/BitrateTest?size=" + size.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs new file mode 100644 index 000000000..19d8381ea --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/MediaStructureControllerTests.cs @@ -0,0 +1,122 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Text.Json; +using System.Threading.Tasks; +using Jellyfin.Api.Models.LibraryStructureDto; +using Jellyfin.Extensions.Json; +using MediaBrowser.Model.Configuration; +using Xunit; + +namespace Jellyfin.Server.Integration.Tests.Controllers +{ + public sealed class MediaStructureControllerTests : IClassFixture<JellyfinApplicationFactory> + { + private readonly JellyfinApplicationFactory _factory; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + private static string? _accessToken; + + public MediaStructureControllerTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task RenameVirtualFolder_WhiteSpaceName_ReturnsBadRequest() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + using var postContent = new ByteArrayContent(Array.Empty<byte>()); + var response = await client.PostAsync("Library/VirtualFolders/Name?name=+&newName=test", postContent).ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + public async Task RenameVirtualFolder_WhiteSpaceNewName_ReturnsBadRequest() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + using var postContent = new ByteArrayContent(Array.Empty<byte>()); + var response = await client.PostAsync("Library/VirtualFolders/Name?name=test&newName=+", postContent).ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + public async Task RenameVirtualFolder_NameDoesntExist_ReturnsNotFound() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + using var postContent = new ByteArrayContent(Array.Empty<byte>()); + var response = await client.PostAsync("Library/VirtualFolders/Name?name=doesnt+exist&newName=test", postContent).ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task AddMediaPath_PathDoesntExist_ReturnsNotFound() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + var data = new MediaPathDto() + { + Name = "Test", + Path = "/this/path/doesnt/exist" + }; + + using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions)); + postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + var response = await client.PostAsync("Library/VirtualFolders/Paths", postContent).ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task UpdateMediaPath_WhiteSpaceName_ReturnsBadRequest() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + var data = new UpdateMediaPathRequestDto() + { + Name = " ", + PathInfo = new MediaPathInfo("test") + }; + + using var postContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions)); + postContent.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json); + var response = await client.PostAsync("Library/VirtualFolders/Paths/Update", postContent).ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + public async Task RemoveMediaPath_WhiteSpaceName_ReturnsBadRequest() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + var response = await client.DeleteAsync("Library/VirtualFolders/Paths?name=+").ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + public async Task RemoveMediaPath_PathDoesntExist_ReturnsNotFound() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false)); + + var response = await client.DeleteAsync("Library/VirtualFolders/Paths?name=none&path=%2Fthis%2Fpath%2Fdoesnt%2Fexist").ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs index 169a5a6c5..9c0fc72f6 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs @@ -6,7 +6,7 @@ using System.Net.Mime; using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models.StartupDtos; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Xunit; using Xunit.Priority; diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs index 6584490de..8866ab53c 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs @@ -8,7 +8,7 @@ using System.Net.Mime; using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models.UserDtos; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Model.Dto; using Xunit; using Xunit.Priority; diff --git a/tests/Jellyfin.Server.Integration.Tests/EncodedQueryStringTest.cs b/tests/Jellyfin.Server.Integration.Tests/EncodedQueryStringTest.cs new file mode 100644 index 000000000..732b4f050 --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/EncodedQueryStringTest.cs @@ -0,0 +1,48 @@ +using System.Net; +using System.Threading.Tasks; +using Xunit; + +namespace Jellyfin.Server.Integration.Tests +{ + /// <summary> + /// Defines the test for encoded querystrings in the url. + /// </summary> + public class EncodedQueryStringTest : IClassFixture<JellyfinApplicationFactory> + { + private readonly JellyfinApplicationFactory _factory; + + public EncodedQueryStringTest(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Theory] + [InlineData("a=1&b=2&c=3", "a=1&b=2&c=3")] // won't be processed as there is more than 1. + [InlineData("a=1", "a=1")] // won't be processed as it has a value + [InlineData("a%3D1%26b%3D2%26c%3D3", "a=1&b=2&c=3")] // will be processed. + [InlineData("a=b&a=c", "a=b")] + [InlineData("a%3Db%26a%3Dc", "a=b")] + public async Task Ensure_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl) + { + var client = _factory.CreateClient(); + + var response = await client.GetAsync("Encoder/UrlDecode?" + sourceUrl).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + Assert.Equal(unencodedUrl, reply); + } + + [Theory] + [InlineData("a=b&a=c", "a=b,c")] + [InlineData("a%3Db%26a%3Dc", "a=b,c")] + public async Task Ensure_Array_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl) + { + var client = _factory.CreateClient(); + + var response = await client.GetAsync("Encoder/UrlArrayDecode?" + sourceUrl).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + Assert.Equal(unencodedUrl, reply); + } + } +} 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 4bf6faef7..592b444c9 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -2,9 +2,6 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> @@ -12,13 +9,13 @@ <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="5.0.6" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.9" /> <PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.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" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> <PackageReference Include="Moq" Version="4.16.0" /> </ItemGroup> diff --git a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs index d9ec81a27..976e19d46 100644 --- a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs +++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs @@ -44,10 +44,7 @@ namespace Jellyfin.Server.Integration.Tests protected override void ConfigureWebHost(IWebHostBuilder builder) { // Specify the startup command line options - var commandLineOpts = new StartupOptions - { - NoWebClient = true - }; + var commandLineOpts = new StartupOptions(); // Use a temporary directory for the application paths var webHostPathRoot = Path.Combine(_testPathRoot, "test-host-" + Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); diff --git a/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs b/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs new file mode 100644 index 000000000..8c49a2e2b --- /dev/null +++ b/tests/Jellyfin.Server.Integration.Tests/Middleware/RobotsRedirectionMiddlewareTests.cs @@ -0,0 +1,32 @@ +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; + +namespace Jellyfin.Server.Integration.Tests.Middleware +{ + public sealed class RobotsRedirectionMiddlewareTests : IClassFixture<JellyfinApplicationFactory> + { + private readonly JellyfinApplicationFactory _factory; + + public RobotsRedirectionMiddlewareTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task RobotsDotTxtRedirects() + { + var client = _factory.CreateClient( + new WebApplicationFactoryClientOptions() + { + AllowAutoRedirect = false + }); + + var response = await client.GetAsync("robots.txt").ConfigureAwait(false); + + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Equal("web/robots.txt", response.Headers.Location?.ToString()); + } + } +} diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 260b99df9..f249be674 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -3,9 +3,6 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> @@ -13,12 +10,12 @@ <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="5.0.6" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.9" /> <PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> <PackageReference Include="Moq" Version="4.16.0" /> </ItemGroup> diff --git a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs index 146b16cf9..b92cb165c 100644 --- a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs +++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs @@ -1,10 +1,15 @@ +using System; +using System.Collections.Generic; using System.Globalization; +using System.Linq; +using System.Net; using System.Text; using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; using Jellyfin.Server.Extensions; using MediaBrowser.Common.Configuration; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; @@ -13,20 +18,63 @@ namespace Jellyfin.Server.Tests { public class ParseNetworkTests { - /// <summary> - /// Order of the result has always got to be hosts, then networks. - /// </summary> - /// <param name="ip4">IP4 enabled.</param> - /// <param name="ip6">IP6 enabled.</param> - /// <param name="hostList">List to parse.</param> - /// <param name="match">What it should match.</param> + public static TheoryData<bool, bool, string[], IPAddress[], IPNetwork[]> TestNetworks_TestData() + { + var data = new TheoryData<bool, bool, string[], IPAddress[], IPNetwork[]>(); + data.Add( + true, + true, + new string[] { "192.168.t", "127.0.0.1", "1234.1232.12.1234" }, + new IPAddress[] { IPAddress.Loopback.MapToIPv6() }, + Array.Empty<IPNetwork>()); + + data.Add( + true, + false, + new string[] { "192.168.x", "127.0.0.1", "1234.1232.12.1234" }, + new IPAddress[] { IPAddress.Loopback }, + Array.Empty<IPNetwork>()); + + data.Add( + true, + true, + new string[] { "::1" }, + Array.Empty<IPAddress>(), + new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) }); + + data.Add( + false, + false, + new string[] { "localhost" }, + Array.Empty<IPAddress>(), + Array.Empty<IPNetwork>()); + + data.Add( + true, + false, + new string[] { "localhost" }, + new IPAddress[] { IPAddress.Loopback }, + Array.Empty<IPNetwork>()); + + data.Add( + false, + true, + new string[] { "localhost" }, + Array.Empty<IPAddress>(), + new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) }); + + data.Add( + true, + true, + new string[] { "localhost" }, + new IPAddress[] { IPAddress.Loopback.MapToIPv6() }, + new IPNetwork[] { new IPNetwork(IPAddress.IPv6Loopback, 128) }); + return data; + } + [Theory] - // [InlineData(true, true, "192.168.0.0/16,www.yahoo.co.uk", "::ffff:212.82.100.150,::ffff:192.168.0.0/16")] <- fails on Max. www.yahoo.co.uk resolves to a different ip address. - // [InlineData(true, false, "192.168.0.0/16,www.yahoo.co.uk", "212.82.100.150,192.168.0.0/16")] - [InlineData(true, true, "192.168.t,127.0.0.1,1234.1232.12.1234", "::ffff:127.0.0.1")] - [InlineData(true, false, "192.168.x,127.0.0.1,1234.1232.12.1234", "127.0.0.1")] - [InlineData(true, true, "::1", "::1/128")] - public void TestNetworks(bool ip4, bool ip6, string hostList, string match) + [MemberData(nameof(TestNetworks_TestData))] + public void TestNetworks(bool ip4, bool ip6, string[] hostList, IPAddress[] knownProxies, IPNetwork[] knownNetworks) { using var nm = CreateNetworkManager(); @@ -36,31 +84,25 @@ namespace Jellyfin.Server.Tests EnableIPV6 = ip6 }; - var result = match + ","; ForwardedHeadersOptions options = new ForwardedHeadersOptions(); // Need this here as ::1 and 127.0.0.1 are in them by default. options.KnownProxies.Clear(); options.KnownNetworks.Clear(); - ApiServiceCollectionExtensions.AddProxyAddresses(settings, hostList.Split(','), options); + ApiServiceCollectionExtensions.AddProxyAddresses(settings, hostList, options); - var sb = new StringBuilder(); - foreach (var item in options.KnownProxies) + Assert.Equal(knownProxies.Length, options.KnownProxies.Count); + foreach (var item in knownProxies) { - sb.Append(item) - .Append(','); + Assert.True(options.KnownProxies.Contains(item)); } - foreach (var item in options.KnownNetworks) + Assert.Equal(knownNetworks.Length, options.KnownNetworks.Count); + foreach (var item in knownNetworks) { - sb.Append(item.Prefix) - .Append('/') - .Append(item.PrefixLength.ToString(CultureInfo.InvariantCulture)) - .Append(','); + Assert.NotNull(options.KnownNetworks.FirstOrDefault(x => x.Prefix.Equals(item.Prefix) && x.PrefixLength == item.PrefixLength)); } - - Assert.Equal(sb.ToString(), result); } private static IConfigurationManager GetMockConfig(NetworkConfiguration conf) diff --git a/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs b/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs new file mode 100644 index 000000000..419afb2dc --- /dev/null +++ b/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; +using Jellyfin.Server.Middleware; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Jellyfin.Server.Tests +{ + public static class UrlDecodeQueryFeatureTests + { + [Theory] + [InlineData("e0a72cb2a2c7", "e0a72cb2a2c7")] // isn't encoded + [InlineData("random+test", "random test")] // encoded + [InlineData("random%20test", "random test")] // encoded + [InlineData("++", " ")] // encoded + public static void EmptyValueTest(string query, string key) + { + var dict = new Dictionary<string, StringValues> + { + { query, StringValues.Empty } + }; + var test = new UrlDecodeQueryFeature(new QueryFeature(new QueryCollection(dict))); + Assert.Single(test.Query); + var (k, v) = test.Query.First(); + Assert.Equal(key, k); + Assert.Empty(v); + } + } +} diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index 4132205c3..e08590758 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -3,9 +3,6 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> <IsPackable>false</IsPackable> - <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <Nullable>enable</Nullable> - <AnalysisMode>AllEnabledByDefault</AnalysisMode> <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> @@ -16,11 +13,11 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="Moq" Version="4.16.1" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" /> - <PackageReference Include="coverlet.collector" Version="3.0.3" /> + <PackageReference Include="coverlet.collector" Version="3.1.0" /> </ItemGroup> <!-- Code Analyzers --> diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs index 357d61c0b..8019e0ab3 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs @@ -1,8 +1,8 @@ -using System.Linq; +using System; +using System.Linq; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.System; using MediaBrowser.XbmcMetadata.Savers; using Xunit; @@ -28,7 +28,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Location var path2 = "/media/movies/Avengers Endgame/movie.nfo"; // uses ContainingFolderPath which uses Operating system specific paths - if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) + if (OperatingSystem.IsWindows()) { movie.Path = movie.Path.Replace('/', '\\'); path1 = path1.Replace('/', '\\'); @@ -49,7 +49,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Location var path2 = "/media/movies/Avengers Endgame/VIDEO_TS/VIDEO_TS.nfo"; // uses ContainingFolderPath which uses Operating system specific paths - if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) + if (OperatingSystem.IsWindows()) { movie.Path = movie.Path.Replace('/', '\\'); path1 = path1.Replace('/', '\\'); diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs index 9ad093a2b..3e726f23d 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs @@ -14,8 +14,6 @@ using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; -#pragma warning disable CA5369 - namespace Jellyfin.XbmcMetadata.Tests.Parsers { public class EpisodeNfoProviderTests diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index 30a48857a..ef3ca15d5 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -59,7 +59,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers _localImageFileMetadata = new FileSystemMetadata() { Exists = true, - FullName = MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows ? + FullName = OperatingSystem.IsWindows() ? "C:\\media\\movies\\Justice League (2017).jpg" : "/media/movies/Justice League (2017).jpg" }; @@ -208,6 +208,20 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers } [Fact] + public void Parse_GivenFileWithFanartTag_Success() + { + var result = new MetadataResult<Video>() + { + Item = new Movie() + }; + + _parser.Fetch(result, "Test Data/Fanart.nfo", CancellationToken.None); + + Assert.Single(result.RemoteImages.Where(x => x.type == ImageType.Backdrop)); + Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg", result.RemoteImages.First(x => x.type == ImageType.Backdrop).url); + } + + [Fact] public void Parse_RadarrUrlFile_Success() { var result = new MetadataResult<Video>() diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs index 2129f3422..eea8cb50a 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA5369 - -using System; +using System; using System.Threading; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.Audio; diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs index 0e61fa2a1..31110dbd7 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA5369 - -using System; +using System; using System.Linq; using System.Threading; using MediaBrowser.Common.Configuration; diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Fanart.nfo b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Fanart.nfo new file mode 100644 index 000000000..0b129bd8c --- /dev/null +++ b/tests/Jellyfin.XbmcMetadata.Tests/Test Data/Fanart.nfo @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<movie> + <thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5865bf95cbadb.png</thumb> + <thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-585e9ca3bcf6a.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-585e9ca3bcf6a.png</thumb> + <thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-57b476a831d74.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-57b476a831d74.png</thumb> + <thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-57947e28cf10b.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-57947e28cf10b.png</thumb> + <thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-5863d5c0cf0c9.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5863d5c0cf0c9.png</thumb> + <thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-5a801747e5545.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5a801747e5545.png</thumb> + <thumb aspect="clearlogo" preview="https://assets.fanart.tv/preview/movies/141052/hdmovielogo/justice-league-5cd75683df92b.png">https://assets.fanart.tv/fanart/movies/141052/hdmovielogo/justice-league-5cd75683df92b.png</thumb> + <thumb aspect="banner" preview="https://assets.fanart.tv/preview/movies/141052/moviebanner/justice-league-586017e95adbd.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-586017e95adbd.jpg</thumb> + <thumb aspect="banner" preview="https://assets.fanart.tv/preview/movies/141052/moviebanner/justice-league-5934d45bc6592.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-5934d45bc6592.jpg</thumb> + <thumb aspect="banner" preview="https://assets.fanart.tv/preview/movies/141052/moviebanner/justice-league-5aa9289a379fa.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebanner/justice-league-5aa9289a379fa.jpg</thumb> + <thumb aspect="landscape" preview="https://assets.fanart.tv/preview/movies/141052/moviethumb/justice-league-585fb155c3743.jpg">https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-585fb155c3743.jpg</thumb> + <thumb aspect="landscape" preview="https://assets.fanart.tv/preview/movies/141052/moviethumb/justice-league-585edbda91d82.jpg">https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-585edbda91d82.jpg</thumb> + <thumb aspect="landscape" preview="https://assets.fanart.tv/preview/movies/141052/moviethumb/justice-league-5b86588882c12.jpg">https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-5b86588882c12.jpg</thumb> + <thumb aspect="landscape" preview="https://assets.fanart.tv/preview/movies/141052/moviethumb/justice-league-5bbb9babe600c.jpg">https://assets.fanart.tv/fanart/movies/141052/moviethumb/justice-league-5bbb9babe600c.jpg</thumb> + <thumb aspect="clearart" preview="https://assets.fanart.tv/preview/movies/141052/hdmovieclearart/justice-league-5865c23193041.png">https://assets.fanart.tv/fanart/movies/141052/hdmovieclearart/justice-league-5865c23193041.png</thumb> + <thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a3af26360617.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png</thumb> + <thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-58690967b9765.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-58690967b9765.png</thumb> + <thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a953ca4db6a6.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a953ca4db6a6.png</thumb> + <thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a0b913c233be.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a0b913c233be.png</thumb> + <thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a87e0cdb1209.png</thumb> + <thumb aspect="discart" preview="https://assets.fanart.tv/preview/movies/141052/moviedisc/justice-league-59dc595362ef1.png">https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-59dc595362ef1.png</thumb> + <fanart> + <thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a5332c7b5e77.jpg</thumb> + <thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a53cf2dac1c8.jpg</thumb> + <thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5976ba93eb5d3.jpg</thumb> + <thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-58fa1f1932897.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-58fa1f1932897.jpg</thumb> + <thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a14f5fd8dd16.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a14f5fd8dd16.jpg</thumb> + <thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5a119394ea362.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5a119394ea362.jpg</thumb> + </fanart> + <thumb aspect="fanart">This-should-not-be-saved-as-a-fanart-image.jpg</thumb> +</movie> |
