From 40f3a8663dc76534ae7b7cce916d13c4ee6cc394 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 17 Feb 2013 23:59:43 -0800 Subject: [PATCH] splited jobprovider into jobrepo, jobcontroller, moved to object db. --- NzbDrone.Api/Series/SeriesModule.cs | 4 +- NzbDrone.Core.Test/CentralDispatchFixture.cs | 6 +- NzbDrone.Core.Test/FluentTest.cs | 5 +- NzbDrone.Core.Test/Framework/CoreTest.cs | 20 +- NzbDrone.Core.Test/Framework/ObjectDbTest.cs | 19 +- .../JobTests/AppUpdateJobFixture.cs | 1 + .../JobTests/JobControllerFixture.cs | 228 +++++++++ .../JobTests/JobRepositoryFixture.cs | 161 ++++++ .../JobProviderTests => JobTests}/TestJobs.cs | 2 +- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 5 +- .../JobProviderTests/JobProviderFixture.cs | 463 ------------------ NzbDrone.Core/Datastore/BasicRepository.cs | 18 +- NzbDrone.Core/Datastore/ConnectionFactory.cs | 3 +- .../PetaPoco/EpisodeSeasonRelator.cs | 1 + .../{Jobs => Instrumentation}/TrimLogsJob.cs | 4 +- .../Jobs/{JobProvider.cs => JobController.cs} | 125 +---- .../{Repository => Jobs}/JobDefinition.cs | 13 +- NzbDrone.Core/Jobs/JobRepository.cs | 75 +++ .../{Jobs => Lifecycle}/AppRestartJob.cs | 3 +- .../{Jobs => Lifecycle}/AppShutdownJob.cs | 5 +- .../{Jobs => Lifecycle}/AppUpdateJob.cs | 3 +- NzbDrone.Core/Lifecycle/IInitilizable.cs | 7 + NzbDrone.Core/NzbDrone.Core.csproj | 14 +- .../Converting/AtomicParsleyProvider.cs | 2 +- .../Providers/Converting/HandbrakeProvider.cs | 2 +- .../Providers/Xbmc/ResourceManager.cs | 27 +- NzbDrone.Core/WebTimer.cs | 4 +- NzbDrone.Test.Common/TestBase.cs | 3 + NzbDrone.Web/Controllers/CommandController.cs | 4 +- NzbDrone.Web/Controllers/EpisodeController.cs | 4 +- NzbDrone.Web/Controllers/HistoryController.cs | 4 +- NzbDrone.Web/Controllers/SeriesController.cs | 4 +- .../Controllers/SettingsController.cs | 8 +- NzbDrone.Web/Controllers/SystemController.cs | 13 +- NzbDrone.Web/Controllers/UpdateController.cs | 5 +- NzbDrone.ncrunchsolution | 2 +- NzbDrone.sln.DotSettings | 2 + SqlCe/NzbDrone.SqlCe.dll | Bin 4096 -> 4096 bytes 38 files changed, 635 insertions(+), 634 deletions(-) create mode 100644 NzbDrone.Core.Test/JobTests/JobControllerFixture.cs create mode 100644 NzbDrone.Core.Test/JobTests/JobRepositoryFixture.cs rename NzbDrone.Core.Test/{ProviderTests/JobProviderTests => JobTests}/TestJobs.cs (95%) delete mode 100644 NzbDrone.Core.Test/ProviderTests/JobProviderTests/JobProviderFixture.cs rename NzbDrone.Core/{Jobs => Instrumentation}/TrimLogsJob.cs (90%) rename NzbDrone.Core/Jobs/{JobProvider.cs => JobController.cs} (64%) rename NzbDrone.Core/{Repository => Jobs}/JobDefinition.cs (57%) create mode 100644 NzbDrone.Core/Jobs/JobRepository.cs rename NzbDrone.Core/{Jobs => Lifecycle}/AppRestartJob.cs (93%) rename NzbDrone.Core/{Jobs => Lifecycle}/AppShutdownJob.cs (96%) rename NzbDrone.Core/{Jobs => Lifecycle}/AppUpdateJob.cs (98%) create mode 100644 NzbDrone.Core/Lifecycle/IInitilizable.cs diff --git a/NzbDrone.Api/Series/SeriesModule.cs b/NzbDrone.Api/Series/SeriesModule.cs index cdd7aae93..9b61b44aa 100644 --- a/NzbDrone.Api/Series/SeriesModule.cs +++ b/NzbDrone.Api/Series/SeriesModule.cs @@ -16,9 +16,9 @@ namespace NzbDrone.Api.Series public class SeriesModule : NzbDroneApiModule { private readonly SeriesProvider _seriesProvider; - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; - public SeriesModule(SeriesProvider seriesProvider, JobProvider jobProvider) + public SeriesModule(SeriesProvider seriesProvider, JobController jobProvider) : base("/Series") { _seriesProvider = seriesProvider; diff --git a/NzbDrone.Core.Test/CentralDispatchFixture.cs b/NzbDrone.Core.Test/CentralDispatchFixture.cs index 1972faa32..f36a4884a 100644 --- a/NzbDrone.Core.Test/CentralDispatchFixture.cs +++ b/NzbDrone.Core.Test/CentralDispatchFixture.cs @@ -104,7 +104,7 @@ public void All_metadata_clients_should_be_registered() [Test] public void jobs_are_initialized() { - kernel.Resolve().All().Should().HaveSameCount(jobs); + kernel.Resolve().All().Should().HaveSameCount(jobs); } [Test] @@ -134,8 +134,8 @@ public void quality_profile_initialized() [Test] public void JobProvider_should_be_singletone() { - var first = kernel.Resolve(); - var second = kernel.Resolve(); + var first = kernel.Resolve(); + var second = kernel.Resolve(); first.Should().BeSameAs(second); } diff --git a/NzbDrone.Core.Test/FluentTest.cs b/NzbDrone.Core.Test/FluentTest.cs index 5974ba814..c4439cc7e 100644 --- a/NzbDrone.Core.Test/FluentTest.cs +++ b/NzbDrone.Core.Test/FluentTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Test.Framework; @@ -215,7 +216,7 @@ public void Truncate_should_truncate_strings_to_max_specified_number_of_bytes() var resultString = str.Truncate(1000); //Resolve - var result = new System.Text.UTF8Encoding().GetBytes(resultString); + var result = new UTF8Encoding().GetBytes(resultString); result.Length.Should().BeLessOrEqualTo(1000); } @@ -229,7 +230,7 @@ public void Truncate_should_not_truncate_string_shorter_than_max_bytes() var resultString = str.Truncate(1000); //Resolve - var result = new System.Text.UTF8Encoding().GetBytes(resultString); + var result = new UTF8Encoding().GetBytes(resultString); result.Length.Should().Be(11); } diff --git a/NzbDrone.Core.Test/Framework/CoreTest.cs b/NzbDrone.Core.Test/Framework/CoreTest.cs index dac4b43db..3d8132e16 100644 --- a/NzbDrone.Core.Test/Framework/CoreTest.cs +++ b/NzbDrone.Core.Test/Framework/CoreTest.cs @@ -28,14 +28,28 @@ protected FileStream OpenRead(params string[] path) } } - public abstract class CoreTest : CoreTest + public abstract class CoreTest : CoreTest where TSubject: class { + private TSubject _subject; + [SetUp] public void CoreTestSetup() { - Subject = Mocker.Resolve(); + _subject = null; } - protected TSubject Subject { get; set; } + protected TSubject Subject + { + get + { + if (_subject == null) + { + _subject = Mocker.Resolve(); + } + + return _subject; + } + + } } } diff --git a/NzbDrone.Core.Test/Framework/ObjectDbTest.cs b/NzbDrone.Core.Test/Framework/ObjectDbTest.cs index 9fa6347c8..ab7c8977a 100644 --- a/NzbDrone.Core.Test/Framework/ObjectDbTest.cs +++ b/NzbDrone.Core.Test/Framework/ObjectDbTest.cs @@ -8,6 +8,23 @@ namespace NzbDrone.Core.Test.Framework { + + public abstract class RepositoryTest : ObjectDbTest + where TRepository : class, IBasicRepository + where TModel : ModelBase, new() + { + + protected BasicRepository Storage { get; private set; } + + [SetUp] + public void RepositoryTestSetup() + { + WithObjectDb(); + Storage = Mocker.Resolve>(); + } + + } + public abstract class ObjectDbTest : ObjectDbTest where TSubject : class { private TSubject _subject; @@ -56,7 +73,7 @@ protected void WithObjectDb(bool memory = true) //} //else //{ - _db = new SiaqoDbFactory(new DiskProvider(),new EnvironmentProvider()).Create(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Guid.NewGuid().ToString())); + _db = new SiaqoDbFactory(new DiskProvider(), new EnvironmentProvider()).Create(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Guid.NewGuid().ToString())); //} Mocker.SetConstant(Db); diff --git a/NzbDrone.Core.Test/JobTests/AppUpdateJobFixture.cs b/NzbDrone.Core.Test/JobTests/AppUpdateJobFixture.cs index a7f384dd7..884e73ebf 100644 --- a/NzbDrone.Core.Test/JobTests/AppUpdateJobFixture.cs +++ b/NzbDrone.Core.Test/JobTests/AppUpdateJobFixture.cs @@ -8,6 +8,7 @@ using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Core.Jobs; +using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; diff --git a/NzbDrone.Core.Test/JobTests/JobControllerFixture.cs b/NzbDrone.Core.Test/JobTests/JobControllerFixture.cs new file mode 100644 index 000000000..d16677d2c --- /dev/null +++ b/NzbDrone.Core.Test/JobTests/JobControllerFixture.cs @@ -0,0 +1,228 @@ +using System.Linq; +using System; +using System.Collections.Generic; +using System.Threading; +using FluentAssertions; +using Moq; +using NCrunch.Framework; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Jobs; +using NzbDrone.Core.Model; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.JobTests +{ + [TestFixture] + [ExclusivelyUses("JOB_PROVIDER")] + public class JobControllerFixture : CoreTest + { + + FakeJob _fakeJob; + SlowJob _slowJob; + BrokenJob _brokenJob; + DisabledJob _disabledJob; + + + private JobDefinition _updatedJob = null; + + [SetUp] + public void Setup() + { + _fakeJob = new FakeJob(); + _slowJob = new SlowJob(); + _brokenJob = new BrokenJob(); + _disabledJob = new DisabledJob(); + _updatedJob = null; + + IEnumerable jobs = new List { _fakeJob, _slowJob, _brokenJob, _disabledJob }; + + Mocker.SetConstant(jobs); + + Mocker.GetMock() + .Setup(c => c.Update(It.IsAny())) + .Callback(c => _updatedJob = c); + + + Mocker.GetMock() + .Setup(c => c.GetDefinition(It.IsAny())) + .Returns(new JobDefinition()); + } + + + private void GivenPendingJob(IList jobDefinition) + { + Mocker.GetMock().Setup(c => c.GetPendingJobs()).Returns(jobDefinition); + } + + [TearDown] + public void TearDown() + { + Subject.Queue.Should().BeEmpty(); + } + + private void WaitForQueue() + { + Console.WriteLine("Waiting for queue to clear."); + var stopWatch = Mocker.Resolve().StopWatch; + + while (stopWatch.IsRunning) + { + Thread.Sleep(10); + } + } + + [Test] + public void running_scheduled_jobs_should_updates_last_execution_time() + { + GivenPendingJob(new List { new JobDefinition { TypeName = _fakeJob.GetType().FullName } }); + + Subject.QueueScheduled(); + WaitForQueue(); + + _updatedJob.LastExecution.Should().BeWithin(TimeSpan.FromSeconds(10)); + _updatedJob.LastExecution.Should().BeWithin(TimeSpan.FromSeconds(10)); + _fakeJob.ExecutionCount.Should().Be(1); + } + + [Test] + public void failing_scheduled_job_should_mark_job_as_failed() + { + GivenPendingJob(new List { new JobDefinition { TypeName = _brokenJob.GetType().FullName } }); + + Subject.QueueScheduled(); + WaitForQueue(); + + _updatedJob.LastExecution.Should().BeWithin(TimeSpan.FromSeconds(10)); + _updatedJob.Success.Should().BeFalse(); + _brokenJob.ExecutionCount.Should().Be(1); + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void can_run_async_job_again() + { + Subject.QueueJob(typeof(FakeJob)); + WaitForQueue(); + Subject.QueueJob(typeof(FakeJob)); + WaitForQueue(); + + Subject.Queue.Should().BeEmpty(); + _fakeJob.ExecutionCount.Should().Be(2); + } + + [Test] + public void no_concurent_jobs() + { + Subject.QueueJob(typeof(SlowJob), 1); + Subject.QueueJob(typeof(SlowJob), 2); + Subject.QueueJob(typeof(SlowJob), 3); + + WaitForQueue(); + + Subject.Queue.Should().BeEmpty(); + _slowJob.ExecutionCount.Should().Be(3); + ExceptionVerification.AssertNoUnexcpectedLogs(); + } + + + [Test] + public void can_run_broken_job_again() + { + Subject.QueueJob(typeof(BrokenJob)); + WaitForQueue(); + + Subject.QueueJob(typeof(BrokenJob)); + WaitForQueue(); + + + _brokenJob.ExecutionCount.Should().Be(2); + ExceptionVerification.ExpectedErrors(2); + } + + [Test] + public void schedule_hit_should_be_ignored_if_queue_is_running() + { + Subject.QueueJob(typeof(SlowJob)); + Subject.QueueScheduled(); + WaitForQueue(); + + _slowJob.ExecutionCount.Should().Be(1); + _fakeJob.ExecutionCount.Should().Be(0); + } + + + [Test] + public void can_queue_jobs_at_the_same_time() + { + + Subject.QueueJob(typeof(SlowJob)); + var thread1 = new Thread(() => Subject.QueueJob(typeof(FakeJob))); + var thread2 = new Thread(() => Subject.QueueJob(typeof(FakeJob))); + + thread1.Start(); + thread2.Start(); + + thread1.Join(); + thread2.Join(); + + WaitForQueue(); + + _fakeJob.ExecutionCount.Should().Be(1); + _slowJob.ExecutionCount.Should().Be(1); + Subject.Queue.Should().BeEmpty(); + } + + + [Test] + public void job_with_specific_target_should_not_update_status() + { + Subject.QueueJob(typeof(FakeJob), 10); + + WaitForQueue(); + + Mocker.GetMock().Verify(c=>c.Update(It.IsAny()),Times.Never()); + _updatedJob.Should().BeNull(); + } + + + + [Test] + public void Item_added_to_queue_while_scheduler_runs_should_be_executed() + { + GivenPendingJob(new List { new JobDefinition { TypeName = _slowJob.GetType().FullName } }); + + var jobThread = new Thread(Subject.QueueScheduled); + jobThread.Start(); + + Thread.Sleep(200); + + Subject.QueueJob(typeof(DisabledJob), 12); + + WaitForQueue(); + + _slowJob.ExecutionCount.Should().Be(1); + _disabledJob.ExecutionCount.Should().Be(1); + } + + [Test] + public void trygin_to_queue_unregistered_job_should_fail() + { + Subject.QueueJob(typeof(UpdateInfoJob)); + WaitForQueue(); + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void scheduled_job_should_have_scheduler_as_source() + { + GivenPendingJob(new List { new JobDefinition { TypeName = _slowJob.GetType().FullName }, new JobDefinition { TypeName = _slowJob.GetType().FullName } }); + Subject.QueueScheduled(); + + Subject.Queue.Should().OnlyContain(c => c.Source == JobQueueItem.JobSourceType.Scheduler); + + WaitForQueue(); + } + } +} diff --git a/NzbDrone.Core.Test/JobTests/JobRepositoryFixture.cs b/NzbDrone.Core.Test/JobTests/JobRepositoryFixture.cs new file mode 100644 index 000000000..99c7686e4 --- /dev/null +++ b/NzbDrone.Core.Test/JobTests/JobRepositoryFixture.cs @@ -0,0 +1,161 @@ +// ReSharper disable RedundantUsingDirective + +using System.Linq; +using System; +using System.Collections.Generic; +using System.Threading; +using FizzWare.NBuilder; +using FluentAssertions; +using NCrunch.Framework; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Jobs; +using NzbDrone.Core.Model; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.JobTests +{ + [TestFixture] + public class JobRepositoryFixture : RepositoryTest + { + FakeJob _fakeJob; + DisabledJob _disabledJob; + + [SetUp] + public void Setup() + { + _fakeJob = new FakeJob(); + _disabledJob = new DisabledJob(); + } + + + [Test] + public void Init_should_add_defintaions() + { + IEnumerable baseFakeJobs = new List { _fakeJob }; + Mocker.SetConstant(baseFakeJobs); + + + Subject.Init(); + + Storage.All().Should().HaveCount(1); + Storage.All()[0].Interval.Should().Be((Int32)_fakeJob.DefaultInterval.TotalMinutes); + Storage.All()[0].Name.Should().Be(_fakeJob.Name); + Storage.All()[0].TypeName.Should().Be(_fakeJob.GetType().ToString()); + Storage.All()[0].LastExecution.Should().HaveYear(DateTime.Now.Year); + Storage.All()[0].LastExecution.Should().HaveMonth(DateTime.Now.Month); + Storage.All()[0].LastExecution.Should().HaveDay(DateTime.Today.Day); + Storage.All()[0].Enable.Should().BeTrue(); + } + + [Test] + public void inti_should_removed_jobs_that_no_longer_exist() + { + IEnumerable fakeJobs = new List { _fakeJob }; + Mocker.SetConstant(fakeJobs); + + Subject.Init(); + + var deletedJob = Builder.CreateNew() + .With(c => c.OID = 0) + .Build(); + + Db.Insert(deletedJob); + Subject.Init(); + + var registeredJobs = Storage.All(); + registeredJobs.Should().HaveCount(1); + registeredJobs.Should().NotContain(c => c.TypeName == deletedJob.TypeName); + } + + [Test] + public void inti_should_removed_jobs_that_no_longer_exist_even_with_same_name() + { + IEnumerable fakeJobs = new List { _fakeJob }; + Mocker.SetConstant(fakeJobs); + + Subject.Init(); + + var deletedJob = Builder.CreateNew() + .With(c => c.Name = _fakeJob.Name) + .With(c => c.OID = 0) + .Build(); + + + Db.Insert(deletedJob); + Subject.Init(); + + + + var registeredJobs = Storage.All(); + registeredJobs.Should().HaveCount(1); + registeredJobs.Should().NotContain(c => c.TypeName == deletedJob.TypeName); + } + + [Test] + public void init_should_update_existing_job() + { + + var oldJob = Builder.CreateNew() + .With(c => c.OID = 0) + .With(c => c.Name = "OldName") + .With(c => c.TypeName = typeof(FakeJob).ToString()) + .With(c => c.Interval = 0) + .With(c => c.Enable = true) + .With(c => c.Success = true) + .With(c => c.LastExecution = DateTime.Now.AddDays(-7).Date) + .Build(); + + Storage.Add(oldJob); + + var newJob = new FakeJob(); + + IEnumerable fakeJobs = new List { newJob }; + Mocker.SetConstant(fakeJobs); + + Subject.Init(); + + + var registeredJobs = Storage.All(); + registeredJobs.Should().HaveCount(1); + registeredJobs.First().TypeName.Should().Be(newJob.GetType().FullName); + registeredJobs.First().Name.Should().Be(newJob.Name); + registeredJobs.First().Interval.Should().Be((int)newJob.DefaultInterval.TotalMinutes); + + registeredJobs.First().Enable.Should().Be(true); + registeredJobs.First().Success.Should().Be(oldJob.Success); + registeredJobs.First().LastExecution.Should().Be(oldJob.LastExecution); + } + + [Test] + public void jobs_with_zero_interval_are_registered_as_disabled() + { + IEnumerable fakeJobs = new List { _disabledJob }; + Mocker.SetConstant(fakeJobs); + + Subject.Init(); + + + Storage.All().Should().HaveCount(1); + Storage.All().First().Enable.Should().BeFalse(); + } + +/* [Test] + public void disabled_jobs_arent_run_by_scheduler() + { + IEnumerable BaseFakeJobs = new List { disabledJob }; + Mocker.SetConstant(BaseFakeJobs); + + var jobProvider = Mocker.Resolve(); + jobProvider.QueueScheduled(); + + WaitForQueue(); + + + disabledJob.ExecutionCount.Should().Be(0); + }*/ + + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/JobProviderTests/TestJobs.cs b/NzbDrone.Core.Test/JobTests/TestJobs.cs similarity index 95% rename from NzbDrone.Core.Test/ProviderTests/JobProviderTests/TestJobs.cs rename to NzbDrone.Core.Test/JobTests/TestJobs.cs index 386ef6b73..8a4c12715 100644 --- a/NzbDrone.Core.Test/ProviderTests/JobProviderTests/TestJobs.cs +++ b/NzbDrone.Core.Test/JobTests/TestJobs.cs @@ -4,7 +4,7 @@ using NzbDrone.Core.Jobs; using NzbDrone.Core.Model.Notification; -namespace NzbDrone.Core.Test.ProviderTests.JobProviderTests +namespace NzbDrone.Core.Test.JobTests { public class FakeJob : IJob diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 469e2c784..2cc3236d4 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -141,6 +141,7 @@ + @@ -212,7 +213,7 @@ - + @@ -245,7 +246,7 @@ - + diff --git a/NzbDrone.Core.Test/ProviderTests/JobProviderTests/JobProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/JobProviderTests/JobProviderFixture.cs deleted file mode 100644 index 11bb2423d..000000000 --- a/NzbDrone.Core.Test/ProviderTests/JobProviderTests/JobProviderFixture.cs +++ /dev/null @@ -1,463 +0,0 @@ -// ReSharper disable RedundantUsingDirective - -using System.Linq; -using System; -using System.Collections.Generic; -using System.Threading; -using FizzWare.NBuilder; -using FluentAssertions; -using NCrunch.Framework; -using NUnit.Framework; -using NzbDrone.Common; -using NzbDrone.Core.Jobs; -using NzbDrone.Core.Model; -using NzbDrone.Core.Repository; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common; -using NzbDrone.Test.Common.AutoMoq; - -namespace NzbDrone.Core.Test.ProviderTests.JobProviderTests -{ - [TestFixture] - [ExclusivelyUses("JOB_PROVIDER")] - public class JobProviderFixture : SqlCeTest - { - - FakeJob fakeJob; - SlowJob slowJob; - BrokenJob brokenJob; - DisabledJob disabledJob; - - [SetUp] - public void Setup() - { - WithRealDb(); - fakeJob = new FakeJob(); - slowJob = new SlowJob(); - brokenJob = new BrokenJob(); - disabledJob = new DisabledJob(); - } - - [TearDown] - public void TearDown() - { - if(!EnvironmentProvider.IsMono) - { - Mocker.Resolve().Queue.Should().BeEmpty(); - } - } - - private void ResetLastExecution() - { - var jobProvider = Mocker.Resolve(); - - var jobs = jobProvider.All(); - foreach (var jobDefinition in jobs) - { - jobDefinition.LastExecution = new DateTime(2000, 1, 1); - jobProvider.SaveDefinition(jobDefinition); - } - } - - private void WaitForQueue() - { - Console.WriteLine("Waiting for queue to clear."); - var stopWatch = Mocker.Resolve().StopWatch; - - while (stopWatch.IsRunning) - { - Thread.Sleep(10); - } - } - - [Test] - public void running_scheduled_jobs_should_updates_last_execution_time() - { - IEnumerable BaseFakeJobs = new List { fakeJob }; - Mocker.SetConstant(BaseFakeJobs); - - - ResetLastExecution(); - Mocker.Resolve().QueueScheduled(); - WaitForQueue(); - - - var settings = Mocker.Resolve().All(); - settings.First().LastExecution.Should().BeWithin(TimeSpan.FromSeconds(10)); - fakeJob.ExecutionCount.Should().Be(1); - } - - [Test] - public void failing_scheduled_job_should_mark_job_as_failed() - { - IEnumerable BaseFakeJobs = new List { brokenJob }; - Mocker.SetConstant(BaseFakeJobs); - - - ResetLastExecution(); - Mocker.Resolve().QueueScheduled(); - WaitForQueue(); - - - var settings = Mocker.Resolve().All(); - settings.First().LastExecution.Should().BeWithin(TimeSpan.FromSeconds(10)); - settings.First().Success.Should().BeFalse(); - brokenJob.ExecutionCount.Should().Be(1); - ExceptionVerification.ExpectedErrors(1); - } - - [Test] - public void scheduler_skips_jobs_that_arent_mature_yet() - { - IEnumerable BaseFakeJobs = new List { fakeJob }; - Mocker.SetConstant(BaseFakeJobs); - - - ResetLastExecution(); - - Mocker.Resolve().QueueScheduled(); - WaitForQueue(); - - Mocker.Resolve().QueueScheduled(); - WaitForQueue(); - - - fakeJob.ExecutionCount.Should().Be(1); - } - - [Test] - //This test will confirm that the concurrency checks are rest - //after execution so the job can successfully run. - public void can_run_async_job_again() - { - IEnumerable BaseFakeJobs = new List { fakeJob }; - Mocker.SetConstant(BaseFakeJobs); - - var jobProvider = Mocker.Resolve(); - - - - jobProvider.QueueJob(typeof(FakeJob)); - WaitForQueue(); - jobProvider.QueueJob(typeof(FakeJob)); - WaitForQueue(); - - - jobProvider.Queue.Should().BeEmpty(); - fakeJob.ExecutionCount.Should().Be(2); - } - - [Test] - public void no_concurent_jobs() - { - IEnumerable BaseFakeJobs = new List { slowJob }; - Mocker.SetConstant(BaseFakeJobs); - - var jobProvider = Mocker.Resolve(); - jobProvider.QueueJob(typeof(SlowJob), 1); - jobProvider.QueueJob(typeof(SlowJob), 2); - jobProvider.QueueJob(typeof(SlowJob), 3); - - WaitForQueue(); - - jobProvider.Queue.Should().BeEmpty(); - slowJob.ExecutionCount.Should().Be(3); - ExceptionVerification.AssertNoUnexcpectedLogs(); - } - - - [Test] - public void can_run_broken_job_again() - { - IEnumerable BaseFakeJobs = new List { brokenJob }; - - Mocker.SetConstant(BaseFakeJobs); - - var jobProvider = Mocker.Resolve(); - - - jobProvider.QueueJob(typeof(BrokenJob)); - WaitForQueue(); - - jobProvider.QueueJob(typeof(BrokenJob)); - WaitForQueue(); - - - brokenJob.ExecutionCount.Should().Be(2); - ExceptionVerification.ExpectedErrors(2); - } - - [Test] - public void schedule_hit_should_be_ignored_if_queue_is_running() - { - IEnumerable fakeJobs = new List { slowJob, fakeJob }; - - Mocker.SetConstant(fakeJobs); - - var jobProvider = Mocker.Resolve(); - - - - jobProvider.QueueJob(typeof(SlowJob)); - jobProvider.QueueScheduled(); - WaitForQueue(); - - - slowJob.ExecutionCount.Should().Be(1); - fakeJob.ExecutionCount.Should().Be(0); - } - - - [Test] - public void can_queue_jobs_at_the_same_time() - { - IEnumerable BaseFakeJobs = new List { slowJob, fakeJob }; - Mocker.SetConstant(BaseFakeJobs); - - var jobProvider = Mocker.Resolve(); - - - jobProvider.QueueJob(typeof(SlowJob)); - var thread1 = new Thread(() => jobProvider.QueueJob(typeof(FakeJob))); - var thread2 = new Thread(() => jobProvider.QueueJob(typeof(FakeJob))); - - thread1.Start(); - thread2.Start(); - - thread1.Join(); - thread2.Join(); - - WaitForQueue(); - - fakeJob.ExecutionCount.Should().Be(1); - slowJob.ExecutionCount.Should().Be(1); - jobProvider.Queue.Should().BeEmpty(); - } - - [Test] - public void Init_Jobs() - { - IEnumerable BaseFakeJobs = new List { fakeJob }; - - Mocker.SetConstant(BaseFakeJobs); - - var jobProvider = Mocker.Resolve(); - - - - var timers = jobProvider.All(); - timers.Should().HaveCount(1); - timers[0].Interval.Should().Be((Int32)fakeJob.DefaultInterval.TotalMinutes); - timers[0].Name.Should().Be(fakeJob.Name); - timers[0].TypeName.Should().Be(fakeJob.GetType().ToString()); - timers[0].LastExecution.Should().HaveYear(DateTime.Now.Year); - timers[0].LastExecution.Should().HaveMonth(DateTime.Now.Month); - timers[0].LastExecution.Should().HaveDay(DateTime.Today.Day); - timers[0].Enable.Should().BeTrue(); - } - - [Test] - public void inti_should_removed_jobs_that_no_longer_exist() - { - IEnumerable fakeJobs = new List { fakeJob }; - Mocker.SetConstant(fakeJobs); - - WithRealDb(); - var deletedJob = Builder.CreateNew().Build(); - Db.Insert(deletedJob); - var jobProvider = Mocker.Resolve(); - - var registeredJobs = Db.Fetch(); - registeredJobs.Should().HaveCount(1); - registeredJobs.Should().NotContain(c => c.TypeName == deletedJob.TypeName); - } - - [Test] - public void inti_should_removed_jobs_that_no_longer_exist_even_with_same_name() - { - IEnumerable fakeJobs = new List { fakeJob }; - Mocker.SetConstant(fakeJobs); - - WithRealDb(); - var deletedJob = Builder.CreateNew() - .With(c => c.Name = fakeJob.Name).Build(); - - Db.Insert(deletedJob); - var jobProvider = Mocker.Resolve(); - - var registeredJobs = Db.Fetch(); - registeredJobs.Should().HaveCount(1); - registeredJobs.Should().NotContain(c => c.TypeName == deletedJob.TypeName); - } - - [Test] - public void init_should_update_existing_job() - { - IEnumerable fakeJobs = new List { fakeJob }; - Mocker.SetConstant(fakeJobs); - - WithRealDb(); - var initialFakeJob = Builder.CreateNew() - .With(c => c.Name = "NewName") - .With(c => c.TypeName = fakeJob.GetType().ToString()) - .With(c => c.Interval = 0) - .With(c => c.Enable = false) - .With(c => c.Success = true) - .With(c => c.LastExecution = DateTime.Now.AddDays(-7).Date) - .Build(); - - var id = Convert.ToInt32(Db.Insert(initialFakeJob)); - - Mocker.Resolve(); - - - - var registeredJobs = Db.Fetch(); - registeredJobs.Should().HaveCount(1); - registeredJobs.First().TypeName.Should().Be(fakeJob.GetType().ToString()); - registeredJobs.First().Name.Should().Be(fakeJob.Name); - registeredJobs.First().Interval.Should().Be((Int32)fakeJob.DefaultInterval.TotalMinutes); - - registeredJobs.First().Enable.Should().Be(true); - registeredJobs.First().Success.Should().Be(initialFakeJob.Success); - registeredJobs.First().LastExecution.Should().Be(initialFakeJob.LastExecution); - } - - [Test] - public void jobs_with_zero_interval_are_registered_as_disabled() - { - IEnumerable fakeJobs = new List { disabledJob }; - Mocker.SetConstant(fakeJobs); - - var jobProvider = Mocker.Resolve(); - - - jobProvider.All().Should().HaveCount(1); - jobProvider.All().First().Enable.Should().BeFalse(); - } - - [Test] - public void disabled_jobs_arent_run_by_scheduler() - { - IEnumerable BaseFakeJobs = new List { disabledJob }; - Mocker.SetConstant(BaseFakeJobs); - - var jobProvider = Mocker.Resolve(); - jobProvider.QueueScheduled(); - - WaitForQueue(); - - - disabledJob.ExecutionCount.Should().Be(0); - } - - [Test] - public void job_with_specific_target_should_not_update_last_execution() - { - IEnumerable BaseFakeJobs = new List { fakeJob }; - Mocker.SetConstant(BaseFakeJobs); - - - var jobProvider = Mocker.Resolve(); - ResetLastExecution(); - jobProvider.QueueJob(typeof(FakeJob), 10); - - WaitForQueue(); - - - jobProvider.All().First().LastExecution.Should().HaveYear(2000); - } - - [Test] - public void job_with_specific_target_should_not_set_success_flag() - { - IEnumerable BaseFakeJobs = new List { fakeJob }; - Mocker.SetConstant(BaseFakeJobs); - - - var jobProvider = Mocker.Resolve(); - jobProvider.QueueJob(typeof(FakeJob), 10); - - WaitForQueue(); - - - jobProvider.All().First().Success.Should().BeFalse(); - } - - - [Test] - public void duplicated_queue_item_should_start_queue_if_its_not_running() - { - IEnumerable BaseFakeJobs = new List { fakeJob }; - Mocker.SetConstant(BaseFakeJobs); - - var stuckQueueItem = new JobQueueItem - { - JobType = fakeJob.GetType(), - Options = new { TargetId = 12 } - }; - - - var jobProvider = Mocker.Resolve(); - jobProvider.Queue.Add(stuckQueueItem); - - WaitForQueue(); - jobProvider.QueueJob(stuckQueueItem.JobType, stuckQueueItem.Options); - WaitForQueue(); - - - fakeJob.ExecutionCount.Should().Be(1); - } - - - [Test] - public void Item_added_to_queue_while_scheduler_runs_should_be_executed() - { - IEnumerable BaseFakeJobs = new List { slowJob, disabledJob }; - Mocker.SetConstant(BaseFakeJobs); - - ResetLastExecution(); - var _jobThread = new Thread(Mocker.Resolve().QueueScheduled); - _jobThread.Start(); - - Thread.Sleep(200); - - Mocker.Resolve().QueueJob(typeof(DisabledJob), 12); - - WaitForQueue(); - - - slowJob.ExecutionCount.Should().Be(1); - disabledJob.ExecutionCount.Should().Be(1); - } - - [Test] - public void trygin_to_queue_unregistered_job_should_fail() - { - IEnumerable BaseFakeJobs = new List { slowJob, disabledJob }; - Mocker.SetConstant(BaseFakeJobs); - - var jobProvider = Mocker.Resolve(); - - jobProvider.QueueJob(typeof(string)); - - WaitForQueue(); - ExceptionVerification.ExpectedErrors(1); - } - - [Test] - public void scheduled_job_should_have_scheduler_as_source() - { - IEnumerable BaseFakeJobs = new List { slowJob, fakeJob}; - Mocker.SetConstant(BaseFakeJobs); - - var jobProvider = Mocker.Resolve(); - ResetLastExecution(); - jobProvider.QueueScheduled(); - - jobProvider.Queue.Should().OnlyContain(c => c.Source == JobQueueItem.JobSourceType.Scheduler); - - WaitForQueue(); - } - } -} diff --git a/NzbDrone.Core/Datastore/BasicRepository.cs b/NzbDrone.Core/Datastore/BasicRepository.cs index 97e71e88d..09cf677a9 100644 --- a/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/NzbDrone.Core/Datastore/BasicRepository.cs @@ -8,6 +8,8 @@ public interface IBasicRepository List All(); TModel Get(int id); TModel Add(TModel model); + TModel Update(TModel model); + TModel Upsert(TModel model); void Delete(int id); } @@ -18,7 +20,7 @@ public BasicRepository(IObjectDatabase objectDatabase) ObjectDatabase = objectDatabase; } - protected IObjectDatabase ObjectDatabase { get; private set; } + public IObjectDatabase ObjectDatabase { get; private set; } protected IEnumerable Queryable { get { return ObjectDatabase.AsQueryable(); } } @@ -37,6 +39,20 @@ public TModel Add(TModel model) return ObjectDatabase.Insert(model); } + public TModel Update(TModel model) + { + return ObjectDatabase.Update(model); + } + + public TModel Upsert(TModel model) + { + if(model.OID == 0) + { + return ObjectDatabase.Insert(model); + } + return ObjectDatabase.Update(model); + } + public void Delete(int id) { var itemToDelete = Get(id); diff --git a/NzbDrone.Core/Datastore/ConnectionFactory.cs b/NzbDrone.Core/Datastore/ConnectionFactory.cs index d68ffaaab..3cf62665c 100644 --- a/NzbDrone.Core/Datastore/ConnectionFactory.cs +++ b/NzbDrone.Core/Datastore/ConnectionFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Configuration; +using System.Data; using System.Data.Common; using System.Reflection; using NLog; @@ -21,7 +22,7 @@ static ConnectionFactory() if (EnvironmentProvider.IsMono) return; - var dataSet = (System.Data.DataSet)ConfigurationManager.GetSection("system.data"); + var dataSet = (DataSet)ConfigurationManager.GetSection("system.data"); dataSet.Tables[0].Rows.Add("Microsoft SQL Server Compact Data Provider 4.0" , "System.Data.SqlServerCe.4.0" , ".NET Framework Data Provider for Microsoft SQL Server Compact" diff --git a/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs b/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs index 462b1c11d..8c57c90b4 100644 --- a/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs +++ b/NzbDrone.Core/Datastore/PetaPoco/EpisodeSeasonRelator.cs @@ -2,6 +2,7 @@ using System.Linq; using NzbDrone.Core.Repository; +// ReSharper disable CheckNamespace namespace PetaPoco { public class EpisodeSeasonRelator diff --git a/NzbDrone.Core/Jobs/TrimLogsJob.cs b/NzbDrone.Core/Instrumentation/TrimLogsJob.cs similarity index 90% rename from NzbDrone.Core/Jobs/TrimLogsJob.cs rename to NzbDrone.Core/Instrumentation/TrimLogsJob.cs index 146e243dc..10e75ab25 100644 --- a/NzbDrone.Core/Jobs/TrimLogsJob.cs +++ b/NzbDrone.Core/Instrumentation/TrimLogsJob.cs @@ -1,9 +1,9 @@ using System; using System.Linq; -using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Jobs; using NzbDrone.Core.Model.Notification; -namespace NzbDrone.Core.Jobs +namespace NzbDrone.Core.Instrumentation { public class TrimLogsJob : IJob { diff --git a/NzbDrone.Core/Jobs/JobProvider.cs b/NzbDrone.Core/Jobs/JobController.cs similarity index 64% rename from NzbDrone.Core/Jobs/JobProvider.cs rename to NzbDrone.Core/Jobs/JobController.cs index 812f91e7c..d77d53d88 100644 --- a/NzbDrone.Core/Jobs/JobProvider.cs +++ b/NzbDrone.Core/Jobs/JobController.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Threading; @@ -8,21 +7,24 @@ using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; -using NzbDrone.Core.Repository; -using PetaPoco; namespace NzbDrone.Core.Jobs { - /// - /// Provides a background task runner, tasks could be queue either by the scheduler using QueueScheduled() - /// or by explicitly calling QueueJob(type,int) - /// - public class JobProvider + public interface IJobController + { + Stopwatch StopWatch { get; } + List Queue { get; } + void QueueScheduled(); + void QueueJob(Type jobType, dynamic options = null, JobQueueItem.JobSourceType source = JobQueueItem.JobSourceType.User); + bool QueueJob(string jobTypeString); + } + + public class JobController : IJobController { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - private readonly IDatabase _database; private readonly NotificationProvider _notificationProvider; private readonly IEnumerable _jobs; + private readonly IJobRepository _jobRepository; + private readonly Logger logger; private Thread _jobThread; public Stopwatch StopWatch { get; private set; } @@ -33,26 +35,16 @@ public class JobProvider private ProgressNotification _notification; - public JobProvider(IDatabase database, NotificationProvider notificationProvider, IEnumerable jobs) + public JobController(NotificationProvider notificationProvider, IEnumerable jobs, IJobRepository jobRepository, Logger logger) { StopWatch = new Stopwatch(); - _database = database; _notificationProvider = notificationProvider; _jobs = jobs; + _jobRepository = jobRepository; + this.logger = logger; ResetThread(); - Initialize(); } - /// - /// Initializes a new instance of the class. by AutoMoq - /// - /// Should only be used by AutoMoq - [EditorBrowsable(EditorBrowsableState.Never)] - public JobProvider() { } - - /// - /// Gets the active queue. - /// public List Queue { get @@ -61,62 +53,6 @@ public List Queue } } - public virtual List All() - { - return _database.Fetch().ToList(); - } - - private void Initialize() - { - var currentJobs = All(); - logger.Debug("Initializing jobs. Available: {0} Existing:{1}", _jobs.Count(), currentJobs.Count); - - foreach (var currentJob in currentJobs) - { - if (!_jobs.Any(c => c.GetType().ToString() == currentJob.TypeName)) - { - logger.Debug("Removing job from database '{0}'", currentJob.Name); - _database.Delete(currentJob); - } - } - - foreach (var job in _jobs) - { - var jobDefinition = currentJobs.SingleOrDefault(c => c.TypeName == job.GetType().ToString()); - - if (jobDefinition == null) - { - jobDefinition = new JobDefinition(); - jobDefinition.TypeName = job.GetType().ToString(); - jobDefinition.LastExecution = DateTime.Now; - } - - jobDefinition.Enable = job.DefaultInterval.TotalSeconds > 0; - jobDefinition.Name = job.Name; - - jobDefinition.Interval = Convert.ToInt32(job.DefaultInterval.TotalMinutes); - - SaveDefinition(jobDefinition); - } - } - - /// - /// Adds/Updates definitions for a job - /// - /// Settings to be added/updated - public virtual void SaveDefinition(JobDefinition definitions) - { - if (definitions.Id == 0) - { - logger.Trace("Adding job definitions for {0}", definitions.Name); - _database.Insert(definitions); - } - else - { - logger.Trace("Updating job definitions for {0}", definitions.Name); - _database.Update(definitions); - } - } public virtual void QueueScheduled() { @@ -131,14 +67,13 @@ public virtual void QueueScheduled() } } - var pendingJobTypes = All().Where( - t => t.Enable && - (DateTime.Now - t.LastExecution) > TimeSpan.FromMinutes(t.Interval) - ).Select(c => _jobs.Single(t => t.GetType().ToString() == c.TypeName).GetType()).ToList(); + var pendingJobs = _jobRepository.GetPendingJobs() + .Select(c => _jobs.Single(t => t.GetType().ToString() == c.TypeName) + .GetType()).ToList(); - pendingJobTypes.ForEach(jobType => QueueJob(jobType, source: JobQueueItem.JobSourceType.Scheduler)); - logger.Trace("{0} Scheduled tasks have been added to the queue", pendingJobTypes.Count); + pendingJobs.ForEach(jobType => QueueJob(jobType, source: JobQueueItem.JobSourceType.Scheduler)); + logger.Trace("{0} Scheduled tasks have been added to the queue", pendingJobs.Count); } public virtual void QueueJob(Type jobType, dynamic options = null, JobQueueItem.JobSourceType source = JobQueueItem.JobSourceType.User) @@ -193,11 +128,6 @@ public virtual bool QueueJob(string jobTypeString) return true; } - public virtual JobDefinition GetDefinition(Type type) - { - return _database.Single("WHERE TypeName = @0", type.ToString()); - } - private void ProcessQueue() { try @@ -261,13 +191,12 @@ private void Execute(JobQueueItem queueItem) return; } - var settings = All().Single(j => j.TypeName == queueItem.JobType.ToString()); - + var jobDefinition = _jobRepository.GetDefinition(queueItem.JobType); using (_notification = new ProgressNotification(jobImplementation.Name)) { try { - logger.Debug("Starting {0}. Last execution {1}", queueItem, settings.LastExecution); + logger.Debug("Starting {0}. Last execution {1}", queueItem, jobDefinition.LastExecution); var sw = Stopwatch.StartNew(); @@ -275,8 +204,8 @@ private void Execute(JobQueueItem queueItem) jobImplementation.Start(_notification, queueItem.Options); _notification.Status = ProgressNotificationStatus.Completed; - settings.LastExecution = DateTime.Now; - settings.Success = true; + jobDefinition.LastExecution = DateTime.Now; + jobDefinition.Success = true; sw.Stop(); logger.Debug("Job {0} successfully completed in {1:0}.{2} seconds.", queueItem, sw.Elapsed.TotalSeconds, sw.Elapsed.Milliseconds / 100, @@ -292,15 +221,15 @@ private void Execute(JobQueueItem queueItem) _notification.Status = ProgressNotificationStatus.Failed; _notification.CurrentMessage = jobImplementation.Name + " Failed."; - settings.LastExecution = DateTime.Now; - settings.Success = false; + jobDefinition.LastExecution = DateTime.Now; + jobDefinition.Success = false; } } //Only update last execution status if was triggered by the scheduler if (queueItem.Options == null) { - SaveDefinition(settings); + _jobRepository.Update(jobDefinition); } } diff --git a/NzbDrone.Core/Repository/JobDefinition.cs b/NzbDrone.Core/Jobs/JobDefinition.cs similarity index 57% rename from NzbDrone.Core/Repository/JobDefinition.cs rename to NzbDrone.Core/Jobs/JobDefinition.cs index 3eb1dcec9..056b655bf 100644 --- a/NzbDrone.Core/Repository/JobDefinition.cs +++ b/NzbDrone.Core/Jobs/JobDefinition.cs @@ -1,14 +1,11 @@ -using System; -using PetaPoco; +using System.Linq; +using System; +using NzbDrone.Core.Datastore; -namespace NzbDrone.Core.Repository +namespace NzbDrone.Core.Jobs { - [TableName("JobDefinitions")] - [PrimaryKey("Id", autoIncrement = true)] - public class JobDefinition + public class JobDefinition : ModelBase { - public Int32 Id { get; set; } - public Boolean Enable { get; set; } public String TypeName { get; set; } diff --git a/NzbDrone.Core/Jobs/JobRepository.cs b/NzbDrone.Core/Jobs/JobRepository.cs new file mode 100644 index 000000000..dfb58c830 --- /dev/null +++ b/NzbDrone.Core/Jobs/JobRepository.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NLog; +using NzbDrone.Core.Datastore; +using NzbDrone.Core.Lifecycle; + +namespace NzbDrone.Core.Jobs +{ + public interface IJobRepository : IInitializable, IBasicRepository + { + IList GetPendingJobs(); + JobDefinition GetDefinition(Type type); + } + + public class JobRepository : BasicRepository, IJobRepository + { + private readonly IEnumerable _jobs; + private readonly Logger _logger; + + public JobRepository(IObjectDatabase objectDatabase, IEnumerable jobs, Logger logger) + : base(objectDatabase) + { + _jobs = jobs; + _logger = logger; + } + + public JobDefinition GetDefinition(Type type) + { + return Queryable.Single(c => c.TypeName == type.FullName); + } + + + public IList GetPendingJobs() + { + return Queryable.Where(c => c.Enable && c.LastExecution < DateTime.UtcNow.AddMinutes(c.Interval)).ToList(); + } + + public void Init() + { + var currentJobs = All(); + _logger.Debug("Initializing jobs. Available: {0} Existing:{1}", _jobs.Count(), currentJobs.Count); + + foreach (var currentJob in currentJobs) + { + if (_jobs.All(c => c.GetType().ToString() != currentJob.TypeName)) + { + _logger.Debug("Removing job from database '{0}'", currentJob.Name); + Delete(currentJob.OID); + } + } + + foreach (var job in _jobs) + { + var jobDefinition = currentJobs.SingleOrDefault(c => c.TypeName == job.GetType().ToString()); + + if (jobDefinition == null) + { + jobDefinition = new JobDefinition + { + TypeName = job.GetType().ToString(), + LastExecution = DateTime.Now + }; + } + + jobDefinition.Enable = job.DefaultInterval.TotalSeconds > 0; + jobDefinition.Name = job.Name; + + jobDefinition.Interval = Convert.ToInt32(job.DefaultInterval.TotalMinutes); + + Upsert(jobDefinition); + } + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/AppRestartJob.cs b/NzbDrone.Core/Lifecycle/AppRestartJob.cs similarity index 93% rename from NzbDrone.Core/Jobs/AppRestartJob.cs rename to NzbDrone.Core/Lifecycle/AppRestartJob.cs index 2db8c3bb3..24d460358 100644 --- a/NzbDrone.Core/Jobs/AppRestartJob.cs +++ b/NzbDrone.Core/Lifecycle/AppRestartJob.cs @@ -2,9 +2,10 @@ using System.Linq; using NLog; using NzbDrone.Common; +using NzbDrone.Core.Jobs; using NzbDrone.Core.Model.Notification; -namespace NzbDrone.Core.Jobs +namespace NzbDrone.Core.Lifecycle { public class AppRestartJob : IJob { diff --git a/NzbDrone.Core/Jobs/AppShutdownJob.cs b/NzbDrone.Core/Lifecycle/AppShutdownJob.cs similarity index 96% rename from NzbDrone.Core/Jobs/AppShutdownJob.cs rename to NzbDrone.Core/Lifecycle/AppShutdownJob.cs index c35ae6893..62ef370cd 100644 --- a/NzbDrone.Core/Jobs/AppShutdownJob.cs +++ b/NzbDrone.Core/Lifecycle/AppShutdownJob.cs @@ -1,12 +1,11 @@ using System; using System.Linq; -using System.Diagnostics; -using System.IO; using NLog; using NzbDrone.Common; +using NzbDrone.Core.Jobs; using NzbDrone.Core.Model.Notification; -namespace NzbDrone.Core.Jobs +namespace NzbDrone.Core.Lifecycle { public class AppShutdownJob : IJob { diff --git a/NzbDrone.Core/Jobs/AppUpdateJob.cs b/NzbDrone.Core/Lifecycle/AppUpdateJob.cs similarity index 98% rename from NzbDrone.Core/Jobs/AppUpdateJob.cs rename to NzbDrone.Core/Lifecycle/AppUpdateJob.cs index c4512d373..3563754de 100644 --- a/NzbDrone.Core/Jobs/AppUpdateJob.cs +++ b/NzbDrone.Core/Lifecycle/AppUpdateJob.cs @@ -4,11 +4,12 @@ using System.IO; using NLog; using NzbDrone.Common; +using NzbDrone.Core.Jobs; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; -namespace NzbDrone.Core.Jobs +namespace NzbDrone.Core.Lifecycle { public class AppUpdateJob : IJob { diff --git a/NzbDrone.Core/Lifecycle/IInitilizable.cs b/NzbDrone.Core/Lifecycle/IInitilizable.cs new file mode 100644 index 000000000..ef32c13ef --- /dev/null +++ b/NzbDrone.Core/Lifecycle/IInitilizable.cs @@ -0,0 +1,7 @@ +namespace NzbDrone.Core.Lifecycle +{ + public interface IInitializable + { + void Init(); + } +} \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 73dc8d5c0..a85883f83 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -270,13 +270,15 @@ - - + + + + @@ -353,11 +355,11 @@ - - + + - + @@ -570,7 +572,7 @@ - + diff --git a/NzbDrone.Core/Providers/Converting/AtomicParsleyProvider.cs b/NzbDrone.Core/Providers/Converting/AtomicParsleyProvider.cs index ae99c9258..f5307e465 100644 --- a/NzbDrone.Core/Providers/Converting/AtomicParsleyProvider.cs +++ b/NzbDrone.Core/Providers/Converting/AtomicParsleyProvider.cs @@ -29,7 +29,7 @@ public virtual bool RunAtomicParsley(Episode episode, string outputFile) throw new NotImplementedException(); var atomicParsleyLocation = _configProvider.GetValue("AtomicParsleyLocation", ""); - var atomicParsleyTitleType = (AtomicParsleyTitleType) System.Convert.ToInt32(_configProvider.GetValue("AtomicParsley", 0)); + var atomicParsleyTitleType = (AtomicParsleyTitleType) Convert.ToInt32(_configProvider.GetValue("AtomicParsley", 0)); var atomicParsleyCommand = String.Format("\"{0}\" --overWrite --title \"{1}\" --genre \"TV Shows\" --stik \"TV Show\" --TVShowName \"{2}\" --TVEpisodeNum \"{3}\" --TVSeason \"{4}\"", outputFile, episode.Title, episode.Series.Title, episode.EpisodeNumber, episode.SeasonNumber); diff --git a/NzbDrone.Core/Providers/Converting/HandbrakeProvider.cs b/NzbDrone.Core/Providers/Converting/HandbrakeProvider.cs index 0379730ca..1a34b22f4 100644 --- a/NzbDrone.Core/Providers/Converting/HandbrakeProvider.cs +++ b/NzbDrone.Core/Providers/Converting/HandbrakeProvider.cs @@ -81,7 +81,7 @@ private void HandBrakeOutputDataReceived(object obj, DataReceivedEventArgs args) _currentEpisode.SeasonNumber, _currentEpisode.EpisodeNumber); - var percent = System.Convert.ToDecimal(match[0].Groups["percent"].Value); + var percent = Convert.ToDecimal(match[0].Groups["percent"].Value); int hours; int minutes; int seconds; diff --git a/NzbDrone.Core/Providers/Xbmc/ResourceManager.cs b/NzbDrone.Core/Providers/Xbmc/ResourceManager.cs index 7a9daefd2..ee717841d 100644 --- a/NzbDrone.Core/Providers/Xbmc/ResourceManager.cs +++ b/NzbDrone.Core/Providers/Xbmc/ResourceManager.cs @@ -1,18 +1,21 @@ -namespace NzbDrone.Core.Providers.Xbmc +using System.Drawing; +using System.IO; + +namespace NzbDrone.Core.Providers.Xbmc { public class ResourceManager { - public static System.Drawing.Icon GetIcon(string Name) + public static Icon GetIcon(string Name) { - System.IO.Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}.ico", Name)); + Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}.ico", Name)); if (stm == null) return null; - return new System.Drawing.Icon(stm); + return new Icon(stm); } public static byte[] GetRawData(string Name) { byte[] data; - using (System.IO.Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}.ico", Name))) + using (Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}.ico", Name))) { if (stm == null) return null; data = new byte[stm.Length]; @@ -25,7 +28,7 @@ public static byte[] GetRawData(string Name) public static byte[] GetRawLogo(string Name) { byte[] data; - using (System.IO.Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}", Name))) + using (Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}", Name))) { if (stm == null) return null; data = new byte[stm.Length]; @@ -35,15 +38,15 @@ public static byte[] GetRawLogo(string Name) return data; } - public static System.Drawing.Bitmap GetIconAsImage(string Name) + public static Bitmap GetIconAsImage(string Name) { - System.IO.Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}.ico", Name)); + Stream stm = typeof(ResourceManager).Assembly.GetManifestResourceStream(string.Format("NzbDrone.Core.{0}.ico", Name)); if (stm == null) return null; - System.Drawing.Bitmap bmp; - using (System.Drawing.Icon ico = new System.Drawing.Icon(stm)) + Bitmap bmp; + using (Icon ico = new Icon(stm)) { - bmp = new System.Drawing.Bitmap(ico.Width, ico.Height); - using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp)) + bmp = new Bitmap(ico.Width, ico.Height); + using (Graphics g = Graphics.FromImage(bmp)) { g.DrawIcon(ico, 0, 0); } diff --git a/NzbDrone.Core/WebTimer.cs b/NzbDrone.Core/WebTimer.cs index 6664e1a02..5dbf8d6d2 100644 --- a/NzbDrone.Core/WebTimer.cs +++ b/NzbDrone.Core/WebTimer.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core { public class WebTimer { - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; private static CacheItemRemovedCallback _onCacheRemove; private static bool _stop; @@ -17,7 +17,7 @@ public class WebTimer - public WebTimer(JobProvider jobProvider) + public WebTimer(JobController jobProvider) { _jobProvider = jobProvider; } diff --git a/NzbDrone.Test.Common/TestBase.cs b/NzbDrone.Test.Common/TestBase.cs index c3ec6a696..da7192230 100644 --- a/NzbDrone.Test.Common/TestBase.cs +++ b/NzbDrone.Test.Common/TestBase.cs @@ -2,6 +2,7 @@ using System.Linq; using System.IO; using Moq; +using NLog; using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Test.Common.AutoMoq; @@ -45,6 +46,8 @@ private string VirtualPath public void TestBaseSetup() { + Mocker.SetConstant(LogManager.GetLogger("TestLogger")); + TempFolder = Path.Combine(Directory.GetCurrentDirectory(), "_temp_" + DateTime.Now.Ticks); MockedRestProvider = new Mock(); diff --git a/NzbDrone.Web/Controllers/CommandController.cs b/NzbDrone.Web/Controllers/CommandController.cs index 1b9c7b11b..0813d1a7d 100644 --- a/NzbDrone.Web/Controllers/CommandController.cs +++ b/NzbDrone.Web/Controllers/CommandController.cs @@ -11,7 +11,7 @@ namespace NzbDrone.Web.Controllers { public class CommandController : Controller { - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; private readonly SabProvider _sabProvider; private readonly SmtpProvider _smtpProvider; private readonly TwitterProvider _twitterProvider; @@ -25,7 +25,7 @@ public class CommandController : Controller private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - public CommandController(JobProvider jobProvider, SabProvider sabProvider, + public CommandController(JobController jobProvider, SabProvider sabProvider, SmtpProvider smtpProvider, TwitterProvider twitterProvider, EpisodeProvider episodeProvider, GrowlProvider growlProvider, SeasonProvider seasonProvider, ProwlProvider prowlProvider, diff --git a/NzbDrone.Web/Controllers/EpisodeController.cs b/NzbDrone.Web/Controllers/EpisodeController.cs index 292c911ae..71579bb91 100644 --- a/NzbDrone.Web/Controllers/EpisodeController.cs +++ b/NzbDrone.Web/Controllers/EpisodeController.cs @@ -9,10 +9,10 @@ namespace NzbDrone.Web.Controllers { public class EpisodeController : Controller { - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; private readonly MediaFileProvider _mediaFileProvider; - public EpisodeController(JobProvider jobProvider, MediaFileProvider mediaFileProvider) + public EpisodeController(JobController jobProvider, MediaFileProvider mediaFileProvider) { _jobProvider = jobProvider; _mediaFileProvider = mediaFileProvider; diff --git a/NzbDrone.Web/Controllers/HistoryController.cs b/NzbDrone.Web/Controllers/HistoryController.cs index 7f37c4a1b..b708d882d 100644 --- a/NzbDrone.Web/Controllers/HistoryController.cs +++ b/NzbDrone.Web/Controllers/HistoryController.cs @@ -16,10 +16,10 @@ namespace NzbDrone.Web.Controllers public class HistoryController : Controller { private readonly HistoryProvider _historyProvider; - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; private readonly ConfigProvider _configProvider; - public HistoryController(HistoryProvider historyProvider, JobProvider jobProvider, + public HistoryController(HistoryProvider historyProvider, JobController jobProvider, ConfigProvider configProvider) { _historyProvider = historyProvider; diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index 3722217ec..9b2ba4b8d 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -24,14 +24,14 @@ public class SeriesController : Controller { private readonly QualityProvider _qualityProvider; private readonly SeriesProvider _seriesProvider; - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; private readonly SeasonProvider _seasonProvider; private readonly ConfigProvider _configProvider; // // GET: /Series/ public SeriesController(SeriesProvider seriesProvider, QualityProvider qualityProvider, - JobProvider jobProvider, SeasonProvider seasonProvider, + JobController jobProvider, SeasonProvider seasonProvider, ConfigProvider configProvider) { _seriesProvider = seriesProvider; diff --git a/NzbDrone.Web/Controllers/SettingsController.cs b/NzbDrone.Web/Controllers/SettingsController.cs index e2bb6a921..4a01ff48e 100644 --- a/NzbDrone.Web/Controllers/SettingsController.cs +++ b/NzbDrone.Web/Controllers/SettingsController.cs @@ -34,7 +34,7 @@ public class SettingsController : Controller private readonly ConfigFileProvider _configFileProvider; private readonly NewznabProvider _newznabProvider; private readonly MetadataProvider _metadataProvider; - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -43,7 +43,7 @@ public SettingsController(ConfigProvider configProvider, IndexerProvider indexer SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider, QualityTypeProvider qualityTypeProvider, ConfigFileProvider configFileProvider, NewznabProvider newznabProvider, MetadataProvider metadataProvider, - JobProvider jobProvider) + JobController jobProvider) { _externalNotificationProvider = externalNotificationProvider; _qualityTypeProvider = qualityTypeProvider; @@ -439,9 +439,9 @@ public JsonResult SaveIndexers(IndexerSettingsModel data) //Save the interval to config and immediately apply it the the job (to avoid a restart) _configProvider.RssSyncInterval = data.RssSyncInterval; - var rssSyncJob = _jobProvider.GetDefinition(typeof(RssSyncJob)); + /* var rssSyncJob = _jobProvider.GetDefinition(typeof(RssSyncJob)); rssSyncJob.Interval = data.RssSyncInterval; - _jobProvider.SaveDefinition(rssSyncJob); + _jobProvider.SaveDefinition(rssSyncJob);*/ try { diff --git a/NzbDrone.Web/Controllers/SystemController.cs b/NzbDrone.Web/Controllers/SystemController.cs index b7065e6b1..3c1cd6d3f 100644 --- a/NzbDrone.Web/Controllers/SystemController.cs +++ b/NzbDrone.Web/Controllers/SystemController.cs @@ -8,6 +8,7 @@ using NzbDrone.Core; using NzbDrone.Core.Helpers; using NzbDrone.Core.Jobs; +using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.DownloadClients; @@ -17,16 +18,17 @@ namespace NzbDrone.Web.Controllers { public class SystemController : Controller { - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; private readonly IndexerProvider _indexerProvider; private readonly ConfigProvider _configProvider; private readonly DiskProvider _diskProvider; private readonly BackupProvider _backupProvider; private readonly StatsProvider _statsProvider; + private readonly IJobRepository _jobRepository; - public SystemController(JobProvider jobProvider, IndexerProvider indexerProvider, + public SystemController(JobController jobProvider, IndexerProvider indexerProvider, ConfigProvider configProvider, DiskProvider diskProvider, - BackupProvider backupProvider, StatsProvider statsProvider) + BackupProvider backupProvider, StatsProvider statsProvider,IJobRepository jobRepository) { _jobProvider = jobProvider; _indexerProvider = indexerProvider; @@ -34,6 +36,7 @@ public SystemController(JobProvider jobProvider, IndexerProvider indexerProvider _diskProvider = diskProvider; _backupProvider = backupProvider; _statsProvider = statsProvider; + _jobRepository = jobRepository; } public ActionResult Index() @@ -58,9 +61,9 @@ public ActionResult Jobs() ViewData["Queue"] = serializedQueue; - var jobs = _jobProvider.All().Select(j => new JobModel + var jobs = _jobRepository.All().Select(j => new JobModel { - Id = j.Id, + Id = j.OID, Enable = j.Enable, TypeName = j.TypeName, Name = j.Name, diff --git a/NzbDrone.Web/Controllers/UpdateController.cs b/NzbDrone.Web/Controllers/UpdateController.cs index 5971690b5..5e67eb711 100644 --- a/NzbDrone.Web/Controllers/UpdateController.cs +++ b/NzbDrone.Web/Controllers/UpdateController.cs @@ -3,6 +3,7 @@ using System.Web.Mvc; using NzbDrone.Common; using NzbDrone.Core.Jobs; +using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Providers; using NzbDrone.Web.Models; @@ -11,11 +12,11 @@ namespace NzbDrone.Web.Controllers public class UpdateController : Controller { private readonly UpdateProvider _updateProvider; - private readonly JobProvider _jobProvider; + private readonly JobController _jobProvider; private readonly EnvironmentProvider _environmentProvider; private readonly DiskProvider _diskProvider; - public UpdateController(UpdateProvider updateProvider, JobProvider jobProvider, + public UpdateController(UpdateProvider updateProvider, JobController jobProvider, EnvironmentProvider environmentProvider, DiskProvider diskProvider) { _updateProvider = updateProvider; diff --git a/NzbDrone.ncrunchsolution b/NzbDrone.ncrunchsolution index e88d63cdb..13efc6321 100644 --- a/NzbDrone.ncrunchsolution +++ b/NzbDrone.ncrunchsolution @@ -1,6 +1,6 @@ 1 - False + True true true UseDynamicAnalysis diff --git a/NzbDrone.sln.DotSettings b/NzbDrone.sln.DotSettings index 52ac224d3..1bbc6428e 100644 --- a/NzbDrone.sln.DotSettings +++ b/NzbDrone.sln.DotSettings @@ -1,4 +1,6 @@  + ERROR + DO_NOT_SHOW <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy> BOTH_SIDES OUTLINE diff --git a/SqlCe/NzbDrone.SqlCe.dll b/SqlCe/NzbDrone.SqlCe.dll index 3ba22d8e7369adce6d0d05cde306b77a72b5747e..e97d5fce656f545a365b90919028ce1782e4cff7 100644 GIT binary patch delta 85 zcmZorXi%8Y!F1=~#x5f^0nIu35)1wpySMW!dVa6uuP)k2Qy`+;e`%b8PY%wiCL`7!(5;fPe{zWf&P4 m)~Et8I|D;duuHI%SIV@T7xn!%Z3y10dz9xj