public void Will_Choose_Highest_Priority_Source_For_Two_Overlapping_Plan_Items() { // Create plan var group = Guid.NewGuid(); var plan1 = RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("s1", group), DateTime.UtcNow.AddHours(1), TimeSpan.FromMinutes(20)); var times1 = plan1.GetTimes(DateTime.UtcNow).Select(s => s.Planned).Single(); var plan2 = RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("s1", group), times1.End - TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(20)); var times2 = plan2.GetTimes(DateTime.UtcNow).Select(s => s.Planned).Single(); var best = ResourceMock.Create("r3", SourceMock.Create("s1", group)).SetPriority(6); // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase) { { ResourceMock.Create("r1", SourceMock.Create("s1", group)).SetPriority(1) }, { best }, { ResourceMock.Create("r2", SourceMock.Create("s1", group)).SetPriority(-1) }, { plan2 }, { plan1 }, }; // Load var schedules = componentUnderTest.GetSchedules(DateTime.UtcNow).ToArray(); // Validate Assert.AreEqual(2, schedules.Length, "Schedules"); Assert.AreSame(best, schedules[0].Resource, "Resource 0"); Assert.AreSame(best, schedules[1].Resource, "Resource 1"); Assert.AreSame(plan1, schedules[0].Definition, "Plan 0"); Assert.AreSame(plan2, schedules[1].Definition, "Plan 1"); Assert.AreEqual(times1, schedules[0].Time, "Time 0"); Assert.AreEqual(times2, schedules[1].Time, "Time 1"); Assert.IsFalse(schedules[0].StartsLate, "Late 0"); Assert.IsFalse(schedules[1].StartsLate, "Late 1"); }
public void Will_Use_Resource_With_Highest_Priority_When_Explicit_Binding_Is_Used() { // Create plan var source1 = SourceMock.Create("s1"); var source2 = SourceMock.Create("s2"); var res1 = ResourceMock.Create("r1", source1, source2).SetPriority(5); var res2 = ResourceMock.Create("r2", source1, source2).SetPriority(6); var res3 = ResourceMock.Create("r3", source1, source2).SetPriority(0); var refTime = DateTime.UtcNow; var plan1 = RecordingDefinition.Create(false, "A1", Guid.NewGuid(), new[] { res1 }, source1, refTime.AddMinutes(100), TimeSpan.FromMinutes(20)); var plan2 = RecordingDefinition.Create(false, "A2", Guid.NewGuid(), new[] { res1 }, source1, refTime.AddMinutes(110), TimeSpan.FromMinutes(20)); var plan3 = RecordingDefinition.Create(false, "A3", Guid.NewGuid(), null, source2, refTime.AddMinutes(130), TimeSpan.FromMinutes(20)); // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase) { { res1 }, { res2 }, { res3 }, { plan1 }, { plan2 }, { plan3 }, }; // Load var schedules = componentUnderTest.GetSchedules(refTime).ToArray(); // Validate Assert.AreEqual(3, schedules.Length, "#schedule"); Assert.AreSame(res1, schedules[0].Resource, "resource 0"); Assert.AreSame(res1, schedules[1].Resource, "resource 1"); Assert.AreSame(res2, schedules[2].Resource, "resource 2"); }
public void Will_Enforce_Start_Order() { // Create plan var source1 = SourceMock.Create("s1"); var source2 = SourceMock.Create("s2"); var res1 = ResourceMock.Create("r1", source1, source2).SetPriority(1); var res2 = ResourceMock.Create("r2", source1, source2).SetPriority(2); var refTime = DateTime.UtcNow.Date.AddDays(10); var plan1 = RecordingDefinition.Create(false, "A1", Guid.NewGuid(), null, source1, refTime.AddHours(18), TimeSpan.FromHours(2)); var plan2 = RecordingDefinition.Create(false, "A2", Guid.NewGuid(), null, source2, refTime.AddHours(19), TimeSpan.FromHours(2)); // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase, Properties.Files.EnforceResourceStartOrder) { { res1 }, { res2 }, { plan1 }, { plan2 }, }; // Load var schedules = componentUnderTest.GetSchedules(refTime).ToArray(); // Validate Assert.AreEqual(2, schedules.Length, "#schedules"); Assert.AreSame(plan1, schedules[0].Definition, "plan 1"); Assert.AreSame(plan2, schedules[1].Definition, "plan 2"); Assert.AreSame(res1, schedules[0].Resource, "resource 1"); Assert.AreSame(res2, schedules[1].Resource, "resource 2"); }
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 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 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 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 Will_Minimize_Sources_Per_Resource() { // Create plan var source1 = SourceMock.Create("s1"); var source2 = SourceMock.Create("s2"); var source3 = SourceMock.Create("s3"); var res1 = ResourceMock.Create("r1", source1, source2, source3).SetPriority(1); var res2 = ResourceMock.Create("r2", source1, source2, source3).SetPriority(1); var res3 = ResourceMock.Create("r3", source1, source2, source3).SetPriority(0); var refTime = DateTime.UtcNow.Date.AddDays(10); var plan1 = RecordingDefinition.Create(false, "A1", Guid.NewGuid(), null, source1, refTime.AddHours(19), TimeSpan.FromHours(2)); var plan2 = RecordingDefinition.Create(false, "A2", Guid.NewGuid(), null, source2, refTime.AddHours(20), TimeSpan.FromHours(3)); var plan3 = RecordingDefinition.Create(false, "A3", Guid.NewGuid(), null, source3, refTime.AddHours(20), TimeSpan.FromHours(2)); var plan4 = RecordingDefinition.Create(false, "A4", Guid.NewGuid(), null, source2, refTime.AddHours(22), TimeSpan.FromHours(2)); // Create component under test var componentUnderTest = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase, Properties.Files.MinimizeSourcesPerResource) { { res1 }, { res2 }, { res3 }, { plan1 }, { plan2 }, { plan3 }, { plan4 }, }; // Load var schedules = componentUnderTest.GetSchedules(refTime).ToArray(); // Validate Assert.AreEqual(4, schedules.Length, "#schedule"); var exec1 = schedules.Single(s => s.Definition.UniqueIdentifier == plan1.UniqueIdentifier); var exec2 = schedules.Single(s => s.Definition.UniqueIdentifier == plan2.UniqueIdentifier); var exec3 = schedules.Single(s => s.Definition.UniqueIdentifier == plan3.UniqueIdentifier); var exec4 = schedules.Single(s => s.Definition.UniqueIdentifier == plan4.UniqueIdentifier); Assert.AreSame(res1, exec1.Resource, "A1"); Assert.AreSame(res2, exec2.Resource, "A2"); Assert.AreSame(res3, exec3.Resource, "A3"); Assert.AreSame(res2, exec4.Resource, "A4"); }
public void Bad_Planning_After_Recording_Start() { // Create component under test using (var rm = ResourceManager.Create(StringComparer.InvariantCultureIgnoreCase)) { var now = DateTime.Now.Date.AddDays(10); var source1 = SourceMock.Create("s1"); var source2 = SourceMock.Create("s2"); var source3 = SourceMock.Create("s3"); var dev1 = ResourceMock.Create("dev1", source1, source2, source3); var dev2 = ResourceMock.Create("dev2", source1, source2, source3); var id1 = Guid.NewGuid(); var start1 = now.AddHours(11).AddMinutes(40); var dur1 = TimeSpan.FromMinutes(15); var plan1 = RecordingDefinition.Create(false, "test1", id1, null, source1, start1, dur1); var plan2 = RecordingDefinition.Create(false, "test2", Guid.NewGuid(), null, source2, now.AddHours(11).AddMinutes(45), TimeSpan.FromMinutes(15)); var plan3 = RecordingDefinition.Create(false, "test3", Guid.NewGuid(), null, source3, now.AddHours(11).AddMinutes(50), TimeSpan.FromMinutes(15)); rm.Add(dev1); rm.Add(dev2); Assert.IsTrue(rm.Start(dev2, source1, id1, "test1", start1, start1 + dur1)); var cut = rm.CreateScheduler(false); cut.Add(plan1); cut.Add(plan2); cut.Add(plan3); var schedules = cut.GetSchedules(start1.AddMinutes(5).AddTicks(1)).Where(s => s.Definition.UniqueIdentifier != id1).ToArray(); Assert.AreEqual(2, schedules.Length, "#schedules"); Assert.AreEqual("test2", schedules[0].Definition.Name, "1"); Assert.AreEqual("test3", schedules[1].Definition.Name, "2"); } }
public void Plan_Item_Remembers_Settings() { // Data under test var duration = TimeSpan.FromMinutes(45); var source = SourceMock.Create("s1"); var start = DateTime.UtcNow; // Process var componentUnderTest = RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, source, start, duration); // Validate Assert.IsNotNull(componentUnderTest, "Create"); Assert.AreSame(source, componentUnderTest.Source, "Source"); // Load plan var times = componentUnderTest.GetTimes(DateTime.MinValue).Select(s => s.Planned).ToArray(); // Validate Assert.AreEqual(1, times.Length, "Times"); Assert.AreEqual(start, times[0].Start, "Start"); Assert.AreEqual(duration, times[0].Duration, "Duration"); }
public void A_Plan_Can_Have_A_List_Of_Preferred_Resources() { // Create environment var source = SourceMock.Create("A"); var device1 = ResourceMock.Create("D1", source); var device2 = ResourceMock.Create("D2", source); var device3 = ResourceMock.Create("D3", source); // Create the plan var plan = RecordingDefinition.Create(false, "test", Guid.NewGuid(), new[] { device2 }, source, DateTime.UtcNow, TimeSpan.FromMinutes(10)); // Create the component under test var cut = new RecordingScheduler(StringComparer.InvariantCultureIgnoreCase) { device1, device3, plan }; // Get the schedule var schedule = cut.GetSchedules(DateTime.UtcNow.AddYears(-1)).Single(); // Validate Assert.IsNull(schedule.Resource, "Resource"); }
public void Repeat_Must_Have_At_Least_One_Day() { // Process RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("a"), DateTime.UtcNow, TimeSpan.FromMinutes(10), DateTime.Now.Date.AddYears(1)); }
public void Plan_Item_Duration_Must_Not_Be_Negative() { // Process RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("s1"), DateTime.UtcNow, new TimeSpan(-1)); }
public void A_Plan_Item_Must_Not_Be_Older_Than_2000() { // Process RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("s1"), new DateTime(1999, 2, 13, 0, 0, 0, DateTimeKind.Utc), TimeSpan.FromMinutes(10)); }
public void End_Of_Repeating_Recording_Must_Be_In_The_Future() { // Process RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("a"), DateTime.UtcNow, TimeSpan.FromMinutes(10), DateTime.Now.Date.AddYears(-1), DayOfWeek.Monday); }
public void A_Plan_Can_Have_A_Repeating_Pattern() { // Create the plan var localStart = new DateTime(2011, 9, 8, 21, 35, 0, DateTimeKind.Local); var localEnd = new DateTime(2012, 3, 31, 0, 0, 0, DateTimeKind.Local); var gmtStart = localStart.ToUniversalTime(); var plan = RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("s1"), gmtStart, TimeSpan.FromMinutes(10), localEnd.Date, DayOfWeek.Monday, DayOfWeek.Friday, DayOfWeek.Tuesday); // Cross check map var allowed = new HashSet <DayOfWeek> { DayOfWeek.Monday, DayOfWeek.Friday, DayOfWeek.Tuesday }; // Scan all foreach (var time in plan.GetTimes(gmtStart.AddYears(-1)).Select(s => s.Planned)) { // Get next good time while (!allowed.Contains(localStart.DayOfWeek)) { localStart = localStart.AddDays(1); } // Test it Assert.AreEqual(localStart, time.LocalStart); Assert.AreEqual(TimeSpan.FromMinutes(10), time.Duration); // Next day localStart = localStart.AddDays(1); } // Check end Assert.AreEqual(localEnd + localStart.TimeOfDay, localStart); }
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"); }
public void An_Unknown_Source_Can_Not_Be_Recorded() { // Create recordings var plan = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Create( "unknown" ), TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 90 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { { FreeTVDevice }, { plan }, }; // Resolve var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 1, schedules.Length, "Schedules" ); Assert.AreSame( null, schedules[0].Resource, "Resource" ); Assert.AreSame( plan, schedules[0].Definition, "Plan" ); }
public void Repeat_Must_Not_Use_A_Day_Twice() { // Process RecordingDefinition.Create(false, "test", Guid.NewGuid(), null, SourceMock.Create("a"), DateTime.UtcNow, TimeSpan.FromMinutes(10), DateTime.Now.Date.AddYears(1), DayOfWeek.Monday, DayOfWeek.Saturday, DayOfWeek.Monday); }