diff options
Diffstat (limited to 'tests')
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); + } } } |
