repo creation with initial code after cloning public repo
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
using System.Linq;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library.Resolvers.Audio;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library;
|
||||
|
||||
public class AudioResolverTests
|
||||
{
|
||||
private static readonly NamingOptions _namingOptions = new();
|
||||
|
||||
[Theory]
|
||||
[InlineData("words.mp3")] // single non-tagged file
|
||||
[InlineData("chapter 01.mp3")]
|
||||
[InlineData("part 1.mp3")]
|
||||
[InlineData("chapter 01.mp3", "non-media.txt")]
|
||||
[InlineData("title.mp3", "title.epub")]
|
||||
[InlineData("01.mp3", "subdirectory/")] // single media file with sub-directory - note that this will hide any contents in the subdirectory
|
||||
public void Resolve_AudiobookDirectory_SingleResult(params string[] children)
|
||||
{
|
||||
var resolved = TestResolveChildren("/parent/title", children);
|
||||
Assert.NotNull(resolved);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
/* Results that can't be displayed as an audio book. */
|
||||
[InlineData] // no contents
|
||||
[InlineData("subdirectory/")]
|
||||
[InlineData("non-media.txt")]
|
||||
/* Names don't indicate parts of a single book. */
|
||||
[InlineData("Name.mp3", "Another Name.mp3")]
|
||||
/* Results that are an audio book but not currently navigable as such (multiple chapters and/or parts). */
|
||||
[InlineData("01.mp3", "02.mp3")]
|
||||
[InlineData("chapter 01.mp3", "chapter 02.mp3")]
|
||||
[InlineData("part 1.mp3", "part 2.mp3")]
|
||||
[InlineData("chapter 01 part 01.mp3", "chapter 01 part 02.mp3")]
|
||||
/* Mismatched chapters, parts, and named files. */
|
||||
[InlineData("chapter 01.mp3", "part 2.mp3")]
|
||||
[InlineData("book title.mp3", "chapter name.mp3")] // "book title" resolves as alternate version of book based on directory name
|
||||
[InlineData("01 Content.mp3", "01 Credits.mp3")] // resolves as alternate versions of chapter 1
|
||||
[InlineData("Chapter Name.mp3", "Part 1.mp3")]
|
||||
public void Resolve_AudiobookDirectory_NoResult(params string[] children)
|
||||
{
|
||||
var resolved = TestResolveChildren("/parent/book title", children);
|
||||
Assert.Null(resolved);
|
||||
}
|
||||
|
||||
private Audio? TestResolveChildren(string parent, string[] children)
|
||||
{
|
||||
var childrenMetadata = children.Select(name => new FileSystemMetadata
|
||||
{
|
||||
FullName = parent + "/" + name,
|
||||
IsDirectory = name.EndsWith('/')
|
||||
}).ToArray();
|
||||
|
||||
var resolver = new AudioResolver(_namingOptions);
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
null,
|
||||
Mock.Of<ILibraryManager>())
|
||||
{
|
||||
CollectionType = CollectionType.books,
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = parent,
|
||||
IsDirectory = true
|
||||
},
|
||||
FileSystemChildren = childrenMetadata
|
||||
};
|
||||
|
||||
return resolver.Resolve(itemResolveArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.Video;
|
||||
using Emby.Server.Implementations.Library;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library;
|
||||
|
||||
public class CoreResolutionIgnoreRuleTest
|
||||
{
|
||||
private readonly CoreResolutionIgnoreRule _rule;
|
||||
private readonly NamingOptions _namingOptions;
|
||||
private readonly Mock<IServerApplicationPaths> _appPathsMock;
|
||||
|
||||
public CoreResolutionIgnoreRuleTest()
|
||||
{
|
||||
_namingOptions = new NamingOptions();
|
||||
|
||||
_namingOptions.AllExtrasTypesFolderNames.TryAdd("extras", ExtraType.Trailer);
|
||||
|
||||
_appPathsMock = new Mock<IServerApplicationPaths>();
|
||||
_appPathsMock.SetupGet(x => x.RootFolderPath).Returns("/server/root");
|
||||
|
||||
_rule = new CoreResolutionIgnoreRule(_namingOptions, _appPathsMock.Object);
|
||||
}
|
||||
|
||||
private FileSystemMetadata MakeFileSystemMetadata(string fullName, bool isDirectory = false)
|
||||
=> new FileSystemMetadata { FullName = fullName, Name = Path.GetFileName(fullName), IsDirectory = isDirectory };
|
||||
|
||||
private BaseItem MakeParent(string name = "Parent", bool isTopParent = false, Type? type = null)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
Type t when t == typeof(Folder) => CreateMock<Folder>(name, isTopParent).Object,
|
||||
Type t when t == typeof(AggregateFolder) => CreateMock<AggregateFolder>(name, isTopParent).Object,
|
||||
Type t when t == typeof(UserRootFolder) => CreateMock<UserRootFolder>(name, isTopParent).Object,
|
||||
_ => CreateMock<BaseItem>(name, isTopParent).Object
|
||||
};
|
||||
}
|
||||
|
||||
private static Mock<T> CreateMock<T>(string name, bool isTopParent)
|
||||
where T : BaseItem
|
||||
{
|
||||
var mock = new Mock<T>();
|
||||
mock.SetupGet(p => p.Name).Returns(name);
|
||||
mock.SetupGet(p => p.IsTopParent).Returns(isTopParent);
|
||||
return mock;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestApplicationFolder()
|
||||
{
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
MakeFileSystemMetadata("/server/root/extras", isDirectory: true),
|
||||
null));
|
||||
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
MakeFileSystemMetadata("/server/root/small.jpg"),
|
||||
null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestTopLevelDirectory()
|
||||
{
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
MakeFileSystemMetadata("Series/Extras", true),
|
||||
MakeParent(type: typeof(AggregateFolder))));
|
||||
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
MakeFileSystemMetadata("Series/Extras/Extras", true),
|
||||
MakeParent(isTopParent: true)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestIgnorePatterns()
|
||||
{
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
MakeFileSystemMetadata("/Media/big.jpg"),
|
||||
MakeParent()));
|
||||
|
||||
Assert.True(_rule.ShouldIgnore(
|
||||
MakeFileSystemMetadata("/Media/small.jpg"),
|
||||
MakeParent()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestExtrasTypesFolderNames()
|
||||
{
|
||||
FileSystemMetadata fileSystemMetadata = MakeFileSystemMetadata("/Movies/Up/extras", true);
|
||||
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
fileSystemMetadata,
|
||||
MakeParent(type: typeof(AggregateFolder))));
|
||||
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
fileSystemMetadata,
|
||||
MakeParent(type: typeof(UserRootFolder))));
|
||||
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
fileSystemMetadata,
|
||||
null));
|
||||
|
||||
Assert.True(_rule.ShouldIgnore(
|
||||
fileSystemMetadata,
|
||||
MakeParent()));
|
||||
|
||||
Assert.True(_rule.ShouldIgnore(
|
||||
fileSystemMetadata,
|
||||
MakeParent(type: typeof(Folder))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestThemeSong()
|
||||
{
|
||||
Assert.False(_rule.ShouldIgnore(
|
||||
MakeFileSystemMetadata("/Movies/Up/intro.mp3"),
|
||||
MakeParent()));
|
||||
|
||||
Assert.True(_rule.ShouldIgnore(
|
||||
MakeFileSystemMetadata("/Movies/Up/theme.mp3"),
|
||||
MakeParent()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using Emby.Server.Implementations.Library;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library;
|
||||
|
||||
public class DotIgnoreIgnoreRuleTest
|
||||
{
|
||||
private static readonly string[] _rule1 = ["SPs"];
|
||||
private static readonly string[] _rule2 = ["SPs", "!thebestshot.mkv"];
|
||||
private static readonly string[] _rule3 = ["*.txt", @"{\colortbl;\red255\green255\blue255;}", "videos/", @"\invalid\escape\sequence", "*.mkv"];
|
||||
private static readonly string[] _rule4 = [@"{\colortbl;\red255\green255\blue255;}", @"\invalid\escape\sequence"];
|
||||
|
||||
public static TheoryData<string[], string, bool, bool> CheckIgnoreRulesTestData =>
|
||||
new()
|
||||
{
|
||||
// Basic pattern matching
|
||||
{ _rule1, "f:/cd/sps/ffffff.mkv", false, true },
|
||||
{ _rule1, "cd/sps/ffffff.mkv", false, true },
|
||||
{ _rule1, "/cd/sps/ffffff.mkv", false, true },
|
||||
|
||||
// Negate pattern
|
||||
{ _rule2, "f:/cd/sps/ffffff.mkv", false, true },
|
||||
{ _rule2, "cd/sps/ffffff.mkv", false, true },
|
||||
{ _rule2, "/cd/sps/ffffff.mkv", false, true },
|
||||
{ _rule2, "f:/cd/sps/thebestshot.mkv", false, false },
|
||||
{ _rule2, "cd/sps/thebestshot.mkv", false, false },
|
||||
{ _rule2, "/cd/sps/thebestshot.mkv", false, false },
|
||||
|
||||
// Mixed valid and invalid patterns - skips invalid, applies valid
|
||||
{ _rule3, "test.txt", false, true },
|
||||
{ _rule3, "videos/movie.mp4", false, true },
|
||||
{ _rule3, "movie.mkv", false, true },
|
||||
{ _rule3, "test.mp3", false, false },
|
||||
|
||||
// Only invalid patterns - falls back to ignore all
|
||||
{ _rule4, "any-file.txt", false, true },
|
||||
{ _rule4, "any/path/to/file.mkv", false, true },
|
||||
};
|
||||
|
||||
public static TheoryData<string[], string, bool, bool> WindowsPathNormalizationTestData =>
|
||||
new()
|
||||
{
|
||||
// Windows paths with backslashes - should match when normalizePath is true
|
||||
{ _rule1, @"C:\cd\sps\ffffff.mkv", false, true },
|
||||
{ _rule1, @"D:\media\sps\movie.mkv", false, true },
|
||||
{ _rule1, @"\\server\share\sps\file.mkv", false, true },
|
||||
|
||||
// Negate pattern with Windows paths
|
||||
{ _rule2, @"C:\cd\sps\ffffff.mkv", false, true },
|
||||
{ _rule2, @"C:\cd\sps\thebestshot.mkv", false, false },
|
||||
|
||||
// Directory matching with Windows paths
|
||||
{ _rule3, @"C:\videos\movie.mp4", false, true },
|
||||
{ _rule3, @"D:\documents\test.txt", false, true },
|
||||
{ _rule3, @"E:\music\song.mp3", false, false },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(CheckIgnoreRulesTestData))]
|
||||
public void CheckIgnoreRules_ReturnsExpectedResult(string[] rules, string path, bool isDirectory, bool expectedIgnored)
|
||||
{
|
||||
Assert.Equal(expectedIgnored, DotIgnoreIgnoreRule.CheckIgnoreRules(path, rules, isDirectory));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(WindowsPathNormalizationTestData))]
|
||||
public void CheckIgnoreRules_WithWindowsPaths_NormalizesBackslashes(string[] rules, string path, bool isDirectory, bool expectedIgnored)
|
||||
{
|
||||
// With normalizePath=true, backslashes should be converted to forward slashes
|
||||
Assert.Equal(expectedIgnored, DotIgnoreIgnoreRule.CheckIgnoreRules(path, rules, isDirectory, normalizePath: true));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"C:\cd\sps\ffffff.mkv")]
|
||||
[InlineData(@"D:\media\sps\movie.mkv")]
|
||||
public void CheckIgnoreRules_WithWindowsPaths_WithoutNormalization_DoesNotMatch(string path)
|
||||
{
|
||||
// Without normalization, Windows paths with backslashes won't match patterns expecting forward slashes
|
||||
Assert.False(DotIgnoreIgnoreRule.CheckIgnoreRules(path, _rule1, isDirectory: false, normalizePath: false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library.Resolvers.TV;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
public class EpisodeResolverTest
|
||||
{
|
||||
private static readonly NamingOptions _namingOptions = new();
|
||||
|
||||
[Fact]
|
||||
public void Resolve_GivenVideoInExtrasFolder_DoesNotResolveToEpisode()
|
||||
{
|
||||
var parent = new Folder { Name = "extras" };
|
||||
|
||||
var episodeResolver = new EpisodeResolver(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions, Mock.Of<IDirectoryService>());
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = parent,
|
||||
CollectionType = CollectionType.tvshows,
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "All My Children/Season 01/Extras/All My Children S01E01 - Behind The Scenes.mkv"
|
||||
}
|
||||
};
|
||||
|
||||
Assert.Null(episodeResolver.Resolve(itemResolveArgs));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolve_GivenVideoInExtrasSeriesFolder_ResolvesToEpisode()
|
||||
{
|
||||
var series = new Series { Name = "Extras" };
|
||||
|
||||
// Have to create a mock because of moq proxies not being castable to a concrete implementation
|
||||
// https://github.com/jellyfin/jellyfin/blob/ab0cff8556403e123642dc9717ba778329554634/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs#L48
|
||||
var episodeResolver = new EpisodeResolverMock(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions, Mock.Of<IDirectoryService>());
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = series,
|
||||
CollectionType = CollectionType.tvshows,
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "Extras/Extras S01E01.mkv"
|
||||
}
|
||||
};
|
||||
Assert.NotNull(episodeResolver.Resolve(itemResolveArgs));
|
||||
}
|
||||
|
||||
private sealed class EpisodeResolverMock : EpisodeResolver
|
||||
{
|
||||
public EpisodeResolverMock(ILogger<EpisodeResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService) : base(logger, namingOptions, directoryService)
|
||||
{
|
||||
}
|
||||
|
||||
protected override TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName) => new();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Emby.Server.Implementations.Library;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
public class IgnorePatternsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("/media/small.jpg", true)]
|
||||
[InlineData("/media/albumart.jpg", true)]
|
||||
[InlineData("/media/movie.sample.mp4", true)]
|
||||
[InlineData("/media/movie/sample.mp4", true)]
|
||||
[InlineData("/media/movie/sample/movie.mp4", true)]
|
||||
[InlineData("/foo/sample/bar/baz.mkv", false)]
|
||||
[InlineData("/media/movies/the sample/the sample.mkv", false)]
|
||||
[InlineData("/media/movies/sampler.mkv", false)]
|
||||
[InlineData("/media/movies/#Recycle/test.txt", true)]
|
||||
[InlineData("/media/movies/#recycle/", true)]
|
||||
[InlineData("/media/movies/#recycle", true)]
|
||||
[InlineData("thumbs.db", true)]
|
||||
[InlineData(@"C:\media\movies\movie.avi", false)]
|
||||
[InlineData("/media/.hiddendir/file.mp4", false)]
|
||||
[InlineData("/media/dir/.hiddenfile.mp4", true)]
|
||||
[InlineData("/media/dir/._macjunk.mp4", true)]
|
||||
[InlineData("/volume1/video/Series/@eaDir", true)]
|
||||
[InlineData("/volume1/video/Series/@eaDir/file.txt", true)]
|
||||
[InlineData("/directory/@Recycle", true)]
|
||||
[InlineData("/directory/@Recycle/file.mp3", true)]
|
||||
[InlineData("/media/movies/.@__thumb", true)]
|
||||
[InlineData("/media/movies/.@__thumb/foo-bar-thumbnail.png", true)]
|
||||
[InlineData("/media/music/Foo B.A.R./epic.flac", false)]
|
||||
[InlineData("/media/music/Foo B.A.R", false)]
|
||||
[InlineData("/media/music/Foo B.A.R.", false)]
|
||||
[InlineData("/movies/.zfs/snapshot/AutoM-2023-09", true)]
|
||||
public void PathIgnored(string path, bool expected)
|
||||
{
|
||||
Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library.Resolvers.Audio;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Resolvers;
|
||||
using MediaBrowser.Controller.Sorting;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library.LibraryManager;
|
||||
|
||||
public class FindExtrasTests
|
||||
{
|
||||
private readonly Emby.Server.Implementations.Library.LibraryManager _libraryManager;
|
||||
private readonly Mock<IFileSystem> _fileSystemMock;
|
||||
|
||||
public FindExtrasTests()
|
||||
{
|
||||
var fixture = new Fixture().Customize(new AutoMoqCustomization());
|
||||
fixture.Register(() => new NamingOptions());
|
||||
var configMock = fixture.Freeze<Mock<IServerConfigurationManager>>();
|
||||
configMock.Setup(c => c.ApplicationPaths.ProgramDataPath).Returns("/data");
|
||||
var itemRepository = fixture.Freeze<Mock<IItemRepository>>();
|
||||
itemRepository.Setup(i => i.RetrieveItem(It.IsAny<Guid>())).Returns<BaseItem>(null);
|
||||
_fileSystemMock = fixture.Freeze<Mock<IFileSystem>>();
|
||||
_fileSystemMock.Setup(f => f.GetFileInfo(It.IsAny<string>())).Returns<string>(path => new FileSystemMetadata { FullName = path });
|
||||
_libraryManager = fixture.Build<Emby.Server.Implementations.Library.LibraryManager>().Do(s => s.AddParts(
|
||||
fixture.Create<IEnumerable<IResolverIgnoreRule>>(),
|
||||
new List<IItemResolver> { new AudioResolver(fixture.Create<NamingOptions>()) },
|
||||
fixture.Create<IEnumerable<IIntroProvider>>(),
|
||||
fixture.Create<IEnumerable<IBaseItemComparer>>(),
|
||||
fixture.Create<IEnumerable<ILibraryPostScanTask>>()))
|
||||
.Create();
|
||||
|
||||
// This is pretty terrible but unavoidable
|
||||
BaseItem.FileSystem ??= fixture.Create<IFileSystem>();
|
||||
BaseItem.MediaSourceManager ??= fixture.Create<IMediaSourceManager>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolder_FindsCorrectExtras()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/Up - trailer.mkv",
|
||||
"/movies/Up/Up - sample.mkv",
|
||||
"/movies/Up/Up something else.mkv",
|
||||
"/movies/Up/Up-extra.mkv"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = false
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Equal(3, extras.Count);
|
||||
Assert.Equal(ExtraType.Unknown, extras[0].ExtraType);
|
||||
Assert.Equal(ExtraType.Trailer, extras[1].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[1].GetType());
|
||||
Assert.Equal(ExtraType.Sample, extras[2].ExtraType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolder_CleanExtraNames()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/Recording the audio[Bluray]-behindthescenes.mkv",
|
||||
"/movies/Up/Interview with the dog-interview.mkv",
|
||||
"/movies/Up/shorts/Balloons[1080p].mkv"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = false
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Equal(3, extras.Count);
|
||||
Assert.Equal(ExtraType.BehindTheScenes, extras[0].ExtraType);
|
||||
Assert.Equal("Recording the audio", extras[0].Name);
|
||||
Assert.Equal(ExtraType.Interview, extras[1].ExtraType);
|
||||
Assert.Equal("Interview with the dog", extras[1].Name);
|
||||
Assert.Equal(ExtraType.Short, extras[2].ExtraType);
|
||||
Assert.Equal("Balloons", extras[2].Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolderWithMixedExtras_FindsCorrectExtras()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/Up - trailer.mkv",
|
||||
"/movies/Up/trailers",
|
||||
"/movies/Up/theme-music",
|
||||
"/movies/Up/theme.mp3",
|
||||
"/movies/Up/not a theme.mp3",
|
||||
"/movies/Up/behind the scenes",
|
||||
"/movies/Up/behind the scenes.mkv",
|
||||
"/movies/Up/Up - sample.mkv",
|
||||
"/movies/Up/Up something else.mkv",
|
||||
"/movies/Up/extras"
|
||||
};
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/trailers",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/trailers/some trailer.mkv",
|
||||
Name = "some trailer.mkv",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/behind the scenes",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/behind the scenes/the making of Up.mkv",
|
||||
Name = "the making of Up.mkv",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/theme-music",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/theme-music/theme2.mp3",
|
||||
Name = "theme2.mp3",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/extras",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/extras/Honest Trailer.mkv",
|
||||
Name = "Honest Trailer.mkv",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
Name = Path.GetFileName(p),
|
||||
IsDirectory = !Path.HasExtension(p)
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
_fileSystemMock.Verify();
|
||||
Assert.Equal(7, extras.Count);
|
||||
Assert.Equal(ExtraType.Unknown, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Video), extras[0].GetType());
|
||||
Assert.Equal(ExtraType.Trailer, extras[1].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[1].GetType());
|
||||
Assert.Equal(ExtraType.Trailer, extras[2].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[2].GetType());
|
||||
Assert.Equal(ExtraType.BehindTheScenes, extras[3].ExtraType);
|
||||
Assert.Equal(ExtraType.Sample, extras[4].ExtraType);
|
||||
Assert.Equal(ExtraType.ThemeSong, extras[5].ExtraType);
|
||||
Assert.Equal(typeof(Audio), extras[5].GetType());
|
||||
Assert.Equal(ExtraType.ThemeSong, extras[6].ExtraType);
|
||||
Assert.Equal(typeof(Audio), extras[6].GetType());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolderWithMixedExtras_FindsOnlyExtrasInMovieFolder()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/trailer.mkv",
|
||||
"/movies/Another Movie/trailer.mkv"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = false
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Single(extras);
|
||||
Assert.Equal(ExtraType.Trailer, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[0].GetType());
|
||||
Assert.Equal("trailer", extras[0].FileNameWithoutExtension);
|
||||
Assert.Equal("/movies/Up/trailer.mkv", extras[0].Path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeparateMovieFolderWithParts_FindsCorrectExtras()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up - part1.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up - part1.mkv",
|
||||
"/movies/Up/Up - part2.mkv",
|
||||
"/movies/Up/trailer.mkv",
|
||||
"/movies/Another Movie/trailer.mkv"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = false
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Single(extras);
|
||||
Assert.Equal(ExtraType.Trailer, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[0].GetType());
|
||||
Assert.Equal("trailer", extras[0].FileNameWithoutExtension);
|
||||
Assert.Equal("/movies/Up/trailer.mkv", extras[0].Path);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_WrongExtensions_FindsNoExtras()
|
||||
{
|
||||
var owner = new Movie { Name = "Up", Path = "/movies/Up/Up.mkv" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/movies/Up/Up.mkv",
|
||||
"/movies/Up/trailer.noext",
|
||||
"/movies/Up/theme.png",
|
||||
"/movies/Up/trailers"
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
Name = Path.GetFileName(p),
|
||||
IsDirectory = !Path.HasExtension(p)
|
||||
}).ToList();
|
||||
|
||||
_fileSystemMock.Setup(f => f.GetFiles(
|
||||
"/movies/Up/trailers",
|
||||
It.IsAny<string[]>(),
|
||||
false,
|
||||
false))
|
||||
.Returns(new List<FileSystemMetadata>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FullName = "/movies/Up/trailers/trailer.jpg",
|
||||
Name = "trailer.jpg",
|
||||
IsDirectory = false
|
||||
}
|
||||
}).Verifiable();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
_fileSystemMock.Verify();
|
||||
Assert.Empty(extras);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindExtras_SeriesWithTrailers_FindsCorrectExtras()
|
||||
{
|
||||
var owner = new Series { Name = "Dexter", Path = "/series/Dexter" };
|
||||
var paths = new List<string>
|
||||
{
|
||||
"/series/Dexter/Season 1/S01E01.mkv",
|
||||
"/series/Dexter/trailer.mkv",
|
||||
"/series/Dexter/trailers/trailer2.mkv",
|
||||
};
|
||||
|
||||
var files = paths.Select(p => new FileSystemMetadata
|
||||
{
|
||||
FullName = p,
|
||||
IsDirectory = string.IsNullOrEmpty(Path.GetExtension(p))
|
||||
}).ToList();
|
||||
|
||||
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
|
||||
|
||||
Assert.Equal(2, extras.Count);
|
||||
Assert.Equal(ExtraType.Trailer, extras[0].ExtraType);
|
||||
Assert.Equal(typeof(Trailer), extras[0].GetType());
|
||||
Assert.Equal("trailer", extras[0].FileNameWithoutExtension);
|
||||
Assert.Equal("/series/Dexter/trailer.mkv", extras[0].Path);
|
||||
Assert.Equal("/series/Dexter/trailers/trailer2.mkv", extras[1].Path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using AutoFixture;
|
||||
using AutoFixture.AutoMoq;
|
||||
using Emby.Server.Implementations.IO;
|
||||
using Emby.Server.Implementations.Library;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
public class MediaSourceManagerTests
|
||||
{
|
||||
private readonly MediaSourceManager _mediaSourceManager;
|
||||
|
||||
public MediaSourceManagerTests()
|
||||
{
|
||||
IFixture fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
|
||||
fixture.Inject<IFileSystem>(fixture.Create<ManagedFileSystem>());
|
||||
_mediaSourceManager = fixture.Create<MediaSourceManager>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"C:\mydir\myfile.ext", MediaProtocol.File)]
|
||||
[InlineData("/mydir/myfile.ext", MediaProtocol.File)]
|
||||
[InlineData("file:///mydir/myfile.ext", MediaProtocol.File)]
|
||||
[InlineData("http://example.com/stream.m3u8", MediaProtocol.Http)]
|
||||
[InlineData("https://example.com/stream.m3u8", MediaProtocol.Http)]
|
||||
[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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using Emby.Server.Implementations.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library;
|
||||
|
||||
public class MediaStreamSelectorTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void GetDefaultAudioStreamIndex_EmptyStreams_Null(bool preferDefaultTrack)
|
||||
{
|
||||
Assert.Null(MediaStreamSelector.GetDefaultAudioStreamIndex(Array.Empty<MediaStream>(), Array.Empty<string>(), preferDefaultTrack));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(new string[0], false, 1)]
|
||||
[InlineData(new string[0], true, 1)]
|
||||
[InlineData(new[] { "eng" }, false, 2)]
|
||||
[InlineData(new[] { "eng" }, true, 1)]
|
||||
[InlineData(new[] { "eng", "fre" }, false, 2)]
|
||||
[InlineData(new[] { "fre", "eng" }, false, 1)]
|
||||
[InlineData(new[] { "eng", "fre" }, true, 1)]
|
||||
public void GetDefaultAudioStreamIndex_PreferredLanguage_SelectsCorrect(string[] preferredLanguages, bool preferDefaultTrack, int expectedIndex)
|
||||
{
|
||||
var streams = new MediaStream[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
Index = 0,
|
||||
Type = MediaStreamType.Video,
|
||||
IsDefault = true
|
||||
},
|
||||
new()
|
||||
{
|
||||
Index = 1,
|
||||
Type = MediaStreamType.Audio,
|
||||
Language = "fre",
|
||||
IsDefault = true
|
||||
},
|
||||
new()
|
||||
{
|
||||
Index = 2,
|
||||
Type = MediaStreamType.Audio,
|
||||
Language = "eng",
|
||||
IsDefault = false
|
||||
}
|
||||
};
|
||||
|
||||
Assert.Equal(expectedIndex, MediaStreamSelector.GetDefaultAudioStreamIndex(streams, preferredLanguages, preferDefaultTrack));
|
||||
}
|
||||
|
||||
public static TheoryData<MediaStream, int> GetStreamScore_MediaStream_TestData()
|
||||
{
|
||||
var data = new TheoryData<MediaStream, int>();
|
||||
|
||||
data.Add(new MediaStream(), 111111);
|
||||
data.Add(
|
||||
new MediaStream()
|
||||
{
|
||||
Language = "eng"
|
||||
},
|
||||
10111111);
|
||||
data.Add(
|
||||
new MediaStream()
|
||||
{
|
||||
Language = "fre"
|
||||
},
|
||||
10011111);
|
||||
data.Add(
|
||||
new MediaStream()
|
||||
{
|
||||
IsForced = true
|
||||
},
|
||||
121111);
|
||||
data.Add(
|
||||
new MediaStream()
|
||||
{
|
||||
IsDefault = true
|
||||
},
|
||||
112111);
|
||||
data.Add(
|
||||
new MediaStream()
|
||||
{
|
||||
SupportsExternalStream = true
|
||||
},
|
||||
111211);
|
||||
data.Add(
|
||||
new MediaStream()
|
||||
{
|
||||
IsExternal = true
|
||||
},
|
||||
111112);
|
||||
data.Add(
|
||||
new MediaStream()
|
||||
{
|
||||
Language = "eng",
|
||||
IsForced = true,
|
||||
IsDefault = true,
|
||||
SupportsExternalStream = true,
|
||||
IsExternal = true
|
||||
},
|
||||
10122212);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetStreamScore_MediaStream_TestData))]
|
||||
public void GetStreamScore_MediaStream_CorrectScore(MediaStream stream, int expectedScore)
|
||||
{
|
||||
var languagePref = new[] { "eng", "fre" };
|
||||
|
||||
Assert.Equal(expectedScore, MediaStreamSelector.GetStreamScore(stream, languagePref));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Server.Implementations.Library.Resolvers.Movies;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library;
|
||||
|
||||
public class MovieResolverTests
|
||||
{
|
||||
private static readonly NamingOptions _namingOptions = new();
|
||||
|
||||
[Fact]
|
||||
public void Resolve_GivenLocalAlternateVersion_ResolvesToVideo()
|
||||
{
|
||||
var movieResolver = new MovieResolver(Mock.Of<IImageProcessor>(), Mock.Of<ILogger<MovieResolver>>(), _namingOptions, Mock.Of<IDirectoryService>());
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
{
|
||||
Parent = null,
|
||||
FileInfo = new FileSystemMetadata
|
||||
{
|
||||
FullName = "/movies/Black Panther (2018)/Black Panther (2018) - 1080p 3D.mk3d"
|
||||
}
|
||||
};
|
||||
|
||||
Assert.NotNull(movieResolver.Resolve(itemResolveArgs));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Emby.Server.Implementations.Library;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Library
|
||||
{
|
||||
public class PathExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("Superman: Red Son [imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son [imdbid-tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son - tt10985510", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son {imdbid=tt10985510}", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son (imdbid-tt10985510)", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son", "imdbid", null)]
|
||||
[InlineData("Superman: Red Son [imdbid1=tt11111111][imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son {imdbid1=tt11111111}(imdbid=tt10985510)", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son (imdbid1-tt11111111)[imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son [tmdbid=618355][imdbid=tt10985510]", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son [tmdbid-618355]{imdbid-tt10985510}", "imdbid", "tt10985510")]
|
||||
[InlineData("Superman: Red Son (tmdbid-618355)[imdbid-tt10985510]", "tmdbid", "618355")]
|
||||
[InlineData("Superman: Red Son [providera-id=1]", "providera-id", "1")]
|
||||
[InlineData("Superman: Red Son [providerb-id=2]", "providerb-id", "2")]
|
||||
[InlineData("Superman: Red Son [providera id=4]", "providera id", "4")]
|
||||
[InlineData("Superman: Red Son [providerb id=5]", "providerb id", "5")]
|
||||
[InlineData("Superman: Red Son [tmdbid=3]", "tmdbid", "3")]
|
||||
[InlineData("Superman: Red Son [tvdbid-6]", "tvdbid", "6")]
|
||||
[InlineData("Superman: Red Son {tmdbid=3}", "tmdbid", "3")]
|
||||
[InlineData("Superman: Red Son (tvdbid-6)", "tvdbid", "6")]
|
||||
[InlineData("[tmdbid=618355]", "tmdbid", "618355")]
|
||||
[InlineData("{tmdbid=618355}", "tmdbid", "618355")]
|
||||
[InlineData("(tmdbid=618355)", "tmdbid", "618355")]
|
||||
[InlineData("[tmdbid-618355]", "tmdbid", "618355")]
|
||||
[InlineData("{tmdbid-618355)", "tmdbid", null)]
|
||||
[InlineData("[tmdbid-618355}", "tmdbid", null)]
|
||||
[InlineData("tmdbid=111111][tmdbid=618355]", "tmdbid", "618355")]
|
||||
[InlineData("[tmdbid=618355]tmdbid=111111]", "tmdbid", "618355")]
|
||||
[InlineData("tmdbid=618355]", "tmdbid", null)]
|
||||
[InlineData("[tmdbid=618355", "tmdbid", null)]
|
||||
[InlineData("tmdbid=618355", "tmdbid", null)]
|
||||
[InlineData("tmdbid=", "tmdbid", null)]
|
||||
[InlineData("tmdbid", "tmdbid", null)]
|
||||
[InlineData("[tmdbid=][imdbid=tt10985510]", "tmdbid", null)]
|
||||
[InlineData("[tmdbid-][imdbid-tt10985510]", "tmdbid", null)]
|
||||
[InlineData("Superman: Red Son [tmdbid-618355][tmdbid=1234567]", "tmdbid", "618355")]
|
||||
[InlineData("{tmdbid=}{imdbid=tt10985510}", "tmdbid", null)]
|
||||
[InlineData("(tmdbid-)(imdbid-tt10985510)", "tmdbid", null)]
|
||||
[InlineData("Superman: Red Son {tmdbid-618355}{tmdbid=1234567}", "tmdbid", "618355")]
|
||||
public void GetAttributeValue_ValidArgs_Correct(string input, string attribute, string? expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, PathExtensions.GetAttributeValue(input, attribute));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "")]
|
||||
[InlineData("Superman: Red Son [imdbid=tt10985510]", "")]
|
||||
[InlineData("", "imdbid")]
|
||||
public void GetAttributeValue_EmptyString_ThrowsArgumentException(string input, string attribute)
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => PathExtensions.GetAttributeValue(input, attribute));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("C:/Users/jeff/myfile.mkv", "C:/Users/jeff", "/home/jeff", "/home/jeff/myfile.mkv")]
|
||||
[InlineData("C:/Users/jeff/myfile.mkv", "C:/Users/jeff/", "/home/jeff", "/home/jeff/myfile.mkv")]
|
||||
[InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/jeff's band", "/home/not jeff", "/home/not jeff/consistently inconsistent.mp3")]
|
||||
[InlineData(@"C:\Users\jeff\myfile.mkv", "C:\\Users/jeff", "/home/jeff", "/home/jeff/myfile.mkv")]
|
||||
[InlineData(@"C:\Users\jeff\myfile.mkv", "C:\\Users/jeff", "/home/jeff/", "/home/jeff/myfile.mkv")]
|
||||
[InlineData(@"C:\Users\jeff\myfile.mkv", "C:\\Users/jeff/", "/home/jeff/", "/home/jeff/myfile.mkv")]
|
||||
[InlineData(@"C:\Users\jeff\myfile.mkv", "C:\\Users/jeff/", "/", "/myfile.mkv")]
|
||||
[InlineData("/o", "/o", "/s", "/s")] // regression test for #5977
|
||||
public void TryReplaceSubPath_ValidArgs_Correct(string path, string subPath, string newSubPath, string? expectedResult)
|
||||
{
|
||||
Assert.True(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result));
|
||||
Assert.Equal(expectedResult, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, null)]
|
||||
[InlineData(null, "/my/path", "/another/path")]
|
||||
[InlineData("/my/path", null, "/another/path")]
|
||||
[InlineData("/my/path", "/another/path", null)]
|
||||
[InlineData("", "", "")]
|
||||
[InlineData("/my/path", "", "")]
|
||||
[InlineData("", "/another/path", "")]
|
||||
[InlineData("", "", "/new/subpath")]
|
||||
[InlineData("/home/jeff/music/jeff's band/consistently inconsistent.mp3", "/home/jeff/music/not jeff's band", "/home/not jeff")]
|
||||
public void TryReplaceSubPath_InvalidInput_ReturnsFalseAndNull(string? path, string? subPath, string? newSubPath)
|
||||
{
|
||||
Assert.False(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result));
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, '/', null)]
|
||||
[InlineData(null, '\\', null)]
|
||||
[InlineData("/home/jeff/myfile.mkv", '\\', @"\home\jeff\myfile.mkv")]
|
||||
[InlineData(@"C:\Users\Jeff\myfile.mkv", '/', "C:/Users/Jeff/myfile.mkv")]
|
||||
[InlineData(@"\home/jeff\myfile.mkv", '\\', @"\home\jeff\myfile.mkv")]
|
||||
[InlineData(@"\home/jeff\myfile.mkv", '/', "/home/jeff/myfile.mkv")]
|
||||
[InlineData("", '/', "")]
|
||||
public void NormalizePath_SpecifyingSeparator_Normalizes(string? path, char separator, string? expectedPath)
|
||||
{
|
||||
Assert.Equal(expectedPath, path.NormalizePath(separator));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/home/jeff/myfile.mkv")]
|
||||
[InlineData(@"C:\Users\Jeff\myfile.mkv")]
|
||||
[InlineData(@"\home/jeff\myfile.mkv")]
|
||||
public void NormalizePath_NoArgs_UsesDirectorySeparatorChar(string path)
|
||||
{
|
||||
var separator = Path.DirectorySeparatorChar;
|
||||
|
||||
Assert.Equal(path.Replace('\\', separator).Replace('/', separator), path.NormalizePath());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/home/jeff/myfile.mkv", '/')]
|
||||
[InlineData(@"C:\Users\Jeff\myfile.mkv", '\\')]
|
||||
[InlineData(@"\home/jeff\myfile.mkv", '/')]
|
||||
public void NormalizePath_OutVar_Correct(string path, char expectedSeparator)
|
||||
{
|
||||
var result = path.NormalizePath(out var separator);
|
||||
|
||||
Assert.Equal(expectedSeparator, separator);
|
||||
Assert.Equal(path.Replace('\\', separator).Replace('/', separator), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NormalizePath_SpecifyInvalidSeparator_ThrowsException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => string.Empty.NormalizePath('a'));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user