aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.Api.Tests/Controllers/SystemControllerTests.cs1
-rw-r--r--tests/Jellyfin.Api.Tests/Controllers/UserControllerTests.cs4
-rw-r--r--tests/Jellyfin.Extensions.Tests/FileHelperTests.cs23
-rw-r--r--tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs4
-rw-r--r--tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml2
-rw-r--r--tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml2
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs34
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_single_frame_mjpeg.json209
-rw-r--r--tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs21
-rw-r--r--tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs8
-rw-r--r--tests/Jellyfin.Model.Tests/Dlna/StreamInfoTests.cs4
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json16
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json16
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkParseTests.cs48
-rw-r--r--tests/Jellyfin.Providers.Tests/Lyrics/LrcLyricParserTests.cs41
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs1
-rw-r--r--tests/Jellyfin.Providers.Tests/Test Data/Lyrics/Fleetwood Mac - Rumors.elrc31
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs184
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs30
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs91
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Playlists/PlaylistManagerTests.cs40
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs5
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs8
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs44
-rw-r--r--tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs3
25 files changed, 720 insertions, 150 deletions
diff --git a/tests/Jellyfin.Api.Tests/Controllers/SystemControllerTests.cs b/tests/Jellyfin.Api.Tests/Controllers/SystemControllerTests.cs
index dd84c1a18..8cb3cde2b 100644
--- a/tests/Jellyfin.Api.Tests/Controllers/SystemControllerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Controllers/SystemControllerTests.cs
@@ -1,4 +1,5 @@
using Jellyfin.Api.Controllers;
+using Jellyfin.Server.Implementations.SystemBackupService;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Model.IO;
diff --git a/tests/Jellyfin.Api.Tests/Controllers/UserControllerTests.cs b/tests/Jellyfin.Api.Tests/Controllers/UserControllerTests.cs
index a74dab5f2..e95df1635 100644
--- a/tests/Jellyfin.Api.Tests/Controllers/UserControllerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Controllers/UserControllerTests.cs
@@ -87,7 +87,7 @@ public class UserControllerTests
Assert.Contains(
Validate(userPolicy), v =>
v.MemberNames.Contains("PasswordResetProviderId") &&
- v.ErrorMessage != null &&
+ v.ErrorMessage is not null &&
v.ErrorMessage.Contains("required", StringComparison.CurrentCultureIgnoreCase));
}
@@ -105,7 +105,7 @@ public class UserControllerTests
Assert.Contains(Validate(userPolicy), v =>
v.MemberNames.Contains("AuthenticationProviderId") &&
- v.ErrorMessage != null &&
+ v.ErrorMessage is not null &&
v.ErrorMessage.Contains("required", StringComparison.CurrentCultureIgnoreCase));
}
diff --git a/tests/Jellyfin.Extensions.Tests/FileHelperTests.cs b/tests/Jellyfin.Extensions.Tests/FileHelperTests.cs
new file mode 100644
index 000000000..fb6a5dd0a
--- /dev/null
+++ b/tests/Jellyfin.Extensions.Tests/FileHelperTests.cs
@@ -0,0 +1,23 @@
+using System.IO;
+using Xunit;
+
+namespace Jellyfin.Extensions.Tests;
+
+public static class FileHelperTests
+{
+ [Fact]
+ public static void CreateEmpty_Valid_Correct()
+ {
+ var path = Path.Join(Path.GetTempPath(), Path.GetRandomFileName());
+ var fileInfo = new FileInfo(path);
+
+ Assert.False(fileInfo.Exists);
+
+ FileHelper.CreateEmpty(path);
+
+ fileInfo.Refresh();
+ Assert.True(fileInfo.Exists);
+
+ File.Delete(path);
+ }
+}
diff --git a/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs b/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs
index 0fb7894e5..b71dc1520 100644
--- a/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs
+++ b/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs
@@ -54,7 +54,7 @@ public class XmlTvListingsProviderTests
Path = path
};
- var startDate = new DateTime(2022, 11, 4);
+ var startDate = new DateTime(2022, 11, 4, 0, 0, 0, DateTimeKind.Utc);
var programs = await _xmlTvListingsProvider.GetProgramsAsync(info, "3297", startDate, startDate.AddDays(1), CancellationToken.None);
var programsList = programs.ToList();
Assert.Single(programsList);
@@ -78,7 +78,7 @@ public class XmlTvListingsProviderTests
Path = path
};
- var startDate = new DateTime(2022, 11, 4);
+ var startDate = new DateTime(2022, 11, 4, 0, 0, 0, DateTimeKind.Utc);
var programs = await _xmlTvListingsProvider.GetProgramsAsync(info, "3297", startDate, startDate.AddDays(1), CancellationToken.None);
var programsList = programs.ToList();
Assert.Single(programsList);
diff --git a/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml
index dd4aa8977..487b02893 100644
--- a/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml
+++ b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml
@@ -1,5 +1,5 @@
<tv date="20221104">
- <programme channel="3297" start="20221104130000 -0400" stop="20221105235959 -0400">
+ <programme channel="3297" start="20221104130000 +0000" stop="20221105235959 +0000">
<category lang="en" />
<category lang="en">sports</category>
</programme>
diff --git a/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml
index 5a5be7997..b3cef41f3 100644
--- a/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml
+++ b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml
@@ -1,5 +1,5 @@
<tv date="20221104">
- <programme channel="3297" start="20221104130000 -0400" stop="20221105235959 -0400">
+ <programme channel="3297" start="20221104130000 +0000" stop="20221105235959 +0000">
<category lang="en">sports</category>
<episode-num system="original-air-date">2022-11-04 13:00:00</episode-num>
<icon height="" src="https://domain.tld/image.png" width=""/>
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index df51d39cb..94710a095 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -289,6 +289,40 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
}
[Fact]
+ public void GetMediaInfo_VideoWithSingleFrameMjpeg_Success()
+ {
+ var bytes = File.ReadAllBytes("Test Data/Probing/video_single_frame_mjpeg.json");
+
+ var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
+ MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_interlaced.mp4", MediaProtocol.File);
+
+ Assert.Equal(3, res.MediaStreams.Count);
+
+ Assert.NotNull(res.VideoStream);
+ Assert.Equal(res.MediaStreams[0], res.VideoStream);
+ Assert.Equal(0, res.VideoStream.Index);
+ Assert.Equal("h264", res.VideoStream.Codec);
+ Assert.Equal("High", res.VideoStream.Profile);
+ Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
+ Assert.Equal(1080, res.VideoStream.Height);
+ Assert.Equal(1920, res.VideoStream.Width);
+ Assert.False(res.VideoStream.IsInterlaced);
+ Assert.Equal("16:9", res.VideoStream.AspectRatio);
+ Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
+ Assert.Equal(42d, res.VideoStream.Level);
+ Assert.Equal(1, res.VideoStream.RefFrames);
+ Assert.True(res.VideoStream.IsAVC);
+ Assert.Equal(50f, res.VideoStream.RealFrameRate);
+ Assert.Equal("1/1000", res.VideoStream.TimeBase);
+ Assert.Equal(8, res.VideoStream.BitDepth);
+ Assert.True(res.VideoStream.IsDefault);
+
+ var mjpeg = res.MediaStreams[2];
+ Assert.NotNull(mjpeg);
+ Assert.Equal("mjpeg", mjpeg.Codec);
+ }
+
+ [Fact]
public void GetMediaInfo_MusicVideo_Success()
{
var bytes = File.ReadAllBytes("Test Data/Probing/music_video_metadata.json");
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_single_frame_mjpeg.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_single_frame_mjpeg.json
new file mode 100644
index 000000000..261f044bb
--- /dev/null
+++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_single_frame_mjpeg.json
@@ -0,0 +1,209 @@
+{
+ "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_tag_string": "[0][0][0][0]",
+ "codec_tag": "0x0000",
+ "width": 1920,
+ "height": 1080,
+ "coded_width": 1920,
+ "coded_height": 1080,
+ "closed_captions": 0,
+ "film_grain": 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": "50/1",
+ "avg_frame_rate": "50/1",
+ "time_base": "1/1000",
+ "start_pts": 0,
+ "start_time": "0.000000",
+ "bits_per_raw_sample": "8",
+ "extradata_size": 55,
+ "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,
+ "non_diegetic": 0,
+ "captions": 0,
+ "descriptions": 0,
+ "metadata": 0,
+ "dependent": 0,
+ "still_image": 0
+ },
+ "tags": {
+ "language": "deu",
+ "HANDLER_NAME": "VideoHandler",
+ "VENDOR_ID": "[0][0][0][0]",
+ "BPS": "3950584",
+ "DURATION": "00:00:10.000000000",
+ "NUMBER_OF_FRAMES": "500",
+ "NUMBER_OF_BYTES": "4938231",
+ "_STATISTICS_WRITING_APP": "mkvpropedit v90.0 ('Hanging On') 64-bit",
+ "_STATISTICS_WRITING_DATE_UTC": "2025-04-19 10:37:57",
+ "_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
+ }
+ },
+ {
+ "index": 1,
+ "codec_name": "aac",
+ "codec_long_name": "AAC (Advanced Audio Coding)",
+ "profile": "LC",
+ "codec_type": "audio",
+ "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,
+ "initial_padding": 0,
+ "r_frame_rate": "0/0",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/1000",
+ "start_pts": 0,
+ "start_time": "0.000000",
+ "extradata_size": 2,
+ "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,
+ "non_diegetic": 0,
+ "captions": 0,
+ "descriptions": 0,
+ "metadata": 0,
+ "dependent": 0,
+ "still_image": 0
+ },
+ "tags": {
+ "language": "deu",
+ "HANDLER_NAME": "SoundHandler",
+ "VENDOR_ID": "[0][0][0][0]",
+ "BPS": "255785",
+ "DURATION": "00:00:09.984000000",
+ "NUMBER_OF_FRAMES": "469",
+ "NUMBER_OF_BYTES": "319220",
+ "_STATISTICS_WRITING_APP": "mkvpropedit v90.0 ('Hanging On') 64-bit",
+ "_STATISTICS_WRITING_DATE_UTC": "2025-04-19 10:37:57",
+ "_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
+ }
+ },
+ {
+ "index": 2,
+ "codec_name": "mjpeg",
+ "codec_long_name": "Motion JPEG",
+ "profile": "Baseline",
+ "codec_type": "video",
+ "codec_tag_string": "[0][0][0][0]",
+ "codec_tag": "0x0000",
+ "width": 960,
+ "height": 540,
+ "coded_width": 960,
+ "coded_height": 540,
+ "closed_captions": 0,
+ "film_grain": 0,
+ "has_b_frames": 0,
+ "sample_aspect_ratio": "1:1",
+ "display_aspect_ratio": "16:9",
+ "pix_fmt": "yuvj420p",
+ "level": -99,
+ "color_range": "pc",
+ "color_space": "bt470bg",
+ "chroma_location": "center",
+ "refs": 1,
+ "r_frame_rate": "1000/1",
+ "avg_frame_rate": "30000/1",
+ "time_base": "1/1000",
+ "start_pts": 0,
+ "start_time": "0.000000",
+ "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": 0,
+ "timed_thumbnails": 0,
+ "non_diegetic": 0,
+ "captions": 0,
+ "descriptions": 0,
+ "metadata": 0,
+ "dependent": 0,
+ "still_image": 0
+ },
+ "tags": {
+ "BPS": "0",
+ "DURATION": "00:00:00.000011111",
+ "NUMBER_OF_FRAMES": "1",
+ "NUMBER_OF_BYTES": "155034",
+ "_STATISTICS_WRITING_APP": "mkvpropedit v90.0 ('Hanging On') 64-bit",
+ "_STATISTICS_WRITING_DATE_UTC": "2025-04-19 10:37:57",
+ "_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
+ }
+ }
+ ],
+ "chapters": [],
+ "format": {
+ "filename": "file:broken_mkv_covers - 01x03 - statistics added using mkvpropedit.mkv",
+ "nb_streams": 3,
+ "nb_programs": 0,
+ "nb_stream_groups": 0,
+ "format_name": "matroska,webm",
+ "format_long_name": "Matroska / WebM",
+ "start_time": "0.000000",
+ "duration": "10.005000",
+ "size": "5425928",
+ "bit_rate": "4338573",
+ "probe_score": 100,
+ "tags": {
+ "title": "Folge 1: Jackpot Immobilie · Wie wir klug vererben",
+ "EPISODE_SORT": "1",
+ "MAJOR_BRAND": "isom",
+ "MINOR_VERSION": "512",
+ "COMPATIBLE_BRANDS": "isomiso2avc1mp41",
+ "DATE": "20250318",
+ "SEASON_NUMBER": "1",
+ "COMMENT": "https://www.ardmediathek.de/video/Y3JpZDovL2JyLmRlL2Jyb2FkY2FzdC8zZWFmYTcxOC1mZDJmLTRmZTMtYWE4Ny03ZjdlNWViNTk1NDhfb25saW5lYnJvYWRjYXN0",
+ "DESCRIPTION": "Jeder kennt es: Enge Familienmitglieder sprechen plötzlich nicht mehr miteinander, der Streit ums Erbe entzweit die Familie. Was steht im Testament? Kann sich die Erbengemeinschaft einigen? Wie läuft das mit der Erbschaftssteuer und was sagt das Erbrecht?\n\nDas \"Lohnt sich das?\"-Team hat zwei Familien gefunden, die das Tabu rund ums Erben brechen. Sie erzählen, um wie viel Geld es geht, aber auch, was dieses Immobilienerbe mit ihrer Familie gemacht hat. Warum es einmal gelingt, Häuser in Millionenhöhe ohne jegliche Erbschaftssteuer zu vererben, während andere Geschwister sich über ihr Elternhaus komplett zerstritten haben. Erbfolge, Freibeträge, Nießbrauch - für alle Basics rund ums Erben ist Ralph Caspers zuständig, der kompetent erklärt. ",
+ "SYNOPSIS": "Jeder kennt es: Enge Familienmitglieder sprechen plötzlich nicht mehr miteinander, der Streit ums Erbe entzweit die Familie. Was steht im Testament? Kann sich die Erbengemeinschaft einigen? Wie läuft das mit der Erbschaftssteuer und was sagt das Erbrecht?\n\nDas \"Lohnt sich das?\"-Team hat zwei Familien gefunden, die das Tabu rund ums Erben brechen. Sie erzählen, um wie viel Geld es geht, aber auch, was dieses Immobilienerbe mit ihrer Familie gemacht hat. Warum es einmal gelingt, Häuser in Millionenhöhe ohne jegliche Erbschaftssteuer zu vererben, während andere Geschwister sich über ihr Elternhaus komplett zerstritten haben. Erbfolge, Freibeträge, Nießbrauch - für alle Basics rund ums Erben ist Ralph Caspers zuständig, der kompetent erklärt. ",
+ "SHOW": "Generation Wohnkrise. Lohnt sich das?",
+ "EPISODE_ID": "Folge 1: Jackpot Immobilie · Wie wir klug vererben",
+ "ENCODER": "Lavf61.7.100"
+ }
+ }
+}
diff --git a/tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs b/tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs
index 981287c03..e32baef55 100644
--- a/tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs
+++ b/tests/Jellyfin.Model.Tests/Dlna/LegacyStreamInfo.cs
@@ -92,7 +92,7 @@ public class LegacyStreamInfo : StreamInfo
private static List<NameValuePair> BuildParams(StreamInfo item, string? accessToken)
{
- var list = new List<NameValuePair>();
+ List<NameValuePair> list = [];
string audioCodecs = item.AudioCodecs.Count == 0 ?
string.Empty :
@@ -109,7 +109,7 @@ public class LegacyStreamInfo : StreamInfo
list.Add(new NameValuePair("VideoCodec", videoCodecs));
list.Add(new NameValuePair("AudioCodec", audioCodecs));
list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
- list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+ list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && (item.AlwaysBurnInSubtitleWhenTranscoding || item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External) ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
list.Add(new NameValuePair("AudioSampleRate", item.AudioSampleRate.HasValue ? item.AudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
@@ -182,25 +182,16 @@ public class LegacyStreamInfo : StreamInfo
list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
}
- if (item.RequireAvc)
- {
- list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
- }
+ list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
- if (item.EnableAudioVbrEncoding)
- {
- list.Add(new NameValuePair("EnableAudioVbrEncoding", item.EnableAudioVbrEncoding.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
- }
+ list.Add(new NameValuePair("EnableAudioVbrEncoding", item.EnableAudioVbrEncoding.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
}
list.Add(new NameValuePair("Tag", item.MediaSource?.ETag ?? string.Empty));
- string subtitleCodecs = item.SubtitleCodecs.Count == 0 ?
- string.Empty :
- string.Join(",", item.SubtitleCodecs);
-
- list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
+ string subtitleCodecs = item.SubtitleCodecs.Count == 0 ? string.Empty : string.Join(",", item.SubtitleCodecs);
list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty));
+ list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
foreach (var pair in item.StreamOptions)
{
diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
index ae9edd386..2c1080ffe 100644
--- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
+++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
@@ -182,6 +182,10 @@ namespace Jellyfin.Model.Tests
[InlineData("Tizen3-stereo", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
[InlineData("Tizen3-stereo", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
[InlineData("Tizen3-stereo", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
+ [InlineData("Tizen3-stereo", "mkv-dvhe.08-eac3-15200k", PlayMethod.DirectPlay)]
+ [InlineData("Tizen3-stereo", "mp4-dvh1.05-eac3-15200k", PlayMethod.Transcode, TranscodeReason.VideoRangeTypeNotSupported | TranscodeReason.AudioChannelsNotSupported, "Transcode")]
+ [InlineData("Tizen3-stereo", "mp4-dvhe.08-eac3-15200k", PlayMethod.DirectPlay)]
+ [InlineData("Tizen3-stereo", "mkv-dvhe.05-eac3-28000k", PlayMethod.Transcode, TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoRangeTypeNotSupported | TranscodeReason.AudioChannelsNotSupported, "Transcode")]
[InlineData("Tizen3-stereo", "numstreams-32", PlayMethod.DirectPlay)]
[InlineData("Tizen3-stereo", "numstreams-33", PlayMethod.Transcode, TranscodeReason.StreamCountExceedsLimit, "Remux")]
// Tizen 4 4K 5.1
@@ -195,6 +199,10 @@ namespace Jellyfin.Model.Tests
[InlineData("Tizen4-4K-5.1", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
[InlineData("Tizen4-4K-5.1", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
[InlineData("Tizen4-4K-5.1", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
+ [InlineData("Tizen4-4K-5.1", "mkv-dvhe.08-eac3-15200k", PlayMethod.DirectPlay)]
+ [InlineData("Tizen4-4K-5.1", "mp4-dvh1.05-eac3-15200k", PlayMethod.Transcode, TranscodeReason.VideoRangeTypeNotSupported, "Transcode")]
+ [InlineData("Tizen4-4K-5.1", "mp4-dvhe.08-eac3-15200k", PlayMethod.DirectPlay)]
+ [InlineData("Tizen4-4K-5.1", "mkv-dvhe.05-eac3-28000k", PlayMethod.Transcode, TranscodeReason.VideoRangeTypeNotSupported, "Transcode")]
[InlineData("Tizen4-4K-5.1", "numstreams-32", PlayMethod.DirectPlay)]
[InlineData("Tizen4-4K-5.1", "numstreams-33", PlayMethod.Transcode, TranscodeReason.StreamCountExceedsLimit, "Remux")]
// WebOS 23
diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamInfoTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamInfoTests.cs
index 86819de8c..8dea46806 100644
--- a/tests/Jellyfin.Model.Tests/Dlna/StreamInfoTests.cs
+++ b/tests/Jellyfin.Model.Tests/Dlna/StreamInfoTests.cs
@@ -31,7 +31,7 @@ public class StreamInfoTests
/// <returns>An <see cref="Array"/> of <see cref="Type"/>.</returns>
private static object? RandomArray(Random random, Type? elementType)
{
- if (elementType == null)
+ if (elementType is null)
{
return null;
}
@@ -148,7 +148,7 @@ public class StreamInfoTests
var type = property.PropertyType;
// If nullable, then set it to null, 25% of the time.
- if (Nullable.GetUnderlyingType(type) != null)
+ if (Nullable.GetUnderlyingType(type) is not null)
{
if (random.Next(0, 4) == 0)
{
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json
index 895d13f07..9d43d2166 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json
@@ -439,7 +439,14 @@
{
"Condition": "EqualsAny",
"Property": "VideoProfile",
- "Value": "high|main|baseline|constrained baseline|high 10",
+ "Value": "high|main|baseline|constrained baseline",
+ "IsRequired": false,
+ "$type": "ProfileCondition"
+ },
+ {
+ "Condition": "EqualsAny",
+ "Property": "VideoRangeType",
+ "Value": "SDR",
"IsRequired": false,
"$type": "ProfileCondition"
},
@@ -479,6 +486,13 @@
"$type": "ProfileCondition"
},
{
+ "Condition": "EqualsAny",
+ "Property": "VideoRangeType",
+ "Value": "SDR|DOVIWithSDR|HDR10|DOVIWithHDR10|HLG|DOVIWithHLG",
+ "IsRequired": false,
+ "$type": "ProfileCondition"
+ },
+ {
"Condition": "LessThanEqual",
"Property": "VideoLevel",
"Value": "183",
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json
index 345d38725..3859ef994 100644
--- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json
@@ -439,7 +439,14 @@
{
"Condition": "EqualsAny",
"Property": "VideoProfile",
- "Value": "high|main|baseline|constrained baseline|high 10",
+ "Value": "high|main|baseline|constrained baseline",
+ "IsRequired": false,
+ "$type": "ProfileCondition"
+ },
+ {
+ "Condition": "EqualsAny",
+ "Property": "VideoRangeType",
+ "Value": "SDR",
"IsRequired": false,
"$type": "ProfileCondition"
},
@@ -472,6 +479,13 @@
"$type": "ProfileCondition"
},
{
+ "Condition": "EqualsAny",
+ "Property": "VideoRangeType",
+ "Value": "SDR|DOVIWithSDR|HDR10|DOVIWithHDR10|HLG|DOVIWithHLG",
+ "IsRequired": false,
+ "$type": "ProfileCondition"
+ },
+ {
"Condition": "LessThanEqual",
"Property": "VideoLevel",
"Value": "183",
diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
index 4144300da..38208476f 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
@@ -79,7 +79,10 @@ namespace Jellyfin.Networking.Tests
[InlineData("[fe80::7add:12ff:febb:c67b%16]")]
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")]
public static void TryParseValidIPStringsTrue(string address)
- => Assert.True(NetworkUtils.TryParseToSubnet(address, out _));
+ {
+ Assert.True(NetworkUtils.TryParseToSubnet(address, out _));
+ Assert.True(NetworkUtils.TryParseToSubnet('!' + address, out _, true));
+ }
/// <summary>
/// Checks invalid IP address formats.
@@ -278,17 +281,40 @@ namespace Jellyfin.Networking.Tests
}
[Theory]
- [InlineData("185.10.10.10,200.200.200.200", "79.2.3.4", true)]
- [InlineData("185.10.10.10", "185.10.10.10", false)]
- [InlineData("", "100.100.100.100", false)]
+ [InlineData("185.10.10.10,200.200.200.200", "79.2.3.4", RemoteAccessPolicyResult.RejectDueToNotAllowlistedRemoteIP)]
+ [InlineData("185.10.10.10", "185.10.10.10", RemoteAccessPolicyResult.Allow)]
+ [InlineData("", "100.100.100.100", RemoteAccessPolicyResult.Allow)]
+
+ public void HasRemoteAccess_GivenWhitelist_AllowsOnlyIPsInWhitelist(string addresses, string remoteIP, RemoteAccessPolicyResult expectedResult)
+ {
+ // Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely.
+ // If left blank, all remote addresses will be allowed.
+ var conf = new NetworkConfiguration()
+ {
+ EnableIPv4 = true,
+ RemoteIPFilter = addresses.Split(','),
+ IsRemoteIPFilterBlacklist = false
+ };
+
+ var startupConf = new Mock<IConfiguration>();
+ using var nm = new NetworkManager(NetworkParseTests.GetMockConfig(conf), startupConf.Object, new NullLogger<NetworkManager>());
+
+ Assert.Equal(expectedResult, nm.ShouldAllowServerAccess(IPAddress.Parse(remoteIP)));
+ }
+
+ [Theory]
+ [InlineData("185.10.10.10,200.200.200.200", "79.2.3.4", RemoteAccessPolicyResult.RejectDueToRemoteAccessDisabled)]
+ [InlineData("185.10.10.10", "127.0.0.1", RemoteAccessPolicyResult.Allow)]
+ [InlineData("", "100.100.100.100", RemoteAccessPolicyResult.RejectDueToRemoteAccessDisabled)]
- public void HasRemoteAccess_GivenWhitelist_AllowsOnlyIPsInWhitelist(string addresses, string remoteIP, bool denied)
+ public void HasRemoteAccess_GivenRemoteAccessDisabled_IgnoresAllowlist(string addresses, string remoteIP, RemoteAccessPolicyResult expectedResult)
{
// Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely.
// If left blank, all remote addresses will be allowed.
var conf = new NetworkConfiguration()
{
EnableIPv4 = true,
+ EnableRemoteAccess = false,
RemoteIPFilter = addresses.Split(','),
IsRemoteIPFilterBlacklist = false
};
@@ -296,15 +322,15 @@ namespace Jellyfin.Networking.Tests
var startupConf = new Mock<IConfiguration>();
using var nm = new NetworkManager(NetworkParseTests.GetMockConfig(conf), startupConf.Object, new NullLogger<NetworkManager>());
- Assert.NotEqual(nm.HasRemoteAccess(IPAddress.Parse(remoteIP)), denied);
+ Assert.Equal(expectedResult, nm.ShouldAllowServerAccess(IPAddress.Parse(remoteIP)));
}
[Theory]
- [InlineData("185.10.10.10", "79.2.3.4", false)]
- [InlineData("185.10.10.10", "185.10.10.10", true)]
- [InlineData("", "100.100.100.100", false)]
+ [InlineData("185.10.10.10", "79.2.3.4", RemoteAccessPolicyResult.Allow)]
+ [InlineData("185.10.10.10", "185.10.10.10", RemoteAccessPolicyResult.RejectDueToIPBlocklist)]
+ [InlineData("", "100.100.100.100", RemoteAccessPolicyResult.Allow)]
- public void HasRemoteAccess_GivenBlacklist_BlacklistTheIPs(string addresses, string remoteIP, bool denied)
+ public void HasRemoteAccess_GivenBlacklist_BlacklistTheIPs(string addresses, string remoteIP, RemoteAccessPolicyResult expectedResult)
{
// Comma separated list of IP addresses or IP/netmask entries for networks that will be allowed to connect remotely.
// If left blank, all remote addresses will be allowed.
@@ -318,7 +344,7 @@ namespace Jellyfin.Networking.Tests
var startupConf = new Mock<IConfiguration>();
using var nm = new NetworkManager(NetworkParseTests.GetMockConfig(conf), startupConf.Object, new NullLogger<NetworkManager>());
- Assert.NotEqual(nm.HasRemoteAccess(IPAddress.Parse(remoteIP)), denied);
+ Assert.Equal(expectedResult, nm.ShouldAllowServerAccess(IPAddress.Parse(remoteIP)));
}
[Theory]
diff --git a/tests/Jellyfin.Providers.Tests/Lyrics/LrcLyricParserTests.cs b/tests/Jellyfin.Providers.Tests/Lyrics/LrcLyricParserTests.cs
new file mode 100644
index 000000000..756a688ab
--- /dev/null
+++ b/tests/Jellyfin.Providers.Tests/Lyrics/LrcLyricParserTests.cs
@@ -0,0 +1,41 @@
+using System.IO;
+using MediaBrowser.Model.Lyrics;
+using MediaBrowser.Providers.Lyric;
+using Xunit;
+
+namespace Jellyfin.Providers.Tests.Lyrics;
+
+public static class LrcLyricParserTests
+{
+ [Fact]
+ public static void ParseElrcCues()
+ {
+ var parser = new LrcLyricParser();
+ var fileContents = File.ReadAllText(Path.Combine("Test Data", "Lyrics", "Fleetwood Mac - Rumors.elrc"));
+ var parsed = parser.ParseLyrics(new LyricFile("Fleetwood Mac - Rumors.elrc", fileContents));
+
+ Assert.NotNull(parsed);
+ Assert.Equal(31, parsed.Lyrics.Count);
+
+ var line1 = parsed.Lyrics[0];
+ Assert.Equal("Every night that goes between", line1.Text);
+ Assert.NotNull(line1.Cues);
+ Assert.Equal(9, line1.Cues.Count);
+ Assert.Equal(68400000, line1.Cues[0].Start);
+ Assert.Equal(72000000, line1.Cues[0].End);
+
+ var line5 = parsed.Lyrics[4];
+ Assert.Equal("Every night you do not come", line5.Text);
+ Assert.NotNull(line5.Cues);
+ Assert.Equal(11, line5.Cues.Count);
+ Assert.Equal(377300000, line5.Cues[5].Start);
+ Assert.Equal(380000000, line5.Cues[5].End);
+
+ var lastLine = parsed.Lyrics[^1];
+ Assert.Equal("I have always been a storm", lastLine.Text);
+ Assert.NotNull(lastLine.Cues);
+ Assert.Equal(11, lastLine.Cues.Count);
+ Assert.Equal(2358000000, lastLine.Cues[^1].Start);
+ Assert.Null(lastLine.Cues[^1].End);
+ }
+}
diff --git a/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs
index c227883b5..87e7a4b56 100644
--- a/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Manager/ProviderManagerTests.cs
@@ -12,6 +12,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Lyrics;
+using MediaBrowser.Controller.MediaSegments;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
diff --git a/tests/Jellyfin.Providers.Tests/Test Data/Lyrics/Fleetwood Mac - Rumors.elrc b/tests/Jellyfin.Providers.Tests/Test Data/Lyrics/Fleetwood Mac - Rumors.elrc
new file mode 100644
index 000000000..1df264c90
--- /dev/null
+++ b/tests/Jellyfin.Providers.Tests/Test Data/Lyrics/Fleetwood Mac - Rumors.elrc
@@ -0,0 +1,31 @@
+[00:06.84] <00:06.84> Every <00:07.20> <00:07.56> night <00:07.87> <00:08.19> that <00:08.46> <00:08.79> goes <00:09.19> <00:09.59> between
+[00:14.69] <00:14.69> I <00:14.78> <00:14.87> feel <00:15.15> <00:15.44> a <00:15.54> <00:15.65> little <00:15.96> <00:16.28> less
+[00:20.98] <00:20.98> As <00:21.10> <00:21.22> you <00:21.27> <00:21.31> slowly <00:21.79> <00:22.27> go <00:22.57> <00:22.87> away <00:23.74> <00:24.19> from <00:24.51> <00:24.82> me
+[00:28.73] <00:28.73> This <00:28.91> <00:29.09> is <00:29.22> <00:29.36> only <00:29.75> <00:30.14> another <00:30.92> <00:31.25> test
+[00:36.11] <00:36.11> Every <00:36.44> <00:36.77> night <00:37.14> <00:37.52> you <00:37.73> <00:38.00> do <00:38.24> <00:38.48> not <00:38.93> <00:39.41> come
+[00:43.56] <00:43.56> Your <00:43.65> <00:43.74> softness <00:44.21> <00:44.69> fades <00:45.01> <00:45.33> away
+[00:50.29] <00:50.29> Did <00:50.42> <00:50.56> I <00:50.70> <00:50.85> ever <00:51.41> <00:51.97> really <00:52.48> <00:52.99> care <00:53.56> <00:53.86> that <00:54.09> <00:54.34> much
+[00:58.07] <00:58.07> Is <00:58.20> <00:58.34> there <00:58.48> <00:58.63> anything <00:59.44> <00:59.69> left <00:59.94> <01:00.20> to <01:00.30> <01:00.41> say?
+[01:05.59] <01:05.59> Every <01:06.58> <01:07.78> hour <01:08.05> <01:08.32> of <01:08.92> <01:09.97> fear <01:10.39> <01:10.81> I <01:11.41> <01:11.47> spend
+[01:13.84] <01:13.84> My <01:13.99> <01:14.14> body <01:14.57> <01:15.01> tries <01:15.32> <01:15.64> to <01:15.71> <01:15.79> cry
+[01:18.60] <01:18.60> Living <01:19.41> <01:20.79> through <01:21.12> <01:21.45> each <01:21.90> <01:23.13> empty <01:23.83> <01:24.54> night
+[01:27.51] <01:27.51> A <01:27.60> <01:27.69> deadly <01:28.12> <01:28.56> call <01:28.90> <01:29.25> inside
+[01:34.29] <01:34.29> I <01:34.39> <01:34.50> haven't <01:35.37> <01:35.64> felt <01:35.92> <01:36.21> this <01:36.34> <01:36.48> way <01:36.79> <01:37.11> I <01:37.35> <01:37.62> feel
+[01:42.11] <01:42.11> Since <01:42.27> <01:42.44> many <01:42.71> <01:42.98> a <01:43.08> <01:43.19> years <01:43.46> <01:43.85> ago
+[01:48.92] <01:48.92> But <01:49.08> <01:49.25> in <01:49.36> <01:49.48> those <01:49.78> <01:50.09> years <01:50.50> <01:50.92> and <01:51.07> <01:51.23> the <01:51.30> <01:51.38> lifetime's <01:51.99> <01:52.61> past
+[01:56.60] <01:56.60> I <01:56.69> <01:56.78> did <01:56.91> <01:57.05> not <01:57.50> <01:57.92> deal <01:58.22> <01:58.52> with <01:58.68> <01:58.85> the <01:58.91> <01:58.97> road
+[02:03.13] <02:03.13> And <02:03.25> <02:03.37> I <02:03.45> <02:03.55> did <02:03.68> <02:03.81> not <02:04.20> <02:04.60> deal <02:04.94> <02:05.29> with <02:05.45> <02:05.62> you, <02:05.90> <02:06.19> I <02:06.50> <02:06.82> know
+[02:10.95] <02:10.95> Though <02:11.11> <02:11.28> the <02:11.35> <02:11.43> love <02:11.79> <02:12.15> has <02:12.32> <02:12.39> always <02:13.06> <02:13.74> been
+[02:17.70] <02:17.70> So <02:17.91> <02:18.12> I <02:18.16> <02:18.21> search <02:18.55> <02:18.90> to <02:19.03> <02:19.17> find <02:19.51> <02:19.86> an <02:20.14> <02:20.43> answer <02:21.24> <02:21.71> there
+[02:25.72] <02:25.72> So <02:25.96> <02:26.20> I <02:26.39> <02:26.59> can <02:27.04> <02:27.19> truly <02:27.65> <02:28.12> win
+[02:33.02] <02:33.02> Every <02:34.04> <02:35.29> hour <02:35.77> <02:35.84> of <02:36.38> <02:37.43> fear <02:37.92> <02:38.42> I <02:38.69> <02:38.96> spend
+[02:41.35] <02:41.35> My <02:41.63> <02:41.91> body <02:42.35> <02:42.79> tries <02:43.09> <02:43.39> to <02:43.48> <02:43.57> cry
+[02:46.32] <02:46.32> Living <02:47.04> <02:48.54> through <02:48.88> <02:49.23> each <02:49.56> <02:50.85> empty <02:51.55> <02:52.26> night
+[02:55.06] <02:55.06> A <02:55.13> <02:55.21> deadly <02:55.64> <02:56.08> call <02:56.42> <02:56.77> inside
+[03:01.50] <03:01.50> So <03:01.74> <03:01.98> I <03:02.04> <03:02.10> try <03:02.42> <03:02.76> to <03:02.81> <03:02.87> say <03:03.45> <03:04.20> goodbye, <03:04.53> <03:04.86> my <03:05.02> <03:05.19> friend
+[03:09.09] <03:09.09> I'd <03:09.19> <03:09.30> like <03:09.42> <03:09.54> to <03:09.70> <03:09.87> leave <03:10.02> <03:10.17> you <03:10.29> <03:10.41> with <03:10.71> <03:10.89> something <03:11.32> <03:11.76> warm
+[03:16.25] <03:16.25> But <03:16.34> <03:16.43> never <03:16.77> <03:17.12> have <03:17.25> <03:17.38> I <03:17.44> <03:17.51> been <03:17.73> <03:17.96> a <03:18.14> <03:18.32> blue <03:18.62> <03:18.92> calm <03:19.58> <03:19.76> sea
+[03:24.18] <03:24.18> I <03:24.31> <03:24.45> have <03:24.57> <03:24.69> always <03:25.59> <03:28.02> been <03:28.19> <03:28.38> a <03:28.51> <03:28.65> storm
+[03:33.69] <03:33.69> Always <03:35.43> <03:36.99> been <03:37.18> <03:37.38> a <03:37.50> <03:37.62> storm
+[03:41.45] <03:41.45> Ooh, <03:41.99> <03:42.53> always <03:44.42> <03:45.86> been <03:46.05> <03:46.25> a <03:46.41> <03:46.58> storm
+[03:50.34] <03:50.34> I <03:50.49> <03:50.64> have <03:51.07> <03:51.51> always <03:53.31> <03:54.78> been <03:54.96> <03:55.14> a <03:55.47> <03:55.80> storm
diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
index 95a5b8179..6997b51ac 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
@@ -5,83 +5,119 @@ using System.Runtime.InteropServices;
using AutoFixture;
using AutoFixture.AutoMoq;
using Emby.Server.Implementations.IO;
+using Jellyfin.Extensions;
using Xunit;
-namespace Jellyfin.Server.Implementations.Tests.IO
+namespace Jellyfin.Server.Implementations.Tests.IO;
+
+public class ManagedFileSystemTests
{
- public class ManagedFileSystemTests
+ private readonly IFixture _fixture;
+ private readonly ManagedFileSystem _sut;
+
+ public ManagedFileSystemTests()
+ {
+ _fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
+ _sut = _fixture.Create<ManagedFileSystem>();
+ }
+
+ [Fact]
+ public void MoveDirectory_SameFileSystem_Correct()
+ => MoveDirectoryInternal();
+
+ [SkippableFact]
+ public void MoveDirectory_DifferentFileSystem_Correct()
+ {
+ const string DestinationParent = "/dev/shm";
+
+ Skip.IfNot(Directory.Exists(DestinationParent));
+
+ MoveDirectoryInternal(DestinationParent);
+ }
+
+ internal void MoveDirectoryInternal(string? destinationParent = null)
{
- private readonly IFixture _fixture;
- private readonly ManagedFileSystem _sut;
-
- public ManagedFileSystemTests()
- {
- _fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
- _sut = _fixture.Create<ManagedFileSystem>();
- }
-
- [SkippableTheory]
- [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Beethoven/Misc/Moonlight Sonata.mp3")]
- [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Beethoven/Misc/Moonlight Sonata.mp3")]
- [InlineData("/Volumes/Library/Sample/Music/Playlists/", "Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Playlists/Beethoven/Misc/Moonlight Sonata.mp3")]
- [InlineData("/Volumes/Library/Sample/Music/Playlists/", "/mnt/Beethoven/Misc/Moonlight Sonata.mp3", "/mnt/Beethoven/Misc/Moonlight Sonata.mp3")]
- public void MakeAbsolutePathCorrectlyHandlesRelativeFilePathsOnUnixLike(
- string folderPath,
- string filePath,
- string expectedAbsolutePath)
- {
- Skip.If(OperatingSystem.IsWindows());
-
- var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath);
- Assert.Equal(expectedAbsolutePath, generatedPath);
- }
-
- [SkippableTheory]
- [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"..\Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Music\Beethoven\Misc\Moonlight Sonata.mp3")]
- [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"..\..\Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Beethoven\Misc\Moonlight Sonata.mp3")]
- [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Music\Playlists\Beethoven\Misc\Moonlight Sonata.mp3")]
- [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"D:\\Beethoven\Misc\Moonlight Sonata.mp3", @"D:\\Beethoven\Misc\Moonlight Sonata.mp3")]
- public void MakeAbsolutePathCorrectlyHandlesRelativeFilePathsOnWindows(
- string folderPath,
- string filePath,
- string expectedAbsolutePath)
- {
- Skip.If(!OperatingSystem.IsWindows());
-
- var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath);
-
- Assert.Equal(expectedAbsolutePath, generatedPath);
- }
-
- [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(OperatingSystem.IsWindows());
-
- string testFileDir = Path.Combine(Path.GetTempPath(), "jellyfin-test-data");
- string testFileName = Path.Combine(testFileDir, Path.GetRandomFileName() + "-danglingsym.link");
-
- Directory.CreateDirectory(testFileDir);
- Assert.Equal(0, symlink("thispathdoesntexist", testFileName));
- Assert.True(File.Exists(testFileName));
-
- var metadata = _sut.GetFileInfo(testFileName);
- Assert.False(metadata.Exists);
- }
-
- [SuppressMessage("Naming Rules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Have to")]
- [DllImport("libc", SetLastError = true, CharSet = CharSet.Ansi)]
- [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
- private static extern int symlink(string target, string linkpath);
+ const string TempFile0 = "tempfile0";
+ const string TempFile1 = "tempfile1";
+
+ destinationParent ??= Path.GetTempPath();
+
+ var sourceDir = Directory.CreateTempSubdirectory();
+ var destinationDir = Path.Join(destinationParent, Path.GetRandomFileName());
+ FileHelper.CreateEmpty(Path.Join(sourceDir.FullName, TempFile0));
+ FileHelper.CreateEmpty(Path.Join(sourceDir.FullName, TempFile1));
+
+ _sut.MoveDirectory(sourceDir.FullName, destinationDir);
+
+ Assert.True(Directory.Exists(destinationDir));
+ Assert.True(File.Exists(Path.Join(destinationDir, TempFile0)));
+ Assert.True(File.Exists(Path.Join(destinationDir, TempFile1)));
+ Assert.False(Directory.Exists(sourceDir.FullName));
+
+ Directory.Delete(destinationDir, true);
}
+
+ [SkippableTheory]
+ [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Beethoven/Misc/Moonlight Sonata.mp3")]
+ [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Beethoven/Misc/Moonlight Sonata.mp3")]
+ [InlineData("/Volumes/Library/Sample/Music/Playlists/", "Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Playlists/Beethoven/Misc/Moonlight Sonata.mp3")]
+ [InlineData("/Volumes/Library/Sample/Music/Playlists/", "/mnt/Beethoven/Misc/Moonlight Sonata.mp3", "/mnt/Beethoven/Misc/Moonlight Sonata.mp3")]
+ public void MakeAbsolutePathCorrectlyHandlesRelativeFilePathsOnUnixLike(
+ string folderPath,
+ string filePath,
+ string expectedAbsolutePath)
+ {
+ Skip.If(OperatingSystem.IsWindows());
+
+ var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath);
+ Assert.Equal(expectedAbsolutePath, generatedPath);
+ }
+
+ [SkippableTheory]
+ [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"..\Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Music\Beethoven\Misc\Moonlight Sonata.mp3")]
+ [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"..\..\Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Beethoven\Misc\Moonlight Sonata.mp3")]
+ [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"Beethoven\Misc\Moonlight Sonata.mp3", @"C:\Volumes\Library\Sample\Music\Playlists\Beethoven\Misc\Moonlight Sonata.mp3")]
+ [InlineData(@"C:\\Volumes\Library\Sample\Music\Playlists\", @"D:\\Beethoven\Misc\Moonlight Sonata.mp3", @"D:\\Beethoven\Misc\Moonlight Sonata.mp3")]
+ public void MakeAbsolutePathCorrectlyHandlesRelativeFilePathsOnWindows(
+ string folderPath,
+ string filePath,
+ string expectedAbsolutePath)
+ {
+ Skip.IfNot(OperatingSystem.IsWindows());
+
+ var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath);
+
+ Assert.Equal(expectedAbsolutePath, generatedPath);
+ }
+
+ [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(OperatingSystem.IsWindows());
+
+ string testFileDir = Path.Combine(Path.GetTempPath(), "jellyfin-test-data");
+ string testFileName = Path.Combine(testFileDir, Path.GetRandomFileName() + "-danglingsym.link");
+
+ Directory.CreateDirectory(testFileDir);
+ Assert.Equal(0, symlink("thispathdoesntexist", testFileName));
+ Assert.True(File.Exists(testFileName));
+
+ var metadata = _sut.GetFileInfo(testFileName);
+ Assert.False(metadata.Exists);
+ }
+
+ [SuppressMessage("Naming Rules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Have to")]
+ [DllImport("libc", SetLastError = true, CharSet = CharSet.Ansi)]
+ [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
+ private static extern int symlink(string target, string linkpath);
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
new file mode 100644
index 000000000..d677c9f09
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs
@@ -0,0 +1,30 @@
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Library;
+
+public class DotIgnoreIgnoreRuleTest
+{
+ [Fact]
+ public void Test()
+ {
+ var ignore = new Ignore.Ignore();
+ ignore.Add("SPs");
+ Assert.True(ignore.IsIgnored("f:/cd/sps/ffffff.mkv"));
+ Assert.True(ignore.IsIgnored("cd/sps/ffffff.mkv"));
+ Assert.True(ignore.IsIgnored("/cd/sps/ffffff.mkv"));
+ }
+
+ [Fact]
+ public void TestNegatePattern()
+ {
+ var ignore = new Ignore.Ignore();
+ ignore.Add("SPs");
+ ignore.Add("!thebestshot.mkv");
+ Assert.True(ignore.IsIgnored("f:/cd/sps/ffffff.mkv"));
+ Assert.True(ignore.IsIgnored("cd/sps/ffffff.mkv"));
+ Assert.True(ignore.IsIgnored("/cd/sps/ffffff.mkv"));
+ Assert.True(!ignore.IsIgnored("f:/cd/sps/thebestshot.mkv"));
+ Assert.True(!ignore.IsIgnored("cd/sps/thebestshot.mkv"));
+ Assert.True(!ignore.IsIgnored("/cd/sps/thebestshot.mkv"));
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
index 5babc9117..a7a1e5e81 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using BitFaster.Caching;
using Emby.Server.Implementations.Localization;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Configuration;
@@ -51,6 +52,30 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
Assert.Contains("ger", germany.ThreeLetterISOLanguageNames);
}
+ [Fact]
+ public async Task TryGetISO6392TFromB_Success()
+ {
+ var localizationManager = Setup(new ServerConfiguration
+ {
+ UICulture = "de-DE"
+ });
+ await localizationManager.LoadAll();
+
+ string? isoT;
+
+ // Translation ger -> deu
+ Assert.True(localizationManager.TryGetISO6392TFromB("ger", out isoT));
+ Assert.Equal("deu", isoT);
+
+ // chi -> zho
+ Assert.True(localizationManager.TryGetISO6392TFromB("chi", out isoT));
+ Assert.Equal("zho", isoT);
+
+ // eng is already ISO 639-2/T
+ Assert.False(localizationManager.TryGetISO6392TFromB("eng", out isoT));
+ Assert.Null(isoT);
+ }
+
[Theory]
[InlineData("de")]
[InlineData("deu")]
@@ -88,7 +113,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal));
Assert.NotNull(tvma);
- Assert.Equal(17, tvma!.Value);
+ Assert.Equal(17, tvma!.RatingScore!.Score);
}
[Fact]
@@ -105,47 +130,49 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal));
Assert.NotNull(fsk);
- Assert.Equal(12, fsk!.Value);
+ Assert.Equal(12, fsk!.RatingScore!.Score);
}
[Theory]
- [InlineData("CA-R", "CA", 18)]
- [InlineData("FSK-16", "DE", 16)]
- [InlineData("FSK-18", "DE", 18)]
- [InlineData("FSK-18", "US", 18)]
- [InlineData("TV-MA", "US", 17)]
- [InlineData("XXX", "asdf", 1000)]
- [InlineData("Germany: FSK-18", "DE", 18)]
- [InlineData("Rated : R", "US", 17)]
- [InlineData("Rated: R", "US", 17)]
- [InlineData("Rated R", "US", 17)]
- [InlineData(" PG-13 ", "US", 13)]
- public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int expectedLevel)
+ [InlineData("CA-R", "CA", 18, 1)]
+ [InlineData("FSK-16", "DE", 16, null)]
+ [InlineData("FSK-18", "DE", 18, null)]
+ [InlineData("FSK-18", "US", 18, null)]
+ [InlineData("TV-MA", "US", 17, 1)]
+ [InlineData("XXX", "asdf", 1000, null)]
+ [InlineData("Germany: FSK-18", "DE", 18, null)]
+ [InlineData("Rated : R", "US", 17, 0)]
+ [InlineData("Rated: R", "US", 17, 0)]
+ [InlineData("Rated R", "US", 17, 0)]
+ [InlineData(" PG-13 ", "US", 13, 0)]
+ public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int? expectedScore, int? expectedSubScore)
{
var localizationManager = Setup(new ServerConfiguration()
{
MetadataCountryCode = countryCode
});
await localizationManager.LoadAll();
- var level = localizationManager.GetRatingLevel(value);
- Assert.NotNull(level);
- Assert.Equal(expectedLevel, level!);
+ var score = localizationManager.GetRatingScore(value);
+ Assert.NotNull(score);
+ Assert.Equal(expectedScore, score.Score);
+ Assert.Equal(expectedSubScore, score.SubScore);
}
[Theory]
- [InlineData("0", 0)]
- [InlineData("1", 1)]
- [InlineData("6", 6)]
- [InlineData("12", 12)]
- [InlineData("42", 42)]
- [InlineData("9999", 9999)]
- public async Task GetRatingLevel_GivenValidAge_Success(string value, int expectedLevel)
+ [InlineData("0", 0, null)]
+ [InlineData("1", 1, null)]
+ [InlineData("6", 6, null)]
+ [InlineData("12", 12, null)]
+ [InlineData("42", 42, null)]
+ [InlineData("9999", 9999, null)]
+ public async Task GetRatingLevel_GivenValidAge_Success(string value, int? expectedScore, int? expectedSubScore)
{
var localizationManager = Setup(new ServerConfiguration { MetadataCountryCode = "nl" });
await localizationManager.LoadAll();
- var level = localizationManager.GetRatingLevel(value);
- Assert.NotNull(level);
- Assert.Equal(expectedLevel, level);
+ var score = localizationManager.GetRatingScore(value);
+ Assert.NotNull(score);
+ Assert.Equal(expectedScore, score.Score);
+ Assert.Equal(expectedSubScore, score.SubScore);
}
[Fact]
@@ -156,10 +183,10 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
UICulture = "de-DE"
});
await localizationManager.LoadAll();
- Assert.Null(localizationManager.GetRatingLevel("NR"));
- Assert.Null(localizationManager.GetRatingLevel("unrated"));
- Assert.Null(localizationManager.GetRatingLevel("Not Rated"));
- Assert.Null(localizationManager.GetRatingLevel("n/a"));
+ Assert.Null(localizationManager.GetRatingScore("NR"));
+ Assert.Null(localizationManager.GetRatingScore("unrated"));
+ Assert.Null(localizationManager.GetRatingScore("Not Rated"));
+ Assert.Null(localizationManager.GetRatingScore("n/a"));
}
[Theory]
@@ -173,7 +200,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
});
await localizationManager.LoadAll();
- Assert.Null(localizationManager.GetRatingLevel(value));
+ Assert.Null(localizationManager.GetRatingScore(value));
}
[Theory]
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Playlists/PlaylistManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Playlists/PlaylistManagerTests.cs
new file mode 100644
index 000000000..cc8ca720e
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Playlists/PlaylistManagerTests.cs
@@ -0,0 +1,40 @@
+using Emby.Server.Implementations.Playlists;
+using Xunit;
+
+namespace Jellyfin.Server.Implementations.Tests.Playlists;
+
+public class PlaylistManagerTests
+{
+ [Fact]
+ public void DetermineAdjustedIndexMoveToFirstPositionNoPriorInAllList()
+ {
+ var priorIndexAllChildren = 0;
+ var newIndex = 0;
+
+ var adjustedIndex = PlaylistManager.DetermineAdjustedIndex(priorIndexAllChildren, newIndex);
+
+ Assert.Equal(0, adjustedIndex);
+ }
+
+ [Fact]
+ public void DetermineAdjustedIndexPriorInMiddleOfAllList()
+ {
+ var priorIndexAllChildren = 2;
+ var newIndex = 0;
+
+ var adjustedIndex = PlaylistManager.DetermineAdjustedIndex(priorIndexAllChildren, newIndex);
+
+ Assert.Equal(1, adjustedIndex);
+ }
+
+ [Fact]
+ public void DetermineAdjustedIndexMoveMiddleOfPlaylist()
+ {
+ var priorIndexAllChildren = 2;
+ var newIndex = 1;
+
+ var adjustedIndex = PlaylistManager.DetermineAdjustedIndex(priorIndexAllChildren, newIndex);
+
+ Assert.Equal(3, adjustedIndex);
+ }
+}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs
index 934024826..b289c763b 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using AutoFixture;
using Emby.Server.Implementations.Library;
using Emby.Server.Implementations.Plugins;
+using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using Jellyfin.Extensions.Json.Converters;
using MediaBrowser.Common.Plugins;
@@ -85,7 +86,7 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins
var dllPath = Path.GetDirectoryName(Path.Combine(_pluginPath, dllFile))!;
Directory.CreateDirectory(dllPath);
- File.Create(Path.Combine(dllPath, filename));
+ FileHelper.CreateEmpty(Path.Combine(dllPath, filename));
var metafilePath = Path.Combine(_pluginPath, "meta.json");
File.WriteAllText(metafilePath, JsonSerializer.Serialize(manifest, _options));
@@ -141,7 +142,7 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins
foreach (var file in files)
{
- File.Create(Path.Combine(_pluginPath, file));
+ FileHelper.CreateEmpty(Path.Combine(_pluginPath, file));
}
var metafilePath = Path.Combine(_pluginPath, "meta.json");
diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs
index 36861294b..c8ae2a88a 100644
--- a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs
@@ -31,6 +31,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
var config = new StartupConfigurationDto()
{
+ ServerName = "NewServer",
UICulture = "NewCulture",
MetadataCountryCode = "be",
PreferredMetadataLanguage = "nl"
@@ -44,7 +45,8 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
Assert.Equal(MediaTypeNames.Application.Json, getResponse.Content.Headers.ContentType?.MediaType);
var newConfig = await getResponse.Content.ReadFromJsonAsync<StartupConfigurationDto>(_jsonOptions);
- Assert.Equal(config.UICulture, newConfig!.UICulture);
+ Assert.Equal(config.ServerName, newConfig!.ServerName);
+ Assert.Equal(config.UICulture, newConfig.UICulture);
Assert.Equal(config.MetadataCountryCode, newConfig.MetadataCountryCode);
Assert.Equal(config.PreferredMetadataLanguage, newConfig.PreferredMetadataLanguage);
}
@@ -88,9 +90,7 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
var newUser = await getResponse.Content.ReadFromJsonAsync<StartupUserDto>(_jsonOptions);
Assert.NotNull(newUser);
Assert.Equal(user.Name, newUser.Name);
- Assert.NotNull(newUser.Password);
- Assert.NotEmpty(newUser.Password);
- Assert.NotEqual(user.Password, newUser.Password);
+ Assert.Null(newUser.Password);
}
[Fact]
diff --git a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
index a7fec2960..725e359d7 100644
--- a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs
@@ -5,7 +5,9 @@ using System.IO;
using Emby.Server.Implementations;
using Jellyfin.Server.Extensions;
using Jellyfin.Server.Helpers;
+using Jellyfin.Server.ServerSetupApp;
using MediaBrowser.Common;
+using MediaBrowser.Common.Configuration;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
@@ -15,6 +17,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Serilog;
+using Serilog.Core;
using Serilog.Extensions.Logging;
namespace Jellyfin.Server.Integration.Tests
@@ -94,7 +97,8 @@ namespace Jellyfin.Server.Integration.Tests
.AddInMemoryCollection(ConfigurationOptions.DefaultConfiguration)
.AddEnvironmentVariables("JELLYFIN_")
.AddInMemoryCollection(commandLineOpts.ConvertToConfig());
- });
+ })
+ .ConfigureServices(e => e.AddSingleton<IStartupLogger, NullStartupLogger>().AddSingleton(e));
}
/// <inheritdoc/>
@@ -103,7 +107,11 @@ namespace Jellyfin.Server.Integration.Tests
var host = builder.Build();
var appHost = (TestAppHost)host.Services.GetRequiredService<IApplicationHost>();
appHost.ServiceProvider = host.Services;
+ var applicationPaths = appHost.ServiceProvider.GetRequiredService<IApplicationPaths>();
+ Program.ApplyStartupMigrationAsync((ServerApplicationPaths)applicationPaths, appHost.ServiceProvider.GetRequiredService<IConfiguration>()).GetAwaiter().GetResult();
+ Program.ApplyCoreMigrationsAsync(appHost.ServiceProvider, Migrations.Stages.JellyfinMigrationStageTypes.CoreInitialisation).GetAwaiter().GetResult();
appHost.InitializeServices(Mock.Of<IConfiguration>()).GetAwaiter().GetResult();
+ Program.ApplyCoreMigrationsAsync(appHost.ServiceProvider, Migrations.Stages.JellyfinMigrationStageTypes.AppInitialisation).GetAwaiter().GetResult();
host.Start();
appHost.RunStartupTasksAsync().GetAwaiter().GetResult();
@@ -123,5 +131,39 @@ namespace Jellyfin.Server.Integration.Tests
base.Dispose(disposing);
}
+
+ private sealed class NullStartupLogger : IStartupLogger
+ {
+ public IStartupLogger BeginGroup(FormattableString logEntry)
+ {
+ return this;
+ }
+
+ public IDisposable? BeginScope<TState>(TState state)
+ where TState : notnull
+ {
+ return NullLogger.Instance.BeginScope(state);
+ }
+
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ return NullLogger.Instance.IsEnabled(logLevel);
+ }
+
+ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
+ {
+ NullLogger.Instance.Log(logLevel, eventId, state, exception, formatter);
+ }
+
+ public Microsoft.Extensions.Logging.ILogger With(Microsoft.Extensions.Logging.ILogger logger)
+ {
+ return this;
+ }
+
+ IStartupLogger IStartupLogger.With(Microsoft.Extensions.Logging.ILogger logger)
+ {
+ return this;
+ }
+ }
}
}
diff --git a/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs
index 98195a294..62cdd25ae 100644
--- a/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs
+++ b/tests/Jellyfin.Server.Integration.Tests/OpenApiSpecTests.cs
@@ -1,6 +1,7 @@
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
using Xunit;
using Xunit.Abstractions;
@@ -33,7 +34,7 @@ namespace Jellyfin.Server.Integration.Tests
// Write out for publishing
string outputPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "openapi.json"));
_outputHelper.WriteLine("Writing OpenAPI Spec JSON to '{0}'.", outputPath);
- await using var fs = File.Create(outputPath);
+ await using var fs = AsyncFile.Create(outputPath);
await response.Content.CopyToAsync(fs);
}
}