public void Encrypted_Source_Of_A_Plan_Item_Is_Supported_By_Resource() { // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); // Add componentUnderTest.Add(ResourceMock.Create("r1", SourceMock.Create("s1")).SetEncryptionLimit(1)); componentUnderTest.Add(RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("s1", true), DateTime.UtcNow, TimeSpan.FromMinutes(12))); }
public void No_Resource_Can_Be_Used_Twice() { // Create the component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); var resource = ResourceMock.Create("a"); // Register devices componentUnderTest.Add(resource); componentUnderTest.Add(resource); }
public void Exceptions_Must_Be_Defined_On_A_Full_Date() { // Create bad exception var exception = new PlanException { ExceptionDate = DateTime.Now.Date.AddMinutes(12) }; // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); // Add componentUnderTest.Add(ResourceMock.Create("r1", SourceMock.Create("s1"))); componentUnderTest.Add(RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("s1"), DateTime.UtcNow, TimeSpan.FromMinutes(12)), exception); }
public void Individual_Decryption_Counter_Must_Not_Be_Negative() { // Create the component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); // Register device componentUnderTest.Add(ResourceMock.Create("a").SetEncryptionLimit(-1)); }
public void A_Resource_Must_Not_Be_Null() { // Create the component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); // Register a null resource componentUnderTest.Add(default(IScheduleResource)); }
public void Can_Not_Add_A_Null_Plan_Item() { // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); // Add componentUnderTest.Add(default(IRecordingDefinition)); }
public void There_Can_Be_Only_One_Exception_Per_Date() { // Create bad exception var exception1 = new PlanException { ExceptionDate = DateTime.Now.Date }; var exception2 = new PlanException { ExceptionDate = exception1.ExceptionDate }; // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); // Add componentUnderTest.Add(ResourceMock.Create("r1", SourceMock.Create("s1"))); componentUnderTest.Add(RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("s1"), DateTime.UtcNow, TimeSpan.FromMinutes(12)), exception1, exception2); }
public void Can_Add_Multiple_Resources() { // Create the component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); // Register devices for (int i = 0; i < 100; i++) { componentUnderTest.Add(ResourceMock.Create(i.ToString("00"))); } }
public void Can_Handle_Very_Long_Recordings_With_Multiple_Devices() { // All sources var sources = Enumerable .Range(0, 100) .Select(i => SourceMock.Create("S" + i.ToString("00"))) .ToArray(); // Create environment var device1 = ResourceMock.Create("D1", sources); var device2 = ResourceMock.Create("D2", sources); var device3 = ResourceMock.Create("D3", sources); var device4 = ResourceMock.Create("D4", sources); // Create the plan var allDays = new[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday }; var refTime = new DateTime(2013, 12, 1); var plan1 = RecordingDefinition.Create(false, "test1", Guid.NewGuid(), null, sources[0], refTime, TimeSpan.FromHours(23), refTime.AddYears(100), allDays); var plan2 = RecordingDefinition.Create(false, "test2", Guid.NewGuid(), null, sources[1], refTime.AddHours(22), TimeSpan.FromHours(4), refTime.AddYears(100), allDays); // Create the component under test var cut = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase) { device1, device2, device3, device4, plan1, plan2 }; // Other foreach (var plan in Enumerable .Range(2, 10) .Select(i => RecordingDefinition.Create(false, "test" + i.ToString("00"), Guid.NewGuid(), null, sources[i], refTime.AddMinutes(5 * i), TimeSpan.FromMinutes(6), refTime.AddYears(100), allDays))) { cut.Add(plan); } // Get the schedule var schedules = cut.GetSchedules(refTime.AddHours(-1)).Take(500).ToArray(); // Validate Assert.AreEqual(500, schedules.Length, "#count"); Assert.IsFalse(schedules.Any(schedule => schedule.StartsLate), "late!"); // Dump plan foreach (var schedule in schedules) { Console.WriteLine(schedule); } }
public void The_Number_Of_Recordings_Per_Plan_Is_Limited() { // Create component under test var cut = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase) { FreeTVDevice }; // Add all plans for (int i = 0; i <= RecordingScheduler.MaximumRecordingsInPlan; i++) { // Create recording var plan = RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddHours(i), TimeSpan.FromMinutes(90)); // Add it cut.Add(plan); } // Resolve var schedules = cut.GetSchedules(TimeBias).ToArray(); // Validate Assert.AreEqual(RecordingScheduler.MaximumRecordingsInPlan + 1, (uint)schedules.Length, "Schedules"); // Check all for (int i = 0; i < RecordingScheduler.MaximumRecordingsInPlan; i++) { // Load var schedule = schedules[i]; // Validate Assert.AreSame(FreeTVDevice, schedule.Resource, "Resource {0}", i); Assert.AreEqual(TimeBias.AddHours(i), schedule.Time.Start, "Start {0}", i); Assert.AreEqual(TimeSpan.FromMinutes(90), schedule.Time.Duration, "Duration {0}", i); Assert.IsFalse(schedule.StartsLate, "Late {0}", i); } // Load the last var last = schedules[RecordingScheduler.MaximumRecordingsInPlan]; // Validate - internal planning is not touched Assert.AreSame(FreeTVDevice, last.Resource, "Resource"); Assert.AreEqual(TimeBias.AddHours(RecordingScheduler.MaximumRecordingsInPlan), last.Time.Start, "Start"); Assert.AreEqual(TimeSpan.FromMinutes(90), last.Time.Duration, "Duration"); Assert.IsFalse(last.StartsLate, "Late"); }
/// <summary> /// Registriert diese Aufzeichnung in einer Planungsinstanz. /// </summary> /// <param name="scheduler">Die zu verwendende Planungsinstanz.</param> /// <param name="job">Der zugehörige Auftrag.</param> /// <param name="devices">Die Liste der Geräte, auf denen die Aufzeichnung ausgeführt werden darf.</param> /// <param name="findSource">Dient zum Prüfen einer Quelle.</param> /// <param name="disabled">Alle deaktivierten Aufträge.</param> /// <param name="context">Die aktuelle Planungsumgebung.</param> /// <exception cref="ArgumentNullException">Es wurden nicht alle Parameter angegeben.</exception> public void AddToScheduler(RecordingScheduler scheduler, VCRJob job, IScheduleResource[] devices, Func <SourceSelection, SourceSelection> findSource, Func <Guid, bool> disabled, PlanContext context) { // Validate if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (job == null) { throw new ArgumentNullException(nameof(job)); } if (findSource == null) { throw new ArgumentNullException(nameof(findSource)); } // Let VCR.NET choose a profile to do the work if (job.AutomaticResourceSelection) { devices = null; } // Create the source selection var persistedSource = Source ?? job.Source; var selection = findSource(persistedSource); // Station no longer available if (selection == null) { if (persistedSource != null) { selection = new SourceSelection { DisplayName = persistedSource.DisplayName, ProfileName = persistedSource.ProfileName, Location = persistedSource.Location, Group = persistedSource.Group, Source = new Station { TransportStream = persistedSource.Source?.TransportStream ?? 0, Network = persistedSource.Source?.Network ?? 0, Service = persistedSource.Source?.Service ?? 0, Name = persistedSource.DisplayName, }, } } } ; // See if we are allowed to process var identifier = UniqueID.Value; if (disabled != null) { if (disabled(identifier)) { return; } } // Load all var name = string.IsNullOrEmpty(Name) ? job.Name : $"{job.Name} ({Name})"; var source = ProfileScheduleResource.CreateSource(selection); var duration = TimeSpan.FromMinutes(Duration); var noStartBefore = NoStartBefore; var start = FirstStart; // Check repetition var repeat = CreateRepeatPattern(); if (repeat == null) { // Only if not being recorded if (!noStartBefore.HasValue) { scheduler.Add(RecordingDefinition.Create(this, name, identifier, devices, source, start, duration)); } } else { // See if we have to adjust the start day if (noStartBefore.HasValue) { // Attach to the limit - actually we shift it a bit further assuming that we did have no large exception towards the past and the duration is moderate var startAfter = noStartBefore.Value.AddHours(12); var startAfterDay = startAfter.ToLocalTime().Date; // Localize the start time var startTime = start.ToLocalTime().TimeOfDay; // First adjust start = (startAfterDay + startTime).ToUniversalTime(); // One more day if (start < startAfter) { start = (startAfterDay.AddDays(1) + startTime).ToUniversalTime(); } } // Read the rest var exceptions = Exceptions.Select(e => e.ToPlanException(duration)).ToArray(); var endDay = LastDay.GetValueOrDefault(MaxMovableDay); // A bit more complex if (start.Date <= endDay.Date) { scheduler.Add(RecordingDefinition.Create(this, name, identifier, devices, source, start, duration, endDay, repeat), exceptions); } } }
public void Performance_Of_A_Real_Life_Scenario() { // Get the path to the test directories var jobDirectory = @"*TBD*"; var profileDirectory = Environment.ExpandEnvironmentVariables(@"%AllUsersProfile%\DVBNETProfiles"); // Helper - will be reused var job = new XmlDocument(); // Sources var sourceMap = new Dictionary <string, SourceMock>(StringComparer.InvariantCultureIgnoreCase); var profileMap = new Dictionary <string, XmlDocument>(StringComparer.InvariantCultureIgnoreCase); var groupMap = new Dictionary <object, Guid>(); var plan = new List <IRecordingDefinition>(); // Process all job files foreach (var jobPath in Directory.EnumerateFiles(jobDirectory, "*.j39")) { // Load to document job.Load(jobPath); // Common data var autoSelect = bool.Parse(job.SelectSingleNode("VCRJob/AutomaticResourceSelection").InnerText); var defaultSource = job.SelectSingleNode("VCRJob/Source"); var profileName = defaultSource.InnerText.Substring(defaultSource.InnerText.LastIndexOf('@') + 1); var jobName = job.SelectSingleNode("VCRJob/Name").InnerText; // Load the profile once XmlDocument profile; if (!profileMap.TryGetValue(profileName, out profile)) { // Create profile = new XmlDocument(); // Load profile.Load(Path.Combine(profileDirectory, profileName + ".dnp")); // Remapping for (; ;) { // Create a new namespace table var namespaces = new XmlNamespaceManager(profile.NameTable); // Add us namespaces.AddNamespace("dvbnet", "http://psimarron.net/DVBNET/Profiles"); // Check for remap var loadFrom = profile.SelectSingleNode("//dvbnet:UseSourcesFrom", namespaces); if (loadFrom == null) { break; } // Load the name var refProfileName = loadFrom.InnerText; if (string.IsNullOrEmpty(refProfileName)) { break; } // Reload if (profileMap.TryGetValue(refProfileName, out profile)) { continue; } // Create profile = new XmlDocument(); // Load profile.Load(Path.Combine(profileDirectory, refProfileName + ".dnp")); // Remember profileMap.Add(refProfileName, profile); } // Remember profileMap.Add(profileName, profile); } // Create a new namespace table var profileNamespaces = new XmlNamespaceManager(profile.NameTable); // Add us profileNamespaces.AddNamespace("dvbnet", "http://psimarron.net/DVBNET/Profiles"); // Validate Assert.IsTrue(autoSelect, "Auto Select"); // Process all schedules foreach (XmlElement schedule in job.SelectNodes("VCRJob/Schedule")) { // Extract data var repeat = schedule.SelectSingleNode("Days").InnerText.Split(' ').Where(d => !string.IsNullOrEmpty(d)).Select(d => (DayOfWeek)Enum.Parse(typeof(DayOfWeek), d)).ToArray(); var firstStart = DateTime.Parse(schedule.SelectSingleNode("FirstStart").InnerText, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); var lastDay = DateTime.Parse(schedule.SelectSingleNode("LastDay").InnerText, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); var duration = TimeSpan.FromMinutes(uint.Parse(schedule.SelectSingleNode("Duration").InnerText)); var source = (XmlElement)(schedule.SelectSingleNode("Source") ?? defaultSource); var id = Guid.Parse(schedule.SelectSingleNode("UniqueID").InnerText); var scheduleName = schedule.SelectSingleNode("Name").InnerText; var sourceName = source.GetAttribute("name"); // Find the real source SourceMock realSource; if (!sourceMap.TryGetValue(sourceName, out realSource)) { // Split name var split = sourceName.LastIndexOf('['); var stationName = sourceName.Substring(0, split).TrimEnd(); var providerName = sourceName.Substring(split + 1).TrimEnd(']'); // Locate the station var station = (XmlElement)profile.SelectSingleNode("//dvbnet:Station[@name='" + stationName + "' and @provider='" + providerName + "']", profileNamespaces); var encrypted = bool.Parse(station.GetAttribute("scrambled")); var group = station.ParentNode; // Unique group identifier Guid groupIdentifier; if (!groupMap.TryGetValue(group, out groupIdentifier)) { groupMap.Add(group, groupIdentifier = Guid.NewGuid()); } // Create the source entry sourceMap.Add(sourceName, realSource = SourceMock.Create(sourceName, groupIdentifier, encrypted)); } // Get the full name if (string.IsNullOrEmpty(scheduleName)) { scheduleName = jobName; } else if (!string.IsNullOrEmpty(jobName)) { scheduleName = jobName + " (" + scheduleName + ")"; } // Create the request if (repeat.Length > 0) { plan.Add(RecordingDefinition.Create(schedule, scheduleName, id, null, realSource, firstStart, duration, lastDay, repeat)); } else { plan.Add(RecordingDefinition.Create(schedule, scheduleName, id, null, realSource, firstStart, duration)); } } } // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase); var sources = sourceMap.Values.ToArray(); // Add resources - hard coded, should be taken from configuration in some future version for (var i = 0; i++ < 4;) { componentUnderTest.Add(ResourceMock.Create("duo" + i.ToString("0"), sources).SetEncryptionLimit(1)); } // Add the plan foreach (var recording in plan) { componentUnderTest.Add(recording); } // Process var refTime = new DateTime(2013, 10, 26, 12, 0, 0, DateTimeKind.Utc); var timer = Stopwatch.StartNew(); var all = componentUnderTest.GetSchedules(refTime).Take(1000).ToArray(); var elapsed = timer.Elapsed; // Report Console.WriteLine("{0:N3}ms", elapsed.TotalMilliseconds); // Validate Assert.AreEqual(1000, all.Length, "#jobs"); }