Merge remote-tracking branch 'upstream/master' into FixFor5280Part2

This commit is contained in:
BaronGreenback
2021-02-28 10:12:14 +00:00
177 changed files with 2955 additions and 2974 deletions

View File

@@ -1,3 +1,5 @@
using System.Net.Mime;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using MediaBrowser.Model.Branding;
@@ -5,11 +7,11 @@ using Xunit;
namespace Jellyfin.Api.Tests
{
public sealed class BrandingServiceTests : IClassFixture<JellyfinApplicationFactory>
public sealed class BrandingControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
public BrandingServiceTests(JellyfinApplicationFactory factory)
public BrandingControllerTests(JellyfinApplicationFactory factory)
{
_factory = factory;
}
@@ -24,8 +26,9 @@ namespace Jellyfin.Api.Tests
var response = await client.GetAsync("/Branding/Configuration");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType?.ToString());
Assert.True(response.IsSuccessStatusCode);
Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType);
Assert.Equal(Encoding.UTF8.BodyName, response.Content.Headers.ContentType?.CharSet);
var responseBody = await response.Content.ReadAsStreamAsync();
_ = await JsonSerializer.DeserializeAsync<BrandingOptions>(responseBody);
}
@@ -42,8 +45,9 @@ namespace Jellyfin.Api.Tests
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("text/css; charset=utf-8", response.Content.Headers.ContentType?.ToString());
Assert.True(response.IsSuccessStatusCode);
Assert.Equal("text/css", response.Content.Headers.ContentType?.MediaType);
Assert.Equal(Encoding.UTF8.BodyName, response.Content.Headers.ContentType?.CharSet);
}
}
}

View File

@@ -0,0 +1,86 @@
using System.IO;
using System.Net;
using System.Net.Mime;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Jellyfin.Api.Models;
using MediaBrowser.Common.Json;
using Xunit;
namespace Jellyfin.Api.Tests.Controllers
{
public sealed class DashboardControllerTests : IClassFixture<JellyfinApplicationFactory>
{
private readonly JellyfinApplicationFactory _factory;
private readonly JsonSerializerOptions _jsonOpions = JsonDefaults.GetOptions();
public DashboardControllerTests(JellyfinApplicationFactory factory)
{
_factory = factory;
}
[Fact]
public async Task GetDashboardConfigurationPage_NonExistingPage_NotFound()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("web/ConfigurationPage?name=ThisPageDoesntExists").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GetDashboardConfigurationPage_ExistingPage_CorrectPage()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/web/ConfigurationPage?name=TestPlugin").ConfigureAwait(false);
Assert.True(response.IsSuccessStatusCode);
Assert.Equal(MediaTypeNames.Text.Html, response.Content.Headers.ContentType?.MediaType);
StreamReader reader = new StreamReader(typeof(TestPlugin).Assembly.GetManifestResourceStream("Jellyfin.Api.Tests.TestPage.html")!);
Assert.Equal(await response.Content.ReadAsStringAsync(), reader.ReadToEnd());
}
[Fact]
public async Task GetDashboardConfigurationPage_BrokenPage_NotFound()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/web/ConfigurationPage?name=BrokenPage").ConfigureAwait(false);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GetConfigurationPages_NoParams_AllConfigurationPages()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/web/ConfigurationPages").ConfigureAwait(false);
Assert.True(response.IsSuccessStatusCode);
var res = await response.Content.ReadAsStreamAsync();
_ = await JsonSerializer.DeserializeAsync<ConfigurationPageInfo[]>(res, _jsonOpions);
// TODO: check content
}
[Fact]
public async Task GetConfigurationPages_True_MainMenuConfigurationPages()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/web/ConfigurationPages?enableInMainMenu=true").ConfigureAwait(false);
Assert.True(response.IsSuccessStatusCode);
Assert.Equal(MediaTypeNames.Application.Json, response.Content.Headers.ContentType?.MediaType);
Assert.Equal(Encoding.UTF8.BodyName, response.Content.Headers.ContentType?.CharSet);
var res = await response.Content.ReadAsStreamAsync();
var data = await JsonSerializer.DeserializeAsync<ConfigurationPageInfo[]>(res, _jsonOpions);
Assert.Empty(data);
}
}
}

