using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using Emby.Server.Implementations.ScheduledTasks.Tasks; using MediaBrowser.Controller.ScheduledTasks; using MediaBrowser.Model.Tasks; using Xunit; namespace Jellyfin.Server.Implementations.Tests.ScheduledTasks; public class ScanLeaderOptionsTests { /// /// A gated key that matches no registered task silently stops gating anything, so the default /// set is pinned to the task keys that actually exist in the build. /// [Fact] public void DefaultGatedTaskKeys_Should_MatchRegisteredScheduledTasks() { var registeredKeys = DiscoverScheduledTaskKeys(); Assert.NotEmpty(registeredKeys); Assert.Empty(new ScanLeaderOptions().GatedTaskKeys.Except(registeredKeys, StringComparer.Ordinal)); } private static HashSet DiscoverScheduledTaskKeys() { var keys = new HashSet(StringComparer.Ordinal); var assemblies = new[] { typeof(DeleteTranscodeFileTask).Assembly, typeof(Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask).Assembly }; foreach (var type in assemblies.SelectMany(a => a.GetTypes())) { if (type.IsAbstract || type.IsInterface || !typeof(IScheduledTask).IsAssignableFrom(type)) { continue; } // Task keys are constant expressions, so an uninitialised instance is enough to read // them without standing up each task's dependency graph. var task = (IScheduledTask)RuntimeHelpers.GetUninitializedObject(type); keys.Add(task.Key); } return keys; } }