/// <summary> /// Ermittelt zu allen erlaubten Geräten die eindeutigen Kennungen. /// </summary> /// <param name="scheduler">Die zugehörige Planungsinstanz.</param> /// <returns>Gesetzt, wenn mindestens ein Gerät erlaubt ist.</returns> public bool PrepareResourceMap(RecordingScheduler scheduler) { // Reset AllowedResources = null; // Check mode var resources = Definition.Resources; if (resources != null) { if (resources.Length > 0) { AllowedResources = Definition.Resources.Where(r => (r != null) && scheduler.Resources.Contains(r)).ToArray(); } } // Create full map if (AllowedResources == null) { AllowedResources = scheduler.Resources.ToArray(); } // Report return(AllowedResources.Length > 0); }
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_Choose_Highest_Priority_Source_For_Single_Plan_Item() { // Create plan var plan = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Create( "s1" ), DateTime.UtcNow.AddHours( 1 ), TimeSpan.FromMinutes( 20 ) ); var best = ResourceMock.Create( "r3", SourceMock.Create( "s1" ) ).SetPriority( 6 ); var times = plan.GetTimes( DateTime.UtcNow ).Select( s => s.Planned ).ToArray(); // Create component under test var componentUnderTest = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { { ResourceMock.Create( "r1", SourceMock.Create( "s1" ) ).SetPriority( 1 ) }, { best }, { ResourceMock.Create( "r2", SourceMock.Create( "s1" ) ).SetPriority( -1 ) }, { plan }, }; // Load var schedules = componentUnderTest.GetSchedules( DateTime.UtcNow ).ToArray(); // Validate Assert.AreEqual( 1, schedules.Length, "Schedules" ); Assert.AreSame( best, schedules[0].Resource, "Resource" ); Assert.AreSame( plan, schedules[0].Definition, "Plan" ); Assert.AreEqual( times.Single(), schedules[0].Time, "Time" ); Assert.IsFalse( schedules[0].StartsLate, "Late" ); }
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_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 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 Decrypted_Source_Requires_Decryption_Resource() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test1", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 90 ) ); var plan2 = RecordingDefinition.Create( false, "test2", Guid.NewGuid(), null, SourceMock.Source1Group1Pay, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 90 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { FreeTVDevice, plan1, plan2 }; // Resolve var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 2, schedules.Length, "Schedule" ); Assert.IsNull( schedules[0].Resource, "Resource 1" ); Assert.AreSame( FreeTVDevice, schedules[1].Resource, "Resource 2" ); }
public void Two_Decrypted_Recordings_On_Same_Group_Require_Two_Decryption_Devices() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Pay, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 100 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source4Group1Pay, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 10 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { PayTVDevice1, PayTVDevice2, plan1, plan2 }; // Process var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 2, schedules.Length, "Schedules" ); Assert.IsNotNull( schedules[0].Resource, "Resource 1" ); Assert.IsNotNull( schedules[1].Resource, "Resource 2" ); Assert.AreNotSame( schedules[0].Resource, schedules[1].Resource, "Resources" ); }
public void Will_Skip_Recording_If_Unable_To_Decrypt() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test1", Guid.NewGuid(), null, SourceMock.Source1Group1Pay, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 100 ) ); var plan2 = RecordingDefinition.Create( false, "test2", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 100 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { FreeTVDevice, plan1, plan2 }; // Process var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 2, schedules.Length, "Schedules" ); Assert.IsNull( schedules[0].Resource, "Resource 1" ); Assert.AreSame( FreeTVDevice, schedules[1].Resource, "Resource 2" ); Assert.IsFalse( schedules[1].StartsLate, "Late 2" ); }
public void Can_Join_Recordings_Even_If_Planning_Creates_Separate_Blocks() { // Create recordings var plan1 = RecordingDefinition.Create( false, "A1", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddMinutes( 60 ), TimeSpan.FromMinutes( 120 ) ); var plan2 = RecordingDefinition.Create( false, "A2", Guid.NewGuid(), null, SourceMock.Source2Group1Free, TimeBias.AddMinutes( 165 ), TimeSpan.FromMinutes( 60 ) ); var plan3 = RecordingDefinition.Create( false, "B1", Guid.NewGuid(), null, SourceMock.Source1Group2Free, TimeBias.AddMinutes( 150 ), TimeSpan.FromMinutes( 120 ) ); var plan4 = RecordingDefinition.Create( false, "C1", Guid.NewGuid(), null, SourceMock.Source1Group3Free, TimeBias.AddMinutes( 120 ), TimeSpan.FromMinutes( 120 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { Device1, Device2, plan1, plan2, plan3, plan4 }; // Resolve var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 4, schedules.Length, "Schedule" ); // Process all foreach (var schedule in schedules) Assert.AreEqual( ReferenceEquals( schedule.Definition, plan3 ), schedule.StartsLate, "Late {0}", schedule.Definition.Name ); }
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" ); }
/// <summary> /// Ermittelt den aktuellen Aufzeichnungsplan. /// </summary> /// <param name="scheduler">Die zu verwendende Zeitplanungsinstanz.</param> /// <param name="referenceTime">Der Bezugspunkt für die Planung.</param> /// <param name="disabled">Alle deaktivierte Aufträge und Aufgaben.</param> /// <param name="limit">Die Anzahl der zu berücksichtigenden Planungselemente.</param> /// <returns>Die Liste der nächsten Aufzeichnungen.</returns> private PlanContext GetPlan( RecordingScheduler scheduler, DateTime referenceTime, Func<Guid, bool> disabled, int limit ) { // Create a new plan context var context = new PlanContext( m_started.Values ); // Configure it m_site.AddRegularJobs( scheduler, disabled, this, context ); // Enable all if (disabled == null) disabled = identifier => false; // Do the sort context.LoadPlan( scheduler.GetSchedules( referenceTime, m_tasks.Where( task => !disabled( task.UniqueIdentifier ) ).ToArray() ).Take( limit ) ); // Report return context; }
public void A_Recording_May_Be_Discarded_If_Overlapping_Occurs_On_Different_Groups() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test1", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 90 ) ); var plan2 = RecordingDefinition.Create( false, "test2", Guid.NewGuid(), null, SourceMock.Source1Group2Free, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 10 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { { FreeTVDevice }, { plan2 }, { plan1 }, }; // Resolve var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 2, schedules.Length, "Schedules" ); Assert.IsNull( schedules[0].Resource, "Resource 1" ); Assert.AreSame( FreeTVDevice, schedules[1].Resource, "Resource 2" ); Assert.AreSame( plan2, schedules[0].Definition, "Plan 1" ); Assert.AreSame( plan1, schedules[1].Definition, "Plan 2" ); Assert.IsFalse( schedules[0].StartsLate, "Late 1" ); Assert.IsFalse( schedules[1].StartsLate, "Late 2" ); }
public void Will_Detect_Recordings_On_Same_Source_As_One() { // Create recordings var plan1 = RecordingDefinition.Create( false, "testA", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddMinutes( 60 ), TimeSpan.FromMinutes( 90 ) ); var plan2 = RecordingDefinition.Create( false, "testB", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddMinutes( 90 ), TimeSpan.FromMinutes( 40 ) ); var plan3 = RecordingDefinition.Create( false, "testC", Guid.NewGuid(), null, SourceMock.Source2Group1Free, TimeBias.AddMinutes( 100 ), TimeSpan.FromMinutes( 40 ) ); // Load current var device = (ResourceMock) FreeTVDevice; var limit = device.SourceLimit; try { // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { device.SetSourceLimit( 2 ), plan1, plan2, plan3 }; // Resolve var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 3, schedules.Length, "Schedules" ); Assert.IsFalse( schedules[0].StartsLate, "Late 1" ); Assert.IsFalse( schedules[1].StartsLate, "Late 2" ); Assert.IsFalse( schedules[2].StartsLate, "Late 3" ); } finally { // Reset device.SetSourceLimit( limit ); } }
public void Restricted_Resource_Can_Not_Serve_Three_Records_At_A_Time() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 90 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source2Group1Free, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 70 ) ); var plan3 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source3Group1Free, TimeBias.AddHours( 2.25 ), TimeSpan.FromMinutes( 90 ) ); // Load current var device = (ResourceMock) FreeTVDevice; var limit = device.SourceLimit; try { // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { device.SetSourceLimit( 2 ), plan1, plan2, plan3 }; // Resolve var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 3, schedules.Length, "Schedules" ); Assert.IsFalse( schedules[0].StartsLate, "Late 1" ); Assert.IsFalse( schedules[1].StartsLate, "Late 2" ); Assert.IsTrue( schedules[2].StartsLate, "Late 3" ); } finally { // Reset device.SetSourceLimit( limit ); } }
public void Same_Source_Does_Not_Require_Additional_Decyrption_Slot() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Pay, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 100 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Pay, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 100 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { PayTVDevice1, plan1, plan2 }; // Process var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 2, schedules.Length, "Schedules" ); Assert.IsFalse( schedules[0].StartsLate, "Late 1" ); Assert.IsFalse( schedules[1].StartsLate, "Late 2" ); }
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" ); }
/// <summary> /// Ermittelt zu allen erlaubten Geräten die eindeutigen Kennungen. /// </summary> /// <param name="scheduler">Die zugehörige Planungsinstanz.</param> /// <returns>Gesetzt, wenn mindestens ein Gerät erlaubt ist.</returns> public bool PrepareResourceMap( RecordingScheduler scheduler ) { // Reset AllowedResources = null; // Check mode var resources = Definition.Resources; if (resources != null) if (resources.Length > 0) AllowedResources = Definition.Resources.Where( r => (r != null) && scheduler.Resources.Contains( r ) ).ToArray(); // Create full map if (AllowedResources == null) AllowedResources = scheduler.Resources.ToArray(); // Report return (AllowedResources.Length > 0); }
public void Decryption_Group_May_Allow_Using_Two_Resources_At_The_Same_Time() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Pay, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 100 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source4Group1Pay, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 100 ) ); // Create group var group = new DecryptionGroup { ScheduleResources = new[] { PayTVDevice1, PayTVDevice2 }, MaximumParallelSources = 2, }; // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { PayTVDevice1, PayTVDevice2, plan1, plan2, group }; // Process var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 2, schedules.Length, "Schedules" ); Assert.IsFalse( schedules[0].StartsLate, "Late 1" ); Assert.IsFalse( schedules[1].StartsLate, "Late 2" ); }
public void Two_Recordings_Of_The_Same_Group_Can_Be_Recorded_Simultanously() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 90 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source2Group1Free, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 60 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { { FreeTVDevice }, { plan1 }, { plan2 }, }; // Resolve var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 2, schedules.Length, "Schedules" ); Assert.AreSame( FreeTVDevice, schedules[0].Resource, "Resource 1" ); Assert.AreSame( FreeTVDevice, schedules[1].Resource, "Resource 2" ); Assert.AreSame( plan1, schedules[0].Definition, "Plan 1" ); Assert.AreSame( plan2, schedules[1].Definition, "Plan 2" ); Assert.IsFalse( schedules[0].StartsLate, "Late 1" ); Assert.IsFalse( schedules[1].StartsLate, "Late 2" ); }
public void A_Repeating_Recording_Can_Have_Exceptions() { // Create recordings var repeatStart = TimeBias.AddHours( 1 ); var repeatStartLocal = repeatStart.ToLocalTime(); var plan = RecordingDefinition.Create ( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Free, repeatStart, TimeSpan.FromMinutes( 90 ), DateTime.MaxValue.Date, Enum.GetValues( typeof( DayOfWeek ) ).Cast<DayOfWeek>().ToArray() ); var exception1 = new PlanException { ExceptionDate = repeatStartLocal.AddDays( 12 ).Date, DurationDelta = TimeSpan.FromMinutes( -10 ), StartDelta = TimeSpan.FromMinutes( 10 ), }; var exception2 = new PlanException { ExceptionDate = repeatStartLocal.AddDays( 22 ).Date, DurationDelta = TimeSpan.FromMinutes( 10 ), }; var exception3 = new PlanException { ExceptionDate = repeatStartLocal.AddDays( 24 ).Date, DurationDelta = TimeSpan.FromMinutes( -100 ), }; // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { { FreeTVDevice }, { plan, exception1, exception2, exception3 }, }; // Load var schedules = cut.GetSchedules( TimeBias ).Take( 30 ).ToArray(); // Check for (int i = 30; i-- > 0;) if (i == 12) { // Validate Assert.AreEqual( schedules[i].Time.Start, repeatStart.AddDays( i ).AddMinutes( 10 ), "Start {0}", i ); Assert.AreEqual( schedules[i].Time.Duration, TimeSpan.FromMinutes( 80 ), "Duration {0}", i ); } else if (i == 22) { // Validate Assert.AreEqual( schedules[i].Time.Start, repeatStart.AddDays( i ), "Start {0}", i ); Assert.AreEqual( schedules[i].Time.Duration, TimeSpan.FromMinutes( 100 ), "Duration {0}", i ); } else { // Correct var day = (i < 24) ? i : i + 1; // Validate Assert.AreEqual( schedules[i].Time.Start, repeatStart.AddDays( day ), "Start {0}", i ); Assert.AreEqual( schedules[i].Time.Duration, TimeSpan.FromMinutes( 90 ), "Duration {0}", i ); } }
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" ); }
public void Free_Recording_Must_Use_Best_Fit_Resource() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source5Group1Pay, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 100 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source4Group1Pay, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 100 ) ); var plan3 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group2Free, TimeBias.AddHours( 2 ).AddMinutes( 10 ), TimeSpan.FromMinutes( 100 ) ); // Attach to the device var device = (ResourceMock) PayTVDevice1; var prio = device.AbsolutePriority; // Must reset try { // Create component under test but make the device to choose the one with the least priority var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { device.SetPriority( -100 ), PayTVDevice2, plan1, plan2, plan3 }; // Process var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 3, schedules.Length, "Schedules" ); Assert.AreSame( device, schedules[2].Resource, "Resource" ); } finally { // Reset device.SetPriority( prio ); } }
public void May_Require_Three_Resources_On_Bad_Mixture_Of_Three_Recordings() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Pay, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 100 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source4Group1Pay, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 100 ) ); var plan3 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group2Free, TimeBias.AddHours( 2 ).AddMinutes( 10 ), TimeSpan.FromMinutes( 100 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { FreeTVDevice, PayTVDevice1, PayTVDevice2, plan1, plan2, plan3 }; // Process var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 3, schedules.Length, "Schedules" ); Assert.AreNotSame( FreeTVDevice, schedules[0].Resource, "Resource 1" ); Assert.AreNotSame( FreeTVDevice, schedules[1].Resource, "Resource 2" ); Assert.AreSame( FreeTVDevice, schedules[2].Resource, "Resource 3" ); }
public void A_Repeating_Recording_May_Be_Overlapped_By_A_Single_Recording() { // Create recordings var repeatStart = TimeBias.AddHours( 1 ); var repeatStartLocal = repeatStart.ToLocalTime(); var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Free, repeatStart, TimeSpan.FromMinutes( 90 ), DateTime.MaxValue.Date, repeatStartLocal.DayOfWeek ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group2Free, repeatStart.AddDays( 7 ).AddMinutes( -10 ), TimeSpan.FromMinutes( 60 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { FreeTVDevice, plan2, plan1, }; // Load first repeats var schedules = cut.GetSchedules( TimeBias ).Take( 3 ).ToArray(); // Validate Assert.AreEqual( 3, schedules.Length, "Schedules" ); Assert.AreSame( plan1, schedules[0].Definition, "Plan 1" ); Assert.AreSame( FreeTVDevice, schedules[0].Resource, "Resource 1" ); Assert.AreSame( plan2, schedules[1].Definition, "Plan 2" ); Assert.AreSame( FreeTVDevice, schedules[1].Resource, "Resource 2" ); Assert.AreEqual( repeatStart.AddDays( 7 ).AddMinutes( -10 ), schedules[1].Time.Start, "Start 2" ); Assert.AreEqual( TimeSpan.FromMinutes( 60 ), schedules[1].Time.Duration, "Duration 2" ); Assert.AreSame( plan1, schedules[2].Definition, "Plan 3" ); Assert.AreSame( FreeTVDevice, schedules[2].Resource, "Resource 3" ); Assert.IsTrue( schedules[2].StartsLate, "Late" ); Assert.AreEqual( repeatStart.AddDays( 7 ).AddMinutes( 50 ), schedules[2].Time.Start, "Start 3" ); Assert.AreEqual( TimeSpan.FromMinutes( 40 ), schedules[2].Time.Duration, "Duration 3" ); }
public void High_Priority_Device_Will_Be_Ignored_If_Not_Able_To_Decrypt() { // Create recordings var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Pay, TimeBias.AddHours( 1 ), TimeSpan.FromMinutes( 100 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source4Group1Pay, TimeBias.AddHours( 2 ), TimeSpan.FromMinutes( 10 ) ); // Attach to the free device var device = (ResourceMock) FreeTVDevice; var prio = device.AbsolutePriority; // With reset try { // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { device.SetPriority( 100 ), PayTVDevice1, PayTVDevice2, plan1, plan2 }; // Process var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 2, schedules.Length, "Schedules" ); Assert.IsNotNull( schedules[0].Resource, "Resource 1" ); Assert.AreNotSame( device, schedules[0].Resource, "Resource 1 Free" ); Assert.IsNotNull( schedules[1].Resource, "Resource 2" ); Assert.AreNotSame( device, schedules[1].Resource, "Resource 2 Free" ); Assert.AreNotSame( schedules[0].Resource, schedules[1].Resource, "Resources" ); } finally { // Back to normal device.SetPriority( prio ); } }
public void Will_Keep_Time_Order_When_Planning() { // Create recordings var plan1 = RecordingDefinition.Create( false, "testA", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddMinutes( 60 ), TimeSpan.FromMinutes( 60 ) ); var plan2 = RecordingDefinition.Create( false, "testB", Guid.NewGuid(), null, SourceMock.Source1Group2Free, TimeBias.AddMinutes( 90 ), TimeSpan.FromMinutes( 60 ) ); var plan3 = RecordingDefinition.Create( false, "testC", Guid.NewGuid(), null, SourceMock.Source2Group1Free, TimeBias.AddMinutes( 100 ), TimeSpan.FromMinutes( 100 ) ); // Create component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { FreeTVDevice, plan1, plan2, plan3 }; // Resolve var schedules = cut.GetSchedules( TimeBias ).ToArray(); // Validate Assert.AreEqual( 3, schedules.Length, "Schedules" ); Assert.AreEqual( "testA", schedules[0].Definition.Name, "Name 1" ); Assert.AreEqual( "testB", schedules[1].Definition.Name, "Name 2" ); Assert.AreEqual( "testC", schedules[2].Definition.Name, "Name 3" ); Assert.AreSame( FreeTVDevice, schedules[0].Resource, "Resource 1" ); Assert.AreSame( FreeTVDevice, schedules[1].Resource, "Resource 2" ); Assert.AreSame( FreeTVDevice, schedules[2].Resource, "Resource 3" ); Assert.IsFalse( schedules[0].StartsLate, "Late 1" ); Assert.IsTrue( schedules[1].StartsLate, "Late 2" ); Assert.IsTrue( schedules[2].StartsLate, "Late 3" ); }
public void Scheduler_Reports_Task_Times_If_No_Recording_Is_Available() { // Create the component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { TaskDevice, OtherDevice }; // Load some var schedules = cut.GetSchedules( TimeBias, Task ).Take( 100 ).ToArray(); // Validate Assert.AreEqual( 100, schedules.Length, "Schedules" ); Assert.AreEqual( TimeBias, schedules[0].Time.Start, "Start 0" ); Assert.AreEqual( TimeBias.ToLocalTime().Date.AddDays( 49 ).AddHours( 20 ).ToUniversalTime(), schedules[99].Time.Start, "Start 99" ); }
/// <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 Recording_Has_Priority_Over_Task() { // Create the recording var plan1 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddMinutes( 15 ), TimeSpan.FromMinutes( 80 ) ); var plan2 = RecordingDefinition.Create( false, "test", Guid.NewGuid(), null, SourceMock.Source1Group1Free, TimeBias.AddHours( 3 ), TimeSpan.FromMinutes( 100 ) ); // Create the component under test var cut = new RecordingScheduler( StringComparer.InvariantCultureIgnoreCase ) { TaskDevice, plan1, plan2 }; // Load some var schedules = cut.GetSchedules( TimeBias, Task ).Take( 5 ).ToArray(); // Validate Assert.AreEqual( 5, schedules.Length, "Schedules" ); Assert.AreSame( plan1, schedules[0].Definition, "Definition 1" ); Assert.IsFalse( schedules[0].StartsLate, "Late 1" ); Assert.AreSame( Task, schedules[1].Definition, "Definition 2" ); Assert.IsTrue( schedules[1].StartsLate, "Late 2" ); Assert.AreSame( Task, schedules[2].Definition, "Definition 3" ); Assert.IsFalse( schedules[2].StartsLate, "Late 3" ); Assert.AreEqual( TimeBias.ToLocalTime().Date.AddHours( 20 ).ToUniversalTime(), schedules[2].Time.Start, "Start 3" ); Assert.AreSame( plan2, schedules[3].Definition, "Definition 4" ); Assert.IsFalse( schedules[3].StartsLate, "Late 4" ); Assert.AreSame( Task, schedules[4].Definition, "Definition 5" ); Assert.IsFalse( schedules[4].StartsLate, "Late 5" ); Assert.AreEqual( TimeBias.ToLocalTime().Date.AddDays( 1 ).AddHours( 10 ).ToUniversalTime(), schedules[4].Time.Start, "Start 5" ); }