View File

@@ -21,7 +21,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
<PackageReference Include="Moq" Version="4.16.0" />
</ItemGroup>
@@ -41,4 +41,8 @@
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="TestPage.html" />
</ItemGroup>
</Project>

View File

@@ -74,7 +74,7 @@ namespace Jellyfin.Api.Tests
_disposableComponents.Add(loggerFactory);
// Create the app host and initialize it
var appHost = new CoreAppHost(
var appHost = new TestAppHost(
appPaths,
loggerFactory,
commandLineOpts,
@@ -95,7 +95,7 @@ namespace Jellyfin.Api.Tests
var testServer = base.CreateServer(builder);
// Finish initializing the app host
var appHost = (CoreAppHost)testServer.Services.GetRequiredService<IApplicationHost>();
var appHost = (TestAppHost)testServer.Services.GetRequiredService<IApplicationHost>();
appHost.ServiceProvider = testServer.Services;
appHost.InitializeServices().GetAwaiter().GetResult();
appHost.RunStartupTasksAsync().GetAwaiter().GetResult();

View File

@@ -1,17 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Jellyfin.Api.Tests.ModelBinders
{
public enum TestType
{
#pragma warning disable SA1602 // Enumeration items should be documented
How,
Much,
Is,
The,
Fish
#pragma warning restore SA1602 // Enumeration items should be documented
}
}

View File

@@ -37,28 +37,28 @@ namespace Jellyfin.Api.Tests
EnableIPV6 = ip6
};
var result = match + ',';
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.Split(','), options);
var sb = new StringBuilder();
foreach (var item in options.KnownProxies)
{
sb.Append(item);
sb.Append(',');
sb.Append(item)
.Append(',');
}
foreach (var item in options.KnownNetworks)
{
sb.Append(item.Prefix);
sb.Append('/');
sb.Append(item.PrefixLength.ToString(CultureInfo.InvariantCulture));
sb.Append(',');
sb.Append(item.Prefix)
.Append('/')
.Append(item.PrefixLength.ToString(CultureInfo.InvariantCulture))
.Append(',');
}
Assert.Equal(sb.ToString(), result);

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Reflection;
using Emby.Server.Implementations;
using Jellyfin.Server;
using MediaBrowser.Controller;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.Tests
{
/// <summary>
/// Implementation of the abstract <see cref="ApplicationHost" /> class.
/// </summary>
public class TestAppHost : CoreAppHost
{
/// <summary>
/// Initializes a new instance of the <see cref="TestAppHost" /> class.
/// </summary>
/// <param name="applicationPaths">The <see cref="ServerApplicationPaths" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="collection">The <see cref="IServiceCollection"/> to be used by the <see cref="CoreAppHost"/>.</param>
public TestAppHost(
IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
IStartupOptions options,
IFileSystem fileSystem,
IServiceCollection collection)
: base(
applicationPaths,
loggerFactory,
options,
fileSystem,
collection)
{
}
/// <inheritdoc />
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
{
foreach (var a in base.GetAssembliesWithPartsInternal())
{
yield return a;
}
yield return typeof(TestPlugin).Assembly;
}
}
}

View File

@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>TestPlugin</title>
</head>
<body>
<h1>This is a Test Page.</h1>
</body>
</html>

View File

@@ -0,0 +1,43 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
namespace Jellyfin.Api.Tests
{
public class TestPlugin : BasePlugin<BasePluginConfiguration>, IHasWebPages
{
public TestPlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
Instance = this;
}
public static TestPlugin? Instance { get; private set; }
public override Guid Id => new Guid("2d350a13-0bf7-4b61-859c-d5e601b5facf");
public override string Name => nameof(TestPlugin);
public override string Description => "Server test Plugin.";
public IEnumerable<PluginPageInfo> GetPages()
{
yield return new PluginPageInfo
{
Name = Name,
EmbeddedResourcePath = GetType().Namespace + ".TestPage.html"
};
yield return new PluginPageInfo
{
Name = "BrokenPage",
EmbeddedResourcePath = GetType().Namespace + ".foobar"
};
}
}
}

