aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs3
-rw-r--r--tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs3
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer-NoHevcRotation.json162
-rw-r--r--tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-aac-4000k-r180.json56
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkParseTests.cs38
-rw-r--r--tests/Jellyfin.Providers.Tests/ExternalId/MusicBrainzExternalUrlProviderTests.cs3
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs116
7 files changed, 380 insertions, 1 deletions
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index 3369af0e84..198cdaa4fc 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -105,10 +105,12 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
var audio1 = res.MediaStreams[1];
Assert.Equal("eac3", audio1.Codec);
+ Assert.True(audio1.IsOriginal);
Assert.Equal(AudioSpatialFormat.DolbyAtmos, audio1.AudioSpatialFormat);
var audio2 = res.MediaStreams[2];
Assert.Equal("dts", audio2.Codec);
+ Assert.False(audio2.IsOriginal);
Assert.Equal(AudioSpatialFormat.DTSX, audio2.AudioSpatialFormat);
Assert.Empty(res.Chapters);
@@ -156,6 +158,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.Equal("aac", res.MediaStreams[1].Codec);
Assert.Equal(7, res.MediaStreams[1].Channels);
Assert.True(res.MediaStreams[1].IsDefault);
+ Assert.False(res.MediaStreams[1].IsOriginal);
Assert.Equal("eng", res.MediaStreams[1].Language);
Assert.Equal("Surround 6.1", res.MediaStreams[1].Title);
diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
index 8269ae58cd..0b103debad 100644
--- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
+++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs
@@ -171,6 +171,9 @@ namespace Jellyfin.Model.Tests
[InlineData("AndroidTVExoPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
[InlineData("AndroidTVExoPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
[InlineData("AndroidTVExoPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // Full transcode because profile only has ts which does not allow vp9
+ [InlineData("AndroidTVExoPlayer", "mp4-hevc-aac-4000k-r180", PlayMethod.DirectPlay)] // #13712
+ // AndroidTV NoHevcRotation
+ [InlineData("AndroidTVExoPlayer-NoHevcRotation", "mp4-hevc-aac-4000k-r180", PlayMethod.Transcode, TranscodeReason.VideoRotationNotSupported, "Transcode")] // #13712
// Tizen 3 Stereo
[InlineData("Tizen3-stereo", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)]
[InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)]
diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer-NoHevcRotation.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer-NoHevcRotation.json
new file mode 100644
index 0000000000..341638bc52
--- /dev/null
+++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-AndroidTVExoPlayer-NoHevcRotation.json
@@ -0,0 +1,162 @@
+{
+ "Name": "Jellyfin AndroidTV-ExoPlayer",
+ "EnableAlbumArtInDidl": false,
+ "EnableSingleAlbumArtLimit": false,
+ "EnableSingleSubtitleLimit": false,
+ "SupportedMediaTypes": "Audio,Photo,Video",
+ "MaxAlbumArtWidth": 0,
+ "MaxAlbumArtHeight": 0,
+ "MaxStreamingBitrate": 120000000,
+ "MaxStaticBitrate": 100000000,
+ "MusicStreamingTranscodingBitrate": 192000,
+ "TimelineOffsetSeconds": 0,
+ "RequiresPlainVideoItems": false,
+ "RequiresPlainFolders": false,
+ "EnableMSMediaReceiverRegistrar": false,
+ "IgnoreTranscodeByteRangeRequests": false,
+ "DirectPlayProfiles": [
+ {
+ "Container": "m4v,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,mp4,webm",
+ "AudioCodec": "aac,mp3,mp2,aac_latm,alac,ac3,eac3,dca,dts,mlp,truehd,pcm_alaw,pcm_mulaw",
+ "VideoCodec": "h264,hevc,vp8,vp9,mpeg,mpeg2video",
+ "Type": "Video",
+ "$type": "DirectPlayProfile"
+ },
+ {
+ "Container": "aac,mp3,mp2,aac_latm,alac,ac3,eac3,dca,dts,mlp,truehd,pcm_alaw,pcm_mulaw,,pa,flac,wav,wma,ogg,oga,webma,ape,opus",
+ "Type": "Audio",
+ "$type": "DirectPlayProfile"
+ },
+ {
+ "Container": "jpg,jpeg,png,gif,web",
+ "Type": "Photo",
+ "$type": "DirectPlayProfile"
+ }
+ ],
+ "CodecProfiles": [
+ {
+ "Type": "Video",
+ "Conditions": [
+ {
+ "Condition": "EqualsAny",
+ "Property": "VideoProfile",
+ "Value": "main|main 10",
+ "IsRequired": false,
+ "$type": "ProfileCondition"
+ },
+ {
+ "Condition": "LessThanEqual",
+ "Property": "VideoLevel",
+ "Value": "51",
+ "IsRequired": false,
+ "$type": "ProfileCondition"
+ },
+ {
+ "Condition": "Equals",
+ "Property": "VideoRotation",
+ "Value": "0",
+ "IsRequired": false,
+ "$type": "ProfileCondition"
+ }
+ ],
+ "Codec": "hevc",
+ "$type": "CodecProfile"
+ }
+ ],
+ "TranscodingProfiles": [
+ {
+ "Container": "ts",
+ "Type": "Video",
+ "VideoCodec": "h264",
+ "AudioCodec": "aac,mp3",
+ "Protocol": "hls",
+ "EstimateContentLength": false,
+ "EnableMpegtsM2TsMode": false,
+ "TranscodeSeekInfo": "Auto",
+ "CopyTimestamps": false,
+ "Context": "Streaming",
+ "EnableSubtitlesInManifest": false,
+ "MinSegments": 0,
+ "SegmentLength": 0,
+ "$type": "TranscodingProfile"
+ },
+ {
+ "Container": "mp3",
+ "Type": "Audio",
+ "AudioCodec": "mp3",
+ "Protocol": "http",
+ "EstimateContentLength": false,
+ "EnableMpegtsM2TsMode": false,
+ "TranscodeSeekInfo": "Auto",
+ "CopyTimestamps": false,
+ "Context": "Streaming",
+ "EnableSubtitlesInManifest": false,
+ "MinSegments": 0,
+ "SegmentLength": 0,
+ "$type": "TranscodingProfile"
+ }
+ ],
+ "SubtitleProfiles": [
+ {
+ "Format": "srt",
+ "Method": "Embed",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "srt",
+ "Method": "External",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "subrip",
+ "Method": "Embed",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "subrip",
+ "Method": "External",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "ass",
+ "Method": "Encode",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "ssa",
+ "Method": "Encode",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "pgs",
+ "Method": "Encode",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "pgssub",
+ "Method": "Encode",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "dvdsub",
+ "Method": "Encode",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "vtt",
+ "Method": "Embed",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "sub",
+ "Method": "Embed",
+ "$type": "SubtitleProfile"
+ },
+ {
+ "Format": "idx",
+ "Method": "Embed",
+ "$type": "SubtitleProfile"
+ }
+ ],
+ "$type": "DeviceProfile"
+}
diff --git a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-aac-4000k-r180.json b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-aac-4000k-r180.json
new file mode 100644
index 0000000000..393b10171d
--- /dev/null
+++ b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-hevc-aac-4000k-r180.json
@@ -0,0 +1,56 @@
+{
+ "Id": "b7a9e2d4c815f36b0d9241a7e58c3f42",
+ "Path": "/Media/MyVideo-1080p.mp4",
+ "Container": "mov,mp4,m4a,3gp,3g2,mj2",
+ "Size": 1421636271,
+ "Name": "MyVideo-1080p",
+ "ETag": "d8e2a1b5c4f907e8a1d2b3c4e5f6a7b8",
+ "RunTimeTicks": 25801230336,
+ "SupportsTranscoding": true,
+ "SupportsDirectStream": true,
+ "SupportsDirectPlay": true,
+ "SupportsProbing": true,
+ "MediaStreams": [
+ {
+ "Codec": "hevc",
+ "CodecTag": "hvc1",
+ "Language": "eng",
+ "TimeBase": "1/11988",
+ "VideoRange": "SDR",
+ "DisplayTitle": "1080p HEVC SDR",
+ "NalLengthSize": "0",
+ "BitRate": 4014613,
+ "BitDepth": 8,
+ "RefFrames": 1,
+ "IsDefault": true,
+ "Height": 1080,
+ "Width": 1920,
+ "AverageFrameRate": 23.976,
+ "RealFrameRate": 23.976,
+ "Profile": "Main",
+ "Type": 1,
+ "AspectRatio": "16:9",
+ "PixelFormat": "yuv420p",
+ "Level": 50,
+ "Rotation": 180
+ },
+ {
+ "Codec": "aac",
+ "CodecTag": "mp4a",
+ "Language": "eng",
+ "TimeBase": "1/48000",
+ "DisplayTitle": "En - AAC - Stereo - Default",
+ "ChannelLayout": "stereo",
+ "BitRate": 125427,
+ "Channels": 2,
+ "SampleRate": 48000,
+ "IsDefault": true,
+ "Profile": "LC",
+ "Index": 1,
+ "Score": 203
+ }
+ ],
+ "Bitrate": 4331578,
+ "DefaultAudioStreamIndex": 1,
+ "DefaultSubtitleStreamIndex": 2
+}
diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
index b63009d6a5..66eec077dc 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
@@ -7,6 +7,7 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
@@ -94,10 +95,47 @@ namespace Jellyfin.Networking.Tests
[InlineData("256.128.0.0.0.1")]
[InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")]
[InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")]
+ [InlineData("fd23:184f:2029:0100/56")]
public static void TryParseInvalidIPStringsFalse(string address)
=> Assert.False(NetworkUtils.TryParseToSubnet(address, out _));
/// <summary>
+ /// Verifies that <see cref="NetworkUtils.TryParseToSubnets"/> emits a targeted warning
+ /// for IPv6 prefix-only notation and a generic warning for other malformed entries.
+ /// </summary>
+ [Fact]
+ public static void TryParseToSubnets_InvalidEntries_LogsWarnings()
+ {
+ var logger = new Mock<ILogger>();
+
+ var values = new[] { "10.0.0.0/8", "fd23:184f:2029:0100/56", "not-an-address" };
+ Assert.True(NetworkUtils.TryParseToSubnets(values, out var result, false, logger.Object));
+ Assert.NotNull(result);
+ Assert.Single(result);
+
+ // IPv6 prefix-only notation should produce a specific, actionable warning.
+ logger.Verify(
+ l => l.Log(
+ LogLevel.Warning,
+ It.IsAny<EventId>(),
+ It.Is<It.IsAnyType>((state, _) => state.ToString()!.Contains("IPv6 prefix-only", StringComparison.Ordinal)
+ && state.ToString()!.Contains("fd23:184f:2029:0100/56", StringComparison.Ordinal)),
+ It.IsAny<Exception>(),
+ It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
+ Times.Once);
+
+ // Other malformed entries should still produce a generic warning.
+ logger.Verify(
+ l => l.Log(
+ LogLevel.Warning,
+ It.IsAny<EventId>(),
+ It.Is<It.IsAnyType>((state, _) => state.ToString()!.Contains("not-an-address", StringComparison.Ordinal)),
+ It.IsAny<Exception>(),
+ It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
+ Times.Once);
+ }
+
+ /// <summary>
/// Checks if IPv4 address is within a defined subnet.
/// </summary>
/// <param name="netMask">Network mask.</param>
diff --git a/tests/Jellyfin.Providers.Tests/ExternalId/MusicBrainzExternalUrlProviderTests.cs b/tests/Jellyfin.Providers.Tests/ExternalId/MusicBrainzExternalUrlProviderTests.cs
index 920529bf73..d35211f387 100644
--- a/tests/Jellyfin.Providers.Tests/ExternalId/MusicBrainzExternalUrlProviderTests.cs
+++ b/tests/Jellyfin.Providers.Tests/ExternalId/MusicBrainzExternalUrlProviderTests.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Plugins.MusicBrainz;
using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
+using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
@@ -41,7 +42,7 @@ namespace Jellyfin.Providers.Tests.ExternalId
appHostMock.Setup(h => h.ApplicationVersionString).Returns("1.0.0");
appHostMock.Setup(h => h.ApplicationUserAgentAddress).Returns("localhost");
- _ = new Plugin(appPathsMock.Object, xmlSerializerMock.Object, appHostMock.Object);
+ _ = new Plugin(appPathsMock.Object, xmlSerializerMock.Object, appHostMock.Object, NullLogger<Plugin>.Instance);
}
public void Dispose()
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs
index 8ed3d8b944..facdb2bc2e 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaSourceManagerTests.cs
@@ -1,9 +1,18 @@
+using System;
using AutoFixture;
using AutoFixture.AutoMoq;
+using Castle.Components.DictionaryAdapter;
using Emby.Server.Implementations.IO;
using Emby.Server.Implementations.Library;
+using Jellyfin.Database.Implementations.Entities;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
+using Moq;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.Library
@@ -11,12 +20,28 @@ namespace Jellyfin.Server.Implementations.Tests.Library
public class MediaSourceManagerTests
{
private readonly MediaSourceManager _mediaSourceManager;
+ private readonly Mock<IUserDataManager> _mockUserDataManager;
+ private readonly Mock<ILocalizationManager> _mockLocalizationManager;
+ private Video _item;
+ private User _user;
public MediaSourceManagerTests()
{
IFixture fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
fixture.Inject<IFileSystem>(fixture.Create<ManagedFileSystem>());
+
+ _mockUserDataManager = fixture.Freeze<Mock<IUserDataManager>>();
+ _mockUserDataManager.Setup(m => m.GetUserData(It.IsAny<User>(), It.IsAny<BaseItem>())).Returns(new UserItemData() { Key = "key" });
+
+ _mockLocalizationManager = fixture.Create<Mock<ILocalizationManager>>();
+ _mockLocalizationManager.Setup(m => m.FindLanguageInfo(It.IsAny<string>())).Returns((string s) => string.IsNullOrEmpty(s) ? null : new CultureDto(s, s, s, new EditableList<string> { s }));
+ fixture.Inject(_mockLocalizationManager.Object);
+
_mediaSourceManager = fixture.Create<MediaSourceManager>();
+
+ _item = new Video { Id = Guid.NewGuid(), OwnerId = Guid.Empty, ParentId = Guid.Empty };
+
+ _user = fixture.Create<User>();
}
[Theory]
@@ -28,5 +53,96 @@ namespace Jellyfin.Server.Implementations.Tests.Library
[InlineData("rtsp://media.example.com:554/twister/audiotrack", MediaProtocol.Rtsp)]
public void GetPathProtocol_ValidArg_Correct(string path, MediaProtocol expected)
=> Assert.Equal(expected, _mediaSourceManager.GetPathProtocol(path));
+
+ [Theory]
+ [InlineData(5, "eng", "eng", false, true)]
+ [InlineData(5, "eng", "eng", true, true)]
+ [InlineData(2, "ger", "eng", false, true)]
+ [InlineData(2, "ger", "eng", true, true)]
+ [InlineData(1, "fre", "eng", false, true)]
+ [InlineData(2, "fre", "eng", true, true)]
+ [InlineData(5, "OriginalLanguage", "eng", false, false)]
+ [InlineData(4, "OriginalLanguage", "eng", false, true)]
+ [InlineData(5, "OriginalLanguage", "eng", true, false)]
+ [InlineData(5, "OriginalLanguage", "eng", true, true)]
+ [InlineData(2, "OriginalLanguage", "jpn", true, true)]
+ [InlineData(2, "OriginalLanguage", "jpn", false, true)]
+ [InlineData(2, "OriginalLanguage", "jpn,eng", false, true)]
+ [InlineData(4, "OriginalLanguage", null, false, true)]
+ [InlineData(2, "OriginalLanguage", null, true, true)]
+ [InlineData(4, "OriginalLanguage", "", false, true)]
+ [InlineData(2, "OriginalLanguage", "", false, false)]
+ [InlineData(2, "OriginalLanguage", "ger", false, true)]
+ [InlineData(2, "OriginalLanguage", "ger", false, false)]
+ [InlineData(1, "OriginalLanguage", "fre", false, false)]
+ [InlineData(2, "OriginalLanguage", "fre", true, true)]
+ [InlineData(2, "OriginalLanguage", "fre", true, false)]
+ public void SetDefaultAudioStreamIndex_Index_Correct(
+ int expectedIndex,
+ string prefferedLanguage,
+ string? originalLanguage,
+ bool playDefault,
+ bool originalExist)
+ {
+ var streams = new MediaStream[]
+ {
+ new()
+ {
+ Index = 0,
+ Type = MediaStreamType.Video,
+ IsDefault = true
+ },
+ new()
+ {
+ Index = 1,
+ Type = MediaStreamType.Audio,
+ Language = "fre",
+ IsDefault = false,
+ IsOriginal = false
+ },
+ new()
+ {
+ Index = 2,
+ Type = MediaStreamType.Audio,
+ Language = "jpn",
+ IsDefault = true,
+ IsOriginal = false
+ },
+ new()
+ {
+ Index = 3,
+ Type = MediaStreamType.Audio,
+ Language = "eng",
+ IsDefault = false,
+ IsOriginal = false
+ },
+ new()
+ {
+ Index = 4,
+ Type = MediaStreamType.Audio,
+ Language = "eng",
+ IsDefault = false,
+ IsOriginal = originalExist,
+ },
+ new()
+ {
+ Index = 5,
+ Type = MediaStreamType.Audio,
+ Language = "eng",
+ IsDefault = true,
+ IsOriginal = false,
+ }
+ };
+ var mediaInfo = new MediaSourceInfo
+ {
+ MediaStreams = streams
+ };
+ _user.AudioLanguagePreference = prefferedLanguage;
+ _user.PlayDefaultAudioTrack = playDefault;
+ _item.OriginalLanguage = originalLanguage;
+
+ _mediaSourceManager.SetDefaultAudioAndSubtitleStreamIndices(_item, mediaInfo, _user);
+ Assert.Equal(expectedIndex, mediaInfo.DefaultAudioStreamIndex);
+ }
}
}