public void ScheduledJobShouldExecute()
        {
            JobScheduleElement element = new JobScheduleElement()
            {
                Name = "Test",
                RepeatHours = 24,
                StartOn = DateTime.Now.AddMilliseconds(-500)
            };

            Assert.IsTrue(ScheduledJob.ShouldExecute(element, 1000, DateTime.UtcNow));

            element.StartOn = DateTime.Now.AddMilliseconds(-1001);
            Assert.IsFalse(ScheduledJob.ShouldExecute(element, 1000, DateTime.UtcNow));

            element.StartOn = DateTime.Now.AddHours(1);
            Assert.IsFalse(ScheduledJob.ShouldExecute(element, 1000, DateTime.UtcNow));

            element.StartOn = DateTime.Now;
            Assert.IsTrue(ScheduledJob.ShouldExecute(element, 1000, DateTime.UtcNow));
        }
        /// <summary>
        /// Gets a value indicating whether the schedule identified by the given element is ready for execution
        /// given the provided heartbeat window and current date.
        /// </summary>
        /// <param name="element">The element to check.</param>
        /// <param name="heartbeat">The heartbeat window, in milliseconds.</param>
        /// <param name="now">The current date, in UTC.</param>
        /// <param name="executeOn">The concrete execution date, if the job should be executed.</param>
        /// <returns>True if the schedule should be executed now, false otherwise.</returns>
        public static bool ShouldExecute(JobScheduleElement element, long heartbeat, DateTime now, out DateTime? executeOn)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element", "element cannot be null");
            }

            if (element.RepeatHours <= 0)
            {
                throw new ConfigurationErrorsException("A job schedule's repeatHours must be greater than 0.", element.ElementInformation.Source, element.ElementInformation.LineNumber);
            }

            if (now.Kind != DateTimeKind.Utc)
            {
                throw new ArgumentException("now must be in UTC.", "now");
            }

            executeOn = null;
            bool shouldExecute = false;
            DateTime startOn = element.StartOn.ToUniversalTime();

            if (now >= startOn)
            {
                double milliseconds = now.Subtract(startOn).TotalMilliseconds;
                double repeatMilliseconds = element.RepeatHours * 3600000;

                if (repeatMilliseconds <= heartbeat)
                {
                    throw new ConfigurationErrorsException(String.Format(CultureInfo.InvariantCulture, "A job schedule's repeatHours must be greater than the job runner's heartbeat ({0} milliseconds).", heartbeat), element.ElementInformation.Source, element.ElementInformation.LineNumber);
                }

                int repeats = (int)Math.Floor(milliseconds / repeatMilliseconds);
                executeOn = startOn.AddMilliseconds(((repeats + 1) * repeatMilliseconds) - repeatMilliseconds);
                DateTime nextHeartbeat = now.AddMilliseconds(heartbeat);
                DateTime prevHeartbeat = now.AddMilliseconds(-1 * heartbeat);

                shouldExecute = executeOn >= prevHeartbeat && executeOn < nextHeartbeat;
            }

            return shouldExecute;
        }
        /// <summary>
        /// Creates a new scheduled job record for storing in the <see cref="IJobStore"/>.
        /// </summary>
        /// <param name="scheduleElement">The configured schedule element to create the record for.</param>
        /// <param name="jobElement">The configured job element to create the record for.</param>
        /// <param name="now">The queue and start date to create the record for.</param>
        /// <returns>A new scheduled job record.</returns>
        public static JobRecord CreateRecord(JobScheduleElement scheduleElement, JobScheduledJobElement jobElement, DateTime now)
        {
            if (scheduleElement == null)
            {
                throw new ArgumentNullException("scheduleElement", "scheduleElement cannot be null.");
            }

            if (String.IsNullOrEmpty(scheduleElement.Name))
            {
                throw new ArgumentException("scheduleElement cannot have an empty Name.", "scheduleElement");
            }

            if (jobElement == null)
            {
                throw new ArgumentNullException("jobElement", "jobElement cannot be null.");
            }

            if (String.IsNullOrEmpty(jobElement.JobType))
            {
                throw new ArgumentException("jobElement cannot have an empty JobType.", "jobElement");
            }

            if (now.Kind != DateTimeKind.Utc)
            {
                throw new ArgumentException("now must be in UTC.", "now");
            }

            return new JobRecord()
            {
                Name = scheduleElement.Name,
                JobType = jobElement.JobType,
                QueueDate = now,
                ScheduleName = scheduleElement.Name,
                StartDate = now,
                Status = JobStatus.Started
            };
        }
 /// <summary>
 /// Gets a value indicating whether the schedule identified by the given element is ready for execution
 /// given the provided heartbeat window and current date.
 /// </summary>
 /// <param name="element">The element to check.</param>
 /// <param name="heartbeat">The heartbeat window, in milliseconds.</param>
 /// <param name="now">The current date, in UTC.</param>
 /// <returns>True if the schedule should be executed now, false otherwise.</returns>
 public static bool ShouldExecute(JobScheduleElement element, long heartbeat, DateTime now)
 {
     DateTime? executeOn;
     return ShouldExecute(element, heartbeat, now, out executeOn);
 }
        public void JobRunnerExecuteScheduledJobs()
        {
            JobScheduleElement sched1 = new JobScheduleElement()
            {
                Name = "___TEST_SCHED_1___" + Guid.NewGuid().ToString(),
                RepeatHours = 24,
                StartOn = DateTime.UtcNow.AddYears(-1).AddMilliseconds(Heartbeat)
            };

            JobScheduleElement sched2 = new JobScheduleElement()
            {
                Name = "___TEST_SCHED_2___" + Guid.NewGuid().ToString(),
                RepeatHours = .5,
                StartOn = DateTime.UtcNow.AddDays(-1).AddMilliseconds(Heartbeat)
            };

            JobScheduleElement sched3 = new JobScheduleElement()
            {
                Name = "___TEST_SCHED_3___" + Guid.NewGuid().ToString(),
                RepeatHours = .5,
                StartOn = DateTime.UtcNow.AddDays(1).AddMilliseconds(Heartbeat)
            };

            JobScheduledJobElement job1 = new JobScheduledJobElement()
            {
                JobType = JobRecord.JobTypeString(typeof(TestIdJob))
            };

            JobScheduledJobElement job2 = new JobScheduledJobElement()
            {
                JobType = JobRecord.JobTypeString(typeof(TestScheduledJob))
            };

            sched1.ScheduledJobs.Add(job1);
            sched1.ScheduledJobs.Add(job2);
            sched2.ScheduledJobs.Add(job2);
            sched3.ScheduledJobs.Add(job1);

            jobRunner.SetSchedules(new JobScheduleElement[] { sched1, sched2, sched3 });
            Thread.Sleep(Heartbeat * 2);

            Assert.AreEqual(2, jobStore.GetJobCount(null, null, sched1.Name));
            Assert.AreEqual(1, jobStore.GetJobCount(null, null, sched2.Name));
            Assert.AreEqual(0, jobStore.GetJobCount(null, null, sched3.Name));
        }