Merge branch 'master' into TVFix

This commit is contained in:
cvium
2021-09-05 10:11:17 +02:00
1033 changed files with 19808 additions and 13191 deletions

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using AutoFixture;
using AutoFixture.AutoMoq;
using Jellyfin.Api.Controllers;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.StreamingDtos;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using Moq;
using Xunit;
namespace Jellyfin.Api.Tests.Controllers
{
public class DynamicHlsControllerTests
{
[Theory]
[MemberData(nameof(GetSegmentLengths_Success_TestData))]
public void GetSegmentLengths_Success(long runtimeTicks, int segmentlength, double[] expected)
{
var res = DynamicHlsController.GetSegmentLengthsInternal(runtimeTicks, segmentlength);
Assert.Equal(expected.Length, res.Length);
for (int i = 0; i < expected.Length; i++)
{
Assert.Equal(expected[i], res[i]);
}
}
public static IEnumerable<object[]> GetSegmentLengths_Success_TestData()
{
yield return new object[] { 0, 6, Array.Empty<double>() };
yield return new object[]
{
TimeSpan.FromSeconds(3).Ticks,
6,
new double[] { 3 }
};
yield return new object[]
{
TimeSpan.FromSeconds(6).Ticks,
6,
new double[] { 6 }
};
yield return new object[]
{
TimeSpan.FromSeconds(3.3333333).Ticks,
6,
new double[] { 3.3333333 }
};
yield return new object[]
{
TimeSpan.FromSeconds(9.3333333).Ticks,
6,
new double[] { 6, 3.3333333 }
};
}
}
}

View File

@@ -8,22 +8,19 @@
<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="AutoFixture" Version="4.16.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.16.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.16.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
<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.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>

View File

@@ -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)
{

View File

@@ -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 -->

View File

@@ -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);
}
}
}

View File

@@ -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 -->

View File

@@ -0,0 +1,131 @@
using Emby.Dlna;
using Emby.Dlna.PlayTo;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace Jellyfin.Dlna.Tests
{
public class DlnaManagerTests
{
private DlnaManager GetManager()
{
var xmlSerializer = new Mock<IXmlSerializer>();
var fileSystem = new Mock<IFileSystem>();
var appPaths = new Mock<IApplicationPaths>();
var loggerFactory = new Mock<ILoggerFactory>();
var appHost = new Mock<IServerApplicationHost>();
return new DlnaManager(xmlSerializer.Object, fileSystem.Object, appPaths.Object, loggerFactory.Object, appHost.Object);
}
[Fact]
public void IsMatch_GivenMatchingName_ReturnsTrue()
{
var device = new DeviceInfo()
{
Name = "My Device",
Manufacturer = "LG Electronics",
ManufacturerUrl = "http://www.lge.com",
ModelDescription = "LG WebOSTV DMRplus",
ModelName = "LG TV",
ModelNumber = "1.0",
};
var profile = new DeviceProfile()
{
Name = "Test Profile",
FriendlyName = "My Device",
Manufacturer = "LG Electronics",
ManufacturerUrl = "http://www.lge.com",
ModelDescription = "LG WebOSTV DMRplus",
ModelName = "LG TV",
ModelNumber = "1.0",
Identification = new ()
{
FriendlyName = "My Device",
Manufacturer = "LG Electronics",
ManufacturerUrl = "http://www.lge.com",
ModelDescription = "LG WebOSTV DMRplus",
ModelName = "LG TV",
ModelNumber = "1.0",
}
};
var profile2 = new DeviceProfile()
{
Name = "Test Profile",
FriendlyName = "My Device",
Identification = new DeviceIdentification()
{
FriendlyName = "My Device",
}
};
var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile2.Identification);
var deviceMatch2 = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
Assert.True(deviceMatch);
Assert.True(deviceMatch2);
}
[Fact]
public void IsMatch_GivenNamesAndManufacturersDoNotMatch_ReturnsFalse()
{
var device = new DeviceInfo()
{
Name = "My Device",
Manufacturer = "JVC"
};
var profile = new DeviceProfile()
{
Name = "Test Profile",
FriendlyName = "My Device",
Manufacturer = "LG Electronics",
ManufacturerUrl = "http://www.lge.com",
ModelDescription = "LG WebOSTV DMRplus",
ModelName = "LG TV",
ModelNumber = "1.0",
Identification = new ()
{
FriendlyName = "My Device",
Manufacturer = "LG Electronics",
ManufacturerUrl = "http://www.lge.com",
ModelDescription = "LG WebOSTV DMRplus",
ModelName = "LG TV",
ModelNumber = "1.0",
}
};
var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
Assert.False(deviceMatch);
}
[Fact]
public void IsMatch_GivenNamesAndRegExMatch_ReturnsTrue()
{
var device = new DeviceInfo()
{
Name = "My Device"
};
var profile = new DeviceProfile()
{
Name = "Test Profile",
FriendlyName = "My .*",
Identification = new ()
};
var deviceMatch = GetManager().IsMatch(device.ToDeviceIdentification(), profile.Identification);
Assert.True(deviceMatch);
}
}
}