View File

@@ -1,43 +0,0 @@
using System;
using MediaBrowser.Common.Extensions;
using Xunit;
namespace Jellyfin.Common.Tests.Extensions
{
public class StringExtensionsTests
{
[Theory]
[InlineData("", 'q', "")]
[InlineData("Banana split", ' ', "Banana")]
[InlineData("Banana split", 'q', "Banana split")]
public void LeftPart_ValidArgsCharNeedle_Correct(string str, char needle, string expectedResult)
{
var result = str.AsSpan().LeftPart(needle).ToString();
Assert.Equal(expectedResult, result);
}
[Theory]
[InlineData("", "", "")]
[InlineData("", "q", "")]
[InlineData("Banana split", "", "")]
[InlineData("Banana split", " ", "Banana")]
[InlineData("Banana split test", " split", "Banana")]
public void LeftPart_ValidArgsWithoutStringComparison_Correct(string str, string needle, string expectedResult)
{
var result = str.AsSpan().LeftPart(needle).ToString();
Assert.Equal(expectedResult, result);
}
[Theory]
[InlineData("", "", StringComparison.Ordinal, "")]
[InlineData("Banana split", " ", StringComparison.Ordinal, "Banana")]
[InlineData("Banana split test", " split", StringComparison.Ordinal, "Banana")]
[InlineData("Banana split test", " Split", StringComparison.Ordinal, "Banana split test")]
[InlineData("Banana split test", " Splït", StringComparison.InvariantCultureIgnoreCase, "Banana split test")]
public void LeftPart_ValidArgs_Correct(string str, string needle, StringComparison stringComparison, string expectedResult)
{
var result = str.AsSpan().LeftPart(needle, stringComparison).ToString();
Assert.Equal(expectedResult, result);
}
}
}

View File

@@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
</ItemGroup>
<!-- Code Analyzers -->

View File

@@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
</ItemGroup>
<!-- Code Analyzers -->

View File

@@ -11,7 +11,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
</ItemGroup>
<!-- Code Analyzers -->

View File

@@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
</ItemGroup>
<!-- Code Analyzers -->

View File

@@ -3,6 +3,7 @@ using System.Globalization;
using System.IO;
using System.Threading;
using MediaBrowser.MediaEncoding.Subtitles;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Jellyfin.MediaEncoding.Subtitles.Tests
@@ -14,25 +15,15 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests
{
using (var stream = File.OpenRead("Test Data/example.ass"))
{
var parsed = new AssParser().Parse(stream, CancellationToken.None);
var parsed = new AssParser(new NullLogger<AssParser>()).Parse(stream, CancellationToken.None);
Assert.Single(parsed.TrackEvents);
var trackEvent = parsed.TrackEvents[0];
Assert.Equal("1", trackEvent.Id);
Assert.Equal(TimeSpan.Parse("00:00:01.18", CultureInfo.InvariantCulture).Ticks, trackEvent.StartPositionTicks);
Assert.Equal(TimeSpan.Parse("00:00:06.85", CultureInfo.InvariantCulture).Ticks, trackEvent.EndPositionTicks);
Assert.Equal("Like an Angel with pity on nobody\r\nThe second line in subtitle", trackEvent.Text);
Assert.Equal("{\\pos(400,570)}Like an Angel with pity on nobody" + Environment.NewLine + "The second line in subtitle", trackEvent.Text);
}
}
[Fact]
public void ParseFieldHeaders_Valid_Success()
{
const string Line = "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text";
var headers = AssParser.ParseFieldHeaders(Line);
Assert.Equal(1, headers["Start"]);
Assert.Equal(2, headers["End"]);
Assert.Equal(9, headers["Text"]);
}
}
}

