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)));
        }
Exemple #2
0
        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);
        }
Exemple #4
0
        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));
        }
Exemple #5
0
        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);
        }
Exemple #8
0
        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");
        }