View File

@@ -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="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 -->

View File

@@ -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));
}
}

View File

@@ -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
{

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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);
}
}
}
}

View File

@@ -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
{

View File

@@ -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.

View File

@@ -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.

View File

@@ -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
{

View File

@@ -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));
}
}
}

View File

@@ -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) };

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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 -->

View File

@@ -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);
}
}
}

View File

@@ -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"
}
}
}

View File

@@ -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"
}
}
}

View File

@@ -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 ODoherty",
"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"
}
}
}

View File

@@ -0,0 +1,19 @@
using MediaBrowser.Model.Dlna;
using Xunit;
namespace Jellyfin.Model.Tests.Dlna
{
public class ContainerProfileTests
{
private readonly ContainerProfile _emptyContainerProfile = new ContainerProfile();
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("mp4")]
public void ContainsContainer_EmptyContainerProfile_True(string? containers)
{
Assert.True(_emptyContainerProfile.ContainsContainer(containers));
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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 -->

View File

@@ -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>

View File

@@ -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)]

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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
{

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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()
{
@@ -148,7 +148,7 @@ namespace Jellyfin.Naming.Tests.Video
yield return new object[]
{
new VideoFileInfo(
path: @"/server/Movies/Rain Man 1988 REMASTERED 1080p BluRay x264 AAC - Ozlem/Rain Man 1988 REMASTERED 1080p BluRay x264 AAC - Ozlem.mp4",
path: @"/server/Movies/Rain Man 1988 REMASTERED 1080p BluRay x264 AAC - JEFF/Rain Man 1988 REMASTERED 1080p BluRay x264 AAC - JEFF.mp4",
container: "mp4",
name: "Rain Man",
year: 1988)
@@ -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,12 +194,16 @@ 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]);
Assert.NotNull(results[1]);
Assert.Null(results[2]);
foreach (var result in results)
{
Assert.Null(result?.Container);
}
}
}
}

View File

@@ -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 _));
}
}

View File

@@ -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 _));
}
}

View File

@@ -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>

View File