View File

@@ -22,7 +22,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests
Assert.Equal("1", trackEvent1.Id);
Assert.Equal(TimeSpan.Parse("00:02:17.440", CultureInfo.InvariantCulture).Ticks, trackEvent1.StartPositionTicks);
Assert.Equal(TimeSpan.Parse("00:02:20.375", CultureInfo.InvariantCulture).Ticks, trackEvent1.EndPositionTicks);
Assert.Equal("Senator, we're making\r\nour final approach into Coruscant.", trackEvent1.Text);
Assert.Equal("Senator, we're making" + Environment.NewLine + "our final approach into Coruscant.", trackEvent1.Text);
var trackEvent2 = parsed.TrackEvents[1];
Assert.Equal("2", trackEvent2.Id);

View File

@@ -1,37 +1,42 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using MediaBrowser.MediaEncoding.Subtitles;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Jellyfin.MediaEncoding.Tests
namespace Jellyfin.MediaEncoding.Subtitles.Tests
{
public class SsaParserTests
{
// commonly shared invariant value between tests, assumes default format order
private const string InvariantDialoguePrefix = "[Events]\nDialogue: ,0:00:00.00,0:00:00.01,,,,,,,";
private SsaParser parser = new SsaParser();
private readonly SsaParser _parser = new SsaParser(new NullLogger<AssParser>());
[Theory]
[InlineData("[EvEnTs]\nDialogue: ,0:00:00.00,0:00:00.01,,,,,,,text", "text")] // label casing insensitivity
[InlineData("[Events]\n,0:00:00.00,0:00:00.01,,,,,,,labelless dialogue", "labelless dialogue")] // no "Dialogue:" label, it is optional
[InlineData("[Events]\nFormat: Text, Start, End, Layer, Effect, Style\nDialogue: reordered text,0:00:00.00,0:00:00.01", "reordered text")] // reordered formats
// TODO: Fix upstream
// [InlineData("[Events]\nFormat: Text, Start, End, Layer, Effect, Style\nDialogue: reordered text,0:00:00.00,0:00:00.01", "reordered text")] // reordered formats
[InlineData(InvariantDialoguePrefix + "Cased TEXT", "Cased TEXT")] // preserve text casing
[InlineData(InvariantDialoguePrefix + " text ", " text ")] // do not trim text
[InlineData(InvariantDialoguePrefix + "text, more text", "text, more text")] // append excess dialogue values (> 10) to text
[InlineData(InvariantDialoguePrefix + "start {\\fnFont Name}text{\\fn} end", "start <font face=\"Font Name\">text</font> end")] // font name
[InlineData(InvariantDialoguePrefix + "start {\\fs10}text{\\fs} end", "start <font size=\"10\">text</font> end")] // font size
[InlineData(InvariantDialoguePrefix + "start {\\c&H112233}text{\\c} end", "start <font color=\"#332211\">text</font> end")] // color
[InlineData(InvariantDialoguePrefix + "start {\\1c&H112233}text{\\1c} end", "start <font color=\"#332211\">text</font> end")] // primay color
[InlineData(InvariantDialoguePrefix + "start {\\fnFont Name}text1 {\\fs10}text2{\\fs}{\\fn} {\\1c&H112233}text3{\\1c} end", "start <font face=\"Font Name\">text1 <font size=\"10\">text2</font></font> <font color=\"#332211\">text3</font> end")] // nested formatting
// TODO: Fix upstream
// [InlineData(InvariantDialoguePrefix + "start {\\1c&H112233}text{\\1c} end", "start <font color=\"#332211\">text</font> end")] // primay color
// [InlineData(InvariantDialoguePrefix + "start {\\fnFont Name}text1 {\\fs10}text2{\\fs}{\\fn} {\\1c&H112233}text3{\\1c} end", "start <font face=\"Font Name\">text1 <font size=\"10\">text2</font></font> <font color=\"#332211\">text3</font> end")] // nested formatting
public void Parse(string ssa, string expectedText)
{
using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa)))
{
SubtitleTrackInfo subtitleTrackInfo = parser.Parse(stream, CancellationToken.None);
SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, CancellationToken.None);
SubtitleTrackEvent actual = subtitleTrackInfo.TrackEvents[0];
Assert.Equal(expectedText, actual.Text);
}
@@ -43,7 +48,7 @@ namespace Jellyfin.MediaEncoding.Tests
{
using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa)))
{
SubtitleTrackInfo subtitleTrackInfo = parser.Parse(stream, CancellationToken.None);
SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, CancellationToken.None);
Assert.Equal(expectedSubtitleTrackEvents.Count, subtitleTrackInfo.TrackEvents.Count);
@@ -52,9 +57,10 @@ namespace Jellyfin.MediaEncoding.Tests
SubtitleTrackEvent expected = expectedSubtitleTrackEvents[i];
SubtitleTrackEvent actual = subtitleTrackInfo.TrackEvents[i];
Assert.Equal(expected.Id, actual.Id);
Assert.Equal(expected.Text, actual.Text);
Assert.Equal(expected.StartPositionTicks, actual.StartPositionTicks);
Assert.Equal(expected.EndPositionTicks, actual.EndPositionTicks);
Assert.Equal(expected.Text, actual.Text);
}
}
}
@@ -71,26 +77,39 @@ namespace Jellyfin.MediaEncoding.Tests
",
new List<SubtitleTrackEvent>
{
new SubtitleTrackEvent
new SubtitleTrackEvent("1", "dialogue1")
{
StartPositionTicks = 11800000,
EndPositionTicks = 18500000,
Text = "dialogue1"
EndPositionTicks = 18500000
},
new SubtitleTrackEvent
new SubtitleTrackEvent("2", "dialogue2")
{
StartPositionTicks = 21800000,
EndPositionTicks = 28500000,
Text = "dialogue2"
EndPositionTicks = 28500000
},
new SubtitleTrackEvent
new SubtitleTrackEvent("3", "dialogue3")
{
StartPositionTicks = 31800000,
EndPositionTicks = 38500000,
Text = "dialogue3"
EndPositionTicks = 38500000
}
}
};
}
[Fact]
public void Parse_Valid_Success()
{
using (var stream = File.OpenRead("Test Data/example.ssa"))
{
var parsed = _parser.Parse(stream, CancellationToken.None);
Assert.Single(parsed.TrackEvents);
var trackEvent = parsed.TrackEvents[0];
Assert.Equal("1", trackEvent.Id);
Assert.Equal(TimeSpan.Parse("00:00:01.18", CultureInfo.InvariantCulture).Ticks, trackEvent.StartPositionTicks);
Assert.Equal(TimeSpan.Parse("00:00:06.85", CultureInfo.InvariantCulture).Ticks, trackEvent.EndPositionTicks);
Assert.Equal("{\\pos(400,570)}Like an angel with pity on nobody", trackEvent.Text);
}
}
}
}

View File

@@ -0,0 +1,20 @@
[Script Info]
; This is a Sub Station Alpha v4 script.
; For Sub Station Alpha info and downloads,
; go to http://www.eswat.demon.co.uk/
Title: Neon Genesis Evangelion - Episode 26 (neutral Spanish)
Original Script: RoRo
Script Updated By: version 2.8.01
ScriptType: v4.00
Collisions: Normal
PlayResY: 600
PlayDepth: 0
Timer: 100,0000
[V4 Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding
Style: DefaultVCD, Arial,28,11861244,11861244,11861244,-2147483640,-1,0,1,1,2,2,30,30,30,0,0
[Events]
Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: Marked=0,0:00:01.18,0:00:06.85,DefaultVCD, NTP,0000,0000,0000,,{\pos(400,570)}Like an angel with pity on nobody

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
using Xunit;
namespace Jellyfin.Model.Tests.Entities
{
public class ProviderIdsExtensionsTests
{
private const string ExampleImdbId = "tt0113375";
[Fact]
public void GetProviderId_NullInstance_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => ProviderIdsExtensions.GetProviderId(null!, MetadataProvider.Imdb));
}
[Fact]
public void GetProviderId_NullName_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => ProviderIdsExtensionsTestsObject.Empty.GetProviderId(null!));
}
[Fact]
public void GetProviderId_NotFoundName_Null()
{
Assert.Null(ProviderIdsExtensionsTestsObject.Empty.GetProviderId(MetadataProvider.Imdb));
}
[Fact]
public void GetProviderId_NullProvider_Null()
{
var nullProvider = new ProviderIdsExtensionsTestsObject()
{
ProviderIds = null!
};
Assert.Null(nullProvider.GetProviderId(MetadataProvider.Imdb));
}
[Fact]
public void TryGetProviderId_NotFoundName_False()
{
Assert.False(ProviderIdsExtensionsTestsObject.Empty.TryGetProviderId(MetadataProvider.Imdb, out _));
}
[Fact]
public void TryGetProviderId_NullProvider_False()
{
var nullProvider = new ProviderIdsExtensionsTestsObject()
{
ProviderIds = null!
};
Assert.False(nullProvider.TryGetProviderId(MetadataProvider.Imdb, out _));
}
[Fact]
public void GetProviderId_FoundName_Id()
{
var provider = new ProviderIdsExtensionsTestsObject();
provider.ProviderIds[MetadataProvider.Imdb.ToString()] = ExampleImdbId;
Assert.Equal(ExampleImdbId, provider.GetProviderId(MetadataProvider.Imdb));
}
[Fact]
public void TryGetProviderId_FoundName_True()
{
var provider = new ProviderIdsExtensionsTestsObject();
provider.ProviderIds[MetadataProvider.Imdb.ToString()] = ExampleImdbId;
Assert.True(provider.TryGetProviderId(MetadataProvider.Imdb, out var id));
Assert.Equal(ExampleImdbId, id);
}
[Fact]
public void SetProviderId_NullInstance_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => ProviderIdsExtensions.SetProviderId(null!, MetadataProvider.Imdb, ExampleImdbId));
}
[Fact]
public void SetProviderId_Null_Remove()
{
var provider = new ProviderIdsExtensionsTestsObject();
provider.SetProviderId(MetadataProvider.Imdb, null!);
Assert.Empty(provider.ProviderIds);
}
[Fact]
public void SetProviderId_EmptyName_Remove()
{
var provider = new ProviderIdsExtensionsTestsObject();
provider.ProviderIds[MetadataProvider.Imdb.ToString()] = ExampleImdbId;
provider.SetProviderId(MetadataProvider.Imdb, string.Empty);
Assert.Empty(provider.ProviderIds);
}
[Fact]
public void SetProviderId_NonEmptyId_Success()
{
var provider = new ProviderIdsExtensionsTestsObject();
provider.SetProviderId(MetadataProvider.Imdb, ExampleImdbId);
Assert.Single(provider.ProviderIds);
}
[Fact]
public void SetProviderId_NullProvider_Success()
{
var nullProvider = new ProviderIdsExtensionsTestsObject()
{
ProviderIds = null!
};
nullProvider.SetProviderId(MetadataProvider.Imdb, ExampleImdbId);
Assert.Single(nullProvider.ProviderIds);
}
[Fact]
public void SetProviderId_NullProviderAndEmptyName_Success()
{
var nullProvider = new ProviderIdsExtensionsTestsObject()
{
ProviderIds = null!
};
nullProvider.SetProviderId(MetadataProvider.Imdb, string.Empty);
Assert.Null(nullProvider.ProviderIds);
}
private class ProviderIdsExtensionsTestsObject : IHasProviderIds
{
public static readonly ProviderIdsExtensionsTestsObject Empty = new ProviderIdsExtensionsTestsObject();
public Dictionary<string, string> ProviderIds { get; set; } = new Dictionary<string, string>();
}
}
}

View File

@@ -8,10 +8,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.1" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
</ItemGroup>
<!-- Code Analyzers -->

View File

@@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
</ItemGroup>
<ItemGroup>

View File

@@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
<PackageReference Include="Moq" Version="4.16.0" />
</ItemGroup>

View File

@@ -13,34 +13,6 @@ namespace Jellyfin.Networking.Tests
{
public class NetworkParseTests
{
/// <summary>
/// Tries to identify the string and return an object of that class.
/// </summary>
/// <param name="addr">String to parse.</param>
/// <param name="result">IPObject to return.</param>
/// <returns>True if the value parsed successfully.</returns>
private static bool TryParse(string addr, out IPObject result)
{
if (!string.IsNullOrEmpty(addr))
{
// Is it an IP address
if (IPNetAddress.TryParse(addr, out IPNetAddress nw))
{
result = nw;
return true;
}
if (IPHost.TryParse(addr, out IPHost h))
{
result = h;
return true;
}
}
result = IPNetAddress.None;
return false;
}
private static IConfigurationManager GetMockConfig(NetworkConfiguration conf)
{
var configManager = new Mock<IConfigurationManager>
@@ -52,15 +24,20 @@ namespace Jellyfin.Networking.Tests
}
/// <summary>
/// Checks the ability to ignore interfaces
/// Checks the ability to ignore virtual interfaces.
/// </summary>
/// <param name="interfaces">Mock network setup, in the format (IP address, interface index, interface name) | .... </param>
/// <param name="lan">LAN addresses.</param>
/// <param name="value">Bind addresses that are excluded.</param>
[Theory]
// All valid
[InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.0/24", "[192.168.1.208/24,200.200.200.200/24]")]
// eth16 only
[InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")]
[InlineData("192.168.1.208/24,-16,vEthernet1|192.168.1.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")]
// All interfaces excluded.
[InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")]
// vEthernet1 and vEthernet212 should be excluded.
[InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")]
public void IgnoreVirtualInterfaces(string interfaces, string lan, string value)
{
var conf = new NetworkConfiguration()
@@ -118,11 +95,33 @@ namespace Jellyfin.Networking.Tests
[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"></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(TryParse(address, out _));
Assert.True(IPNetAddress.TryParse(address, out _));
}
@@ -138,7 +137,8 @@ namespace Jellyfin.Networking.Tests
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
public void InvalidAddressString(string address)
{
Assert.False(TryParse(address, out _));
Assert.False(IPNetAddress.TryParse(address, out _));
Assert.False(IPHost.TryParse(address, out _));
}
@@ -172,11 +172,11 @@ namespace Jellyfin.Networking.Tests
"[]")]
[InlineData(
"192.158.1.2/16, localhost, fd23:184f:2029:0:3139:7386:67d7:d517, !10.10.10.10",
"[192.158.1.2/16,127.0.0.1/32,fd23:184f:2029:0:3139:7386:67d7:d517/128]",
"[192.158.1.2/16,[127.0.0.1/32,::1/128],fd23:184f:2029:0:3139:7386:67d7:d517/128]",
"[192.158.1.2/16,127.0.0.1/32]",
"[10.10.10.10/32]",
"[10.10.10.10/32]",
"[192.158.0.0/16,127.0.0.1/32,fd23:184f:2029:0:3139:7386:67d7:d517/128]")]
"[192.158.0.0/16,127.0.0.1/32,::1/128,fd23:184f:2029:0:3139:7386:67d7:d517/128]")]
[InlineData("192.158.1.2/255.255.0.0,192.169.1.2/8",
"[192.158.1.2/16,192.169.1.2/8]",
"[192.158.1.2/16,192.169.1.2/8]",
@@ -333,8 +333,8 @@ namespace Jellyfin.Networking.Tests
public void TestSubnetContains(string network, string ip)
{
Assert.True(TryParse(network, out IPObject? networkObj));
Assert.True(TryParse(ip, out IPObject? ipObj));
Assert.True(IPNetAddress.TryParse(network, out var networkObj));
Assert.True(IPNetAddress.TryParse(ip, out var ipObj));
Assert.True(networkObj.Contains(ipObj));
}
@@ -468,7 +468,7 @@ namespace Jellyfin.Networking.Tests
// User on internal network, no binding specified - so result is the 1st internal.
[InlineData("192.168.1.1", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")]
// User on external network, internal binding only - so asumption is a proxy forward, return external override.
// User on external network, internal binding only - so assumption is a proxy forward, return external override.
[InlineData("jellyfin.org", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")]
// User on external network, no binding - so result is the 1st external which is overriden.

View File

@@ -26,7 +26,7 @@
<PackageReference Include="Moq" Version="4.16.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
</ItemGroup>
<!-- Code Analyzers -->

View File

@@ -18,7 +18,7 @@
<PackageReference Include="Moq" Version="4.16.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.0.3" />
</ItemGroup>
<!-- Code Analyzers -->

View File

@@ -42,7 +42,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
}
[Fact]
public void Fetch_Valid_Succes()
public void Fetch_Valid_Success()
{
var result = new MetadataResult<Episode>()
{
@@ -97,6 +97,26 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
Assert.Equal(new DateTime(2017, 10, 7, 14, 25, 47), item.DateCreated);
}
[Fact]
public void Fetch_Valid_MultiEpisode_Success()
{
var result = new MetadataResult<Episode>()
{
Item = new Episode()
};
_parser.Fetch(result, "Test Data/Rising.nfo", CancellationToken.None);
var item = result.Item;
Assert.Equal("Rising (1)", item.Name);
Assert.Equal(1, item.IndexNumber);
Assert.Equal(2, item.IndexNumberEnd);
Assert.Equal(1, item.ParentIndexNumber);
Assert.Equal("A new Stargate team embarks on a dangerous mission to a distant galaxy, where they discover a mythical lost city -- and a deadly new enemy.", item.Overview);
Assert.Equal(new DateTime(2004, 7, 16), item.PremiereDate);
Assert.Equal(2004, item.ProductionYear);
}
[Fact]
public void Fetch_WithNullItem_ThrowsArgumentException()
{

View File

@@ -57,7 +57,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
}
[Fact]
public void Fetch_Valid_Succes()
public void Fetch_Valid_Success()
{
var result = new MetadataResult<Video>()
{

View File

@@ -43,7 +43,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
}
[Fact]
public void Fetch_Valid_Succes()
public void Fetch_Valid_Success()
{
var result = new MetadataResult<MusicAlbum>()
{

View File

@@ -40,7 +40,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
}
[Fact]
public void Fetch_Valid_Succes()
public void Fetch_Valid_Success()
{
var result = new MetadataResult<MusicArtist>()
{

View File

@@ -36,7 +36,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
}
[Fact]
public void Fetch_Valid_Succes()
public void Fetch_Valid_Success()
{
var result = new MetadataResult<Season>()
{

View File

@@ -34,7 +34,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers
}
[Fact]
public void Fetch_Valid_Succes()
public void Fetch_Valid_Success()
{
var result = new MetadataResult<Series>()
{

View File

@@ -0,0 +1,20 @@
<episodedetails>
<title>Rising (1)</title>
<season>1</season>
<episode>1</episode>
<aired>2004-07-16</aired>
<plot>A new Stargate team embarks on a dangerous mission to a distant galaxy, where they discover a mythical lost city -- and a deadly new enemy.</plot>
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25333.jpg</thumb>
<watched>false</watched>
<rating>8.0</rating>
</episodedetails>
<episodedetails>
<title>Rising (2)</title>
<season>1</season>
<episode>2</episode>
<aired>2004-07-16</aired>
<plot>Sheppard tries to convince Weir to mount a rescue mission to free Colonel Sumner, Teyla, and the others captured by the Wraith.</plot>
<thumb>https://artworks.thetvdb.com/banners/episodes/70851/25334.jpg</thumb>
<watched>false</watched>
<rating>7.9</rating>
</episodedetails>