@@ -56,66 +56,6 @@ namespace Jellyfin.Networking.Tests
Assert.Equal(nm.GetInternalBindAddresses().AsString(), value);
}
/// <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>
@@ -384,6 +324,9 @@ namespace Jellyfin.Networking.Tests
[InlineData("jellyfin.org", "eth16", false, "eth16")]
// User on external network, no binding - so result is the 1st external.
[InlineData("jellyfin.org", "", false, "eth11")]
// Dns failure - should skip the test.
// https://en.wikipedia.org/wiki/.test
[InlineData("invalid.domain.test", "", false, "eth11")]
// User assumed to be internal, no binding - so result is the 1st internal.
[InlineData("", "", false, "eth16")]
public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result)
@@ -416,10 +359,13 @@ namespace Jellyfin.Networking.Tests
_ = nm.TryParseInterface(result, out Collection<IPObject>? resultObj);
if (resultObj != null)
// Check to see if dns resolution is working. If not, skip test.
_ = IPHost.TryParse(source, out var host);
if (resultObj != null && host?.HasAddress == true)
{
result = ((IPNetAddress)resultObj[0]).ToString(true);
var intf = nm.GetBindInterface(source, out int? _);
var intf = nm.GetBindInterface(source, out _);
Assert.Equal(intf, result);
}

View File

@@ -0,0 +1,34 @@
<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="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.1.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</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.Providers/MediaBrowser.Providers.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,96 @@
#pragma warning disable CA1002 // Do not expose generic lists
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Providers.MediaInfo;
using Moq;
using Xunit;
namespace Jellyfin.Providers.Tests.MediaInfo
{
public class SubtitleResolverTests
{
public static IEnumerable<object[]> AddExternalSubtitleStreams_GivenMixedFilenames_ReturnsValidSubtitles_TestData()
{
var index = 0;
yield return new object[]
{
new List<MediaStream>(),
"/video/My.Video.mkv",
index,
new[]
{
"/video/My.Video.mp3",
"/video/My.Video.png",
"/video/My.Video.srt",
"/video/My.Video.txt",
"/video/My.Video.vtt",
"/video/My.Video.ass",
"/video/My.Video.sub",
"/video/My.Video.ssa",
"/video/My.Video.smi",
"/video/My.Video.sami",
"/video/My.Video.en.srt",
"/video/My.Video.default.en.srt",
"/video/My.Video.default.forced.en.srt",
"/video/My.Video.en.default.forced.srt",
"/video/My.Video.With.Additional.Garbage.en.srt",
"/video/My.Video With Additional Garbage.srt"
},
new[]
{
CreateMediaStream("/video/My.Video.srt", "srt", null, index++),
CreateMediaStream("/video/My.Video.vtt", "vtt", null, index++),
CreateMediaStream("/video/My.Video.ass", "ass", null, index++),
CreateMediaStream("/video/My.Video.sub", "sub", null, index++),
CreateMediaStream("/video/My.Video.ssa", "ssa", null, index++),
CreateMediaStream("/video/My.Video.smi", "smi", null, index++),
CreateMediaStream("/video/My.Video.sami", "sami", null, index++),
CreateMediaStream("/video/My.Video.en.srt", "srt", "en", index++),
CreateMediaStream("/video/My.Video.default.en.srt", "srt", "en", index++, isDefault: true),
CreateMediaStream("/video/My.Video.default.forced.en.srt", "srt", "en", index++, isForced: true, isDefault: true),
CreateMediaStream("/video/My.Video.en.default.forced.srt", "srt", "en", index++, isForced: true, isDefault: true),
CreateMediaStream("/video/My.Video.With.Additional.Garbage.en.srt", "srt", "en", index),
}
};
}
[Theory]
[MemberData(nameof(AddExternalSubtitleStreams_GivenMixedFilenames_ReturnsValidSubtitles_TestData))]
public void AddExternalSubtitleStreams_GivenMixedFilenames_ReturnsValidSubtitles(List<MediaStream> streams, string videoPath, int startIndex, string[] files, MediaStream[] expectedResult)
{
new SubtitleResolver(Mock.Of<ILocalizationManager>()).AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
Assert.Equal(expectedResult.Length, streams.Count);
for (var i = 0; i < expectedResult.Length; i++)
{
var expected = expectedResult[i];
var actual = streams[i];
Assert.Equal(expected.Index, actual.Index);
Assert.Equal(expected.Type, actual.Type);
Assert.Equal(expected.IsExternal, actual.IsExternal);
Assert.Equal(expected.Path, actual.Path);
Assert.Equal(expected.IsDefault, actual.IsDefault);
Assert.Equal(expected.IsForced, actual.IsForced);
Assert.Equal(expected.Language, actual.Language);
}
}
private static MediaStream CreateMediaStream(string path, string codec, string? language, int index, bool isForced = false, bool isDefault = false)
{
return new ()
{
Index = index,
Codec = codec,
Type = MediaStreamType.Subtitle,
IsExternal = true,
Path = path,
IsDefault = isDefault,
IsForced = isForced,
Language = language
};
}
}
}

View File

@@ -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
{

View File

@@ -0,0 +1,27 @@
using MediaBrowser.Providers.Plugins.Tmdb;
using Xunit;
namespace Jellyfin.Providers.Tests.Tmdb
{
public static class TmdbUtilsTests
{
[Theory]
[InlineData("de", "de")]
[InlineData("En", "En")]
[InlineData("de-de", "de-DE")]
[InlineData("en-US", "en-US")]
[InlineData("de-CH", "de")]
public static void NormalizeLanguage_Valid_Success(string input, string expected)
{
Assert.Equal(expected, TmdbUtils.NormalizeLanguage(input));
}
[Theory]
[InlineData(null, null)]
[InlineData("", "")]
public static void NormalizeLanguage_Invalid_Equal(string? input, string? expected)
{
Assert.Equal(expected, TmdbUtils.NormalizeLanguage(input!));
}
}
}

View File

@@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using AutoFixture;
using AutoFixture.AutoMoq;
using Emby.Server.Implementations.Data;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using Moq;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.Data
{
public class SqliteItemRepositoryTests
{
public const string VirtualMetaDataPath = "%MetadataPath%";
public const string MetaDataPath = "/meta/data/path";
private readonly IFixture _fixture;
private readonly SqliteItemRepository _sqliteItemRepository;
public SqliteItemRepositoryTests()
{
var appHost = new Mock<IServerApplicationHost>();
appHost.Setup(x => x.ExpandVirtualPath(It.IsAny<string>()))
.Returns((string x) => x.Replace(VirtualMetaDataPath, MetaDataPath, StringComparison.Ordinal));
appHost.Setup(x => x.ReverseVirtualPath(It.IsAny<string>()))
.Returns((string x) => x.Replace(MetaDataPath, VirtualMetaDataPath, StringComparison.Ordinal));
_fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
_fixture.Inject(appHost);
_sqliteItemRepository = _fixture.Create<SqliteItemRepository>();
}
public static IEnumerable<object[]> ItemImageInfoFromValueString_Valid_TestData()
{
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",
new ItemImageInfo
{
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[]
{
"https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*0*0",
new ItemImageInfo
{
Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
Type = ImageType.Primary,
}
};
yield return new object[]
{
"https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary",
new ItemImageInfo
{
Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
Type = ImageType.Primary,
}
};
yield return new object[]
{
"https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg*0*Primary*600",
new ItemImageInfo
{
Path = "https://image.tmdb.org/t/p/original/zhB5CHEgqqh4wnEqDNJLfWXJlcL.jpg",
Type = ImageType.Primary,
}
};
yield return new object[]
{
"%MetadataPath%/library/68/68578562b96c80a7ebd530848801f645/poster.jpg*637264380567586027*Primary*600*336",
new ItemImageInfo
{
Path = "/meta/data/path/library/68/68578562b96c80a7ebd530848801f645/poster.jpg",
Type = ImageType.Primary,
DateModified = new DateTime(637264380567586027, DateTimeKind.Utc),
Width = 600,
Height = 336
}
};
}
[Theory]
[MemberData(nameof(ItemImageInfoFromValueString_Valid_TestData))]
public void ItemImageInfoFromValueString_Valid_Success(string value, ItemImageInfo expected)
{
var result = _sqliteItemRepository.ItemImageInfoFromValueString(value);
Assert.Equal(expected.Path, result.Path);
Assert.Equal(expected.Type, result.Type);
Assert.Equal(expected.DateModified, result.DateModified);
Assert.Equal(expected.Width, result.Width);
Assert.Equal(expected.Height, result.Height);
Assert.Equal(expected.BlurHash, result.BlurHash);
}
[Theory]
[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));
}
public static IEnumerable<object[]> DeserializeImages_Valid_TestData()
{
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",
new ItemImageInfo[]
{
new ItemImageInfo()
{
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[]
{
"%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/poster.jpg*637261226720645297*Primary*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/logo.png*637261226720805297*Logo*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/landscape.jpg*637261226721285297*Thumb*0*0|%MetadataPath%/library/2a/2a27372f1e9bc757b1db99721bbeae1e/backdrop.jpg*637261226721685297*Backdrop*0*0",
new ItemImageInfo[]
{
new ItemImageInfo()
{
Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/poster.jpg",
Type = ImageType.Primary,
DateModified = new DateTime(637261226720645297, DateTimeKind.Utc),
},
new ItemImageInfo()
{
Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/logo.png",
Type = ImageType.Logo,
DateModified = new DateTime(637261226720805297, DateTimeKind.Utc),
},
new ItemImageInfo()
{
Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/landscape.jpg",
Type = ImageType.Thumb,
DateModified = new DateTime(637261226721285297, DateTimeKind.Utc),
},
new ItemImageInfo()
{
Path = "/meta/data/path/library/2a/2a27372f1e9bc757b1db99721bbeae1e/backdrop.jpg",
Type = ImageType.Backdrop,
DateModified = new DateTime(637261226721685297, DateTimeKind.Utc),
}
}
};
}
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)
{
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_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)
{
Assert.Equal(expected, _sqliteItemRepository.SerializeImages(value));
}
public static IEnumerable<object[]> DeserializeProviderIds_Valid_TestData()
{
yield return new object[]
{
"Imdb=tt0119567",
new Dictionary<string, string>()
{
{ "Imdb", "tt0119567" },
}
};
yield return new object[]
{
"Imdb=tt0119567|Tmdb=330|TmdbCollection=328",
new Dictionary<string, string>()
{
{ "Imdb", "tt0119567" },
{ "Tmdb", "330" },
{ "TmdbCollection", "328" },
}
};
yield return new object[]
{
"MusicBrainzAlbum=9d363e43-f24f-4b39-bc5a-7ef305c677c7|MusicBrainzReleaseGroup=63eba062-847c-3b73-8b0f-6baf27bba6fa|AudioDbArtist=111352|AudioDbAlbum=2116560|MusicBrainzAlbumArtist=20244d07-534f-4eff-b4d4-930878889970",
new Dictionary<string, string>()
{
{ "MusicBrainzAlbum", "9d363e43-f24f-4b39-bc5a-7ef305c677c7" },
{ "MusicBrainzReleaseGroup", "63eba062-847c-3b73-8b0f-6baf27bba6fa" },
{ "AudioDbArtist", "111352" },
{ "AudioDbAlbum", "2116560" },
{ "MusicBrainzAlbumArtist", "20244d07-534f-4eff-b4d4-930878889970" },
}
};
}
[Theory]
[MemberData(nameof(DeserializeProviderIds_Valid_TestData))]
public void DeserializeProviderIds_Valid_Success(string value, Dictionary<string, string> expected)
{
var result = new ProviderIdsExtensionsTestsObject();
SqliteItemRepository.DeserializeProviderIds(value, result);
Assert.Equal(expected, result.ProviderIds);
}
[Theory]
[MemberData(nameof(DeserializeProviderIds_Valid_TestData))]
public void SerializeProviderIds_Valid_Success(string expected, Dictionary<string, string> values)
{
Assert.Equal(expected, SqliteItemRepository.SerializeProviderIds(values));
}
private class ProviderIdsExtensionsTestsObject : IHasProviderIds
{
public Dictionary<string, string> ProviderIds { get; set; } = new Dictionary<string, string>();
}
}
}

View File

@@ -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]);
@@ -42,10 +42,20 @@ namespace Jellyfin.Server.Implementations.Tests.IO
}
}
[Theory]
[InlineData("ValidFileName", "ValidFileName")]
[InlineData("AC/DC", "AC DC")]
[InlineData("Invalid\0", "Invalid ")]
[InlineData("AC/DC\0KD/A", "AC DC KD A")]
public void GetValidFilename_ReturnsValidFilename(string filename, string expectedFileName)
{
Assert.Equal(expectedFileName, _sut.GetValidFilename(filename));
}
[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");

View File

@@ -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>
@@ -22,14 +19,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.16.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.16.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="AutoFixture" Version="4.17.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="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>

View File

@@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Moq;
using Xunit;
@@ -28,7 +29,10 @@ namespace Jellyfin.Server.Implementations.Tests.Library
{
Parent = parent,
CollectionType = CollectionType.TvShows,
Path = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
FileInfo = new FileSystemMetadata()
{
FullName = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
}
};
Assert.Null(episodeResolver.Resolve(itemResolveArgs));
@@ -48,7 +52,10 @@ namespace Jellyfin.Server.Implementations.Tests.Library
{
Parent = series,
CollectionType = CollectionType.TvShows,
Path = "Extras/Extras S01E01.mkv"
FileInfo = new FileSystemMetadata()
{
FullName = "Extras/Extras S01E01.mkv"
}
};
Assert.NotNull(episodeResolver.Resolve(itemResolveArgs));
}

View File

@@ -33,6 +33,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff", "/home/jeff/", "/home/jeff/myfile.mkv")]
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/home/jeff/", "/home/jeff/myfile.mkv")]
[InlineData("C:\\Users\\jeff\\myfile.mkv", "C:\\Users/jeff/", "/", "/myfile.mkv")]
[InlineData("/o", "/o", "/s", "/s")] // regression test for #5977
public void TryReplaceSubPath_ValidArgs_Correct(string path, string subPath, string newSubPath, string? expectedResult)
{
Assert.True(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result));

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using AutoFixture;
@@ -15,8 +16,6 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
{
public class HdHomerunHostTests
{
private const string TestIp = "http://192.168.1.182";
private readonly Fixture _fixture;
private readonly HdHomerunHost _hdHomerunHost;
@@ -30,7 +29,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
{
return Task.FromResult(new HttpResponseMessage()
{
Content = new StreamContent(File.OpenRead("Test Data/LiveTv/" + m.RequestUri?.Segments[^1]))
Content = new StreamContent(File.OpenRead(Path.Combine("Test Data/LiveTv", m.RequestUri!.Host, m.RequestUri.Segments[^1])))
});
});
@@ -50,7 +49,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
{
var host = new TunerHostInfo()
{
Url = TestIp
Url = "192.168.1.182"
};
var modelInfo = await _hdHomerunHost.GetModelInfo(host, true, CancellationToken.None).ConfigureAwait(false);
@@ -65,6 +64,26 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
Assert.Equal("http://192.168.1.182:80/lineup.json", modelInfo.LineupURL);
}
[Fact]
public async Task GetModelInfo_Legacy_Success()
{
var host = new TunerHostInfo()
{
Url = "10.10.10.100"
};
var modelInfo = await _hdHomerunHost.GetModelInfo(host, true, CancellationToken.None).ConfigureAwait(false);
Assert.Equal("HDHomeRun DUAL", modelInfo.FriendlyName);
Assert.Equal("HDHR3-US", modelInfo.ModelNumber);
Assert.Equal("hdhomerun3_atsc", modelInfo.FirmwareName);
Assert.Equal("20200225", modelInfo.FirmwareVersion);
Assert.Equal("10xxxxx5", modelInfo.DeviceID);
Assert.Null(modelInfo.DeviceAuth);
Assert.Equal(2, modelInfo.TunerCount);
Assert.Equal("http://10.10.10.100:80", modelInfo.BaseURL);
Assert.Null(modelInfo.LineupURL);
}
[Fact]
public async Task GetModelInfo_EmptyUrl_ArgumentException()
{
@@ -81,7 +100,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
{
var host = new TunerHostInfo()
{
Url = TestIp
Url = "192.168.1.182"
};
var channels = await _hdHomerunHost.GetLineup(host, CancellationToken.None).ConfigureAwait(false);
@@ -93,12 +112,24 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
Assert.Equal("http://192.168.1.111:5004/auto/v4.1", channels[0].URL);
}
[Fact]
public async Task GetLineup_Legacy_Success()
{
var host = new TunerHostInfo()
{
Url = "10.10.10.100"
};
// Placeholder json is invalid, just need to make sure we can reach it
await Assert.ThrowsAsync<JsonException>(() => _hdHomerunHost.GetLineup(host, CancellationToken.None));
}
[Fact]
public async Task GetLineup_ImportFavoritesOnly_Success()
{
var host = new TunerHostInfo()
{
Url = TestIp,
Url = "192.168.1.182",
ImportFavoritesOnly = true
};
@@ -114,9 +145,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
[Fact]
public async Task TryGetTunerHostInfo_Valid_Success()
{
var host = await _hdHomerunHost.TryGetTunerHostInfo(TestIp, CancellationToken.None).ConfigureAwait(false);
var host = await _hdHomerunHost.TryGetTunerHostInfo("192.168.1.182", CancellationToken.None).ConfigureAwait(false);
Assert.Equal(_hdHomerunHost.Type, host.Type);
Assert.Equal(TestIp, host.Url);
Assert.Equal("192.168.1.182", host.Url);
Assert.Equal("HDHomeRun PRIME", host.FriendlyName);
Assert.Equal("FFFFFFFF", host.DeviceId);
Assert.Equal(3, host.TunerCount);

View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using Emby.Server.Implementations.LiveTv.EmbyTV;
using MediaBrowser.Controller.LiveTv;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.LiveTv
{
public static class RecordingHelperTests
{
public static IEnumerable<object[]> GetRecordingName_Success_TestData()
{
yield return new object[]
{
"The Incredibles 2020_04_20_21_06_00",
new TimerInfo
{
Name = "The Incredibles",
StartDate = new DateTime(2020, 4, 20, 21, 6, 0, DateTimeKind.Local),
IsMovie = true
}
};
yield return new object[]
{
"The Incredibles (2004)",
new TimerInfo
{
Name = "The Incredibles",
IsMovie = true,
ProductionYear = 2004
}
};
yield return new object[]
{
"The Big Bang Theory 2020_04_20_21_06_00",
new TimerInfo
{
Name = "The Big Bang Theory",
StartDate = new DateTime(2020, 4, 20, 21, 6, 0, DateTimeKind.Local),
IsProgramSeries = true,
}
};
yield return new object[]
{
"The Big Bang Theory S12E10",
new TimerInfo
{
Name = "The Big Bang Theory",
IsProgramSeries = true,
SeasonNumber = 12,
EpisodeNumber = 10
}
};
yield return new object[]
{
"The Big Bang Theory S12E10 The VCR Illumination",
new TimerInfo
{
Name = "The Big Bang Theory",
IsProgramSeries = true,
SeasonNumber = 12,
EpisodeNumber = 10,
EpisodeTitle = "The VCR Illumination"
}
};
yield return new object[]
{
"The Big Bang Theory 2018-12-06",
new TimerInfo
{
Name = "The Big Bang Theory",
IsProgramSeries = true,
OriginalAirDate = new DateTime(2018, 12, 6)
}
};
yield return new object[]
{
"The Big Bang Theory 2018-12-06 - The VCR Illumination",
new TimerInfo
{
Name = "The Big Bang Theory",
IsProgramSeries = true,
OriginalAirDate = new DateTime(2018, 12, 6),
EpisodeTitle = "The VCR Illumination"
}
};
yield return new object[]
{
"The Big Bang Theory 2018_12_06_21_06_00 - The VCR Illumination",
new TimerInfo
{
Name = "The Big Bang Theory",
StartDate = new DateTime(2018, 12, 6, 21, 6, 0, DateTimeKind.Local),
IsProgramSeries = true,
OriginalAirDate = new DateTime(2018, 12, 6),
EpisodeTitle = "The VCR Illumination"
}
};
}
[Theory]
[MemberData(nameof(GetRecordingName_Success_TestData))]
public static void GetRecordingName_Success(string expected, TimerInfo timerInfo)
{
Assert.Equal(expected, RecordingHelper.GetRecordingName(timerInfo));
}
}
}

View File

@@ -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>());
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1 @@
{"FriendlyName":"HDHomeRun DUAL","ModelNumber":"HDHR3-US","Legacy":1,"FirmwareName":"hdhomerun3_atsc","FirmwareVersion":"20200225","DeviceID":"10xxxxx5","TunerCount":2,"BaseURL":"http://10.10.10.100:80"}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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
{
}
}

View File

@@ -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

View File

@@ -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
};
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -2,26 +2,30 @@
<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="AutoFixture" Version="4.16.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.16.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.16.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
<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.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>
<ItemGroup>
<!-- Don't run tests in parallel -->
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />

View File

@@ -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()));

View File

@@ -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());
}
}
}

View File

@@ -0,0 +1,4 @@
{
"parallelizeAssembly": false,
"parallelizeTestCollections": false
}

View File

@@ -3,22 +3,19 @@
<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="AutoFixture" Version="4.16.0" />
<PackageReference Include="AutoFixture.AutoMoq" Version="4.16.0" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.16.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.5" />
<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.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>

View File

@@ -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)

View File

@@ -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);
}
}
}

View File

@@ -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 -->

View File

@@ -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('/', '\\');

View File

@@ -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

View File

@@ -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"
};
@@ -156,7 +156,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal("Justice League Collection", item.CollectionName);
// Images
Assert.Equal(6, result.RemoteImages.Count);
Assert.Equal(7, result.RemoteImages.Count);
var posters = result.RemoteImages.Where(x => x.type == ImageType.Primary).ToList();
Assert.Single(posters);
@@ -182,6 +182,10 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Single(discArt);
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviedisc/justice-league-5a3af26360617.png", discArt[0].url);
var backdrop = result.RemoteImages.Where(x => x.type == ImageType.Backdrop).ToList();
Assert.Single(backdrop);
Assert.Equal("https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg", backdrop[0].url);
// Local Image - contains only one item depending on operating system
Assert.Single(result.Images);
Assert.Equal(_localImageFileMetadata.Name, result.Images[0].FileInfo.Name);
@@ -203,6 +207,20 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal(id, item.ProviderIds[provider]);
}
[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()
{

View File

@@ -1,6 +1,4 @@
#pragma warning disable CA5369
using System;
using System;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities.Audio;

View File

@@ -1,6 +1,4 @@
#pragma warning disable CA5369
using System;
using System;
using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration;

View File

@@ -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>

View File

@@ -82,8 +82,8 @@
<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>
<thumb aspect="fanart">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg</thumb>
<fanart>
<thumb preview="https://assets.fanart.tv/preview/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg">https://assets.fanart.tv/fanart/movies/141052/moviebackground/justice-league-5793f518c6d6e.jpg</thumb>
<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>