splited jobprovider into jobrepo, jobcontroller, moved to object db.

pull/2/head
kay.one 12 years ago
parent 97398f604b
commit 40f3a8663d

@ -16,9 +16,9 @@ namespace NzbDrone.Api.Series
public class SeriesModule : NzbDroneApiModule public class SeriesModule : NzbDroneApiModule
{ {
private readonly SeriesProvider _seriesProvider; 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") : base("/Series")
{ {
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;

@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test
[Test] [Test]
public void jobs_are_initialized() public void jobs_are_initialized()
{ {
kernel.Resolve<JobProvider>().All().Should().HaveSameCount(jobs); kernel.Resolve<IJobRepository>().All().Should().HaveSameCount(jobs);
} }
[Test] [Test]
@ -134,8 +134,8 @@ namespace NzbDrone.Core.Test
[Test] [Test]
public void JobProvider_should_be_singletone() public void JobProvider_should_be_singletone()
{ {
var first = kernel.Resolve<JobProvider>(); var first = kernel.Resolve<JobController>();
var second = kernel.Resolve<JobProvider>(); var second = kernel.Resolve<JobController>();
first.Should().BeSameAs(second); first.Should().BeSameAs(second);
} }

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -215,7 +216,7 @@ namespace NzbDrone.Core.Test
var resultString = str.Truncate(1000); var resultString = str.Truncate(1000);
//Resolve //Resolve
var result = new System.Text.UTF8Encoding().GetBytes(resultString); var result = new UTF8Encoding().GetBytes(resultString);
result.Length.Should().BeLessOrEqualTo(1000); result.Length.Should().BeLessOrEqualTo(1000);
} }
@ -229,7 +230,7 @@ namespace NzbDrone.Core.Test
var resultString = str.Truncate(1000); var resultString = str.Truncate(1000);
//Resolve //Resolve
var result = new System.Text.UTF8Encoding().GetBytes(resultString); var result = new UTF8Encoding().GetBytes(resultString);
result.Length.Should().Be(11); result.Length.Should().Be(11);
} }

@ -28,14 +28,28 @@ namespace NzbDrone.Core.Test.Framework
} }
} }
public abstract class CoreTest<TSubject> : CoreTest public abstract class CoreTest<TSubject> : CoreTest where TSubject: class
{ {
private TSubject _subject;
[SetUp] [SetUp]
public void CoreTestSetup() public void CoreTestSetup()
{ {
Subject = Mocker.Resolve<TSubject>(); _subject = null;
} }
protected TSubject Subject { get; set; } protected TSubject Subject
{
get
{
if (_subject == null)
{
_subject = Mocker.Resolve<TSubject>();
}
return _subject;
}
}
} }
} }

@ -8,6 +8,23 @@ using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Test.Framework namespace NzbDrone.Core.Test.Framework
{ {
public abstract class RepositoryTest<TRepository, TModel> : ObjectDbTest<TRepository>
where TRepository : class, IBasicRepository<TModel>
where TModel : ModelBase, new()
{
protected BasicRepository<TModel> Storage { get; private set; }
[SetUp]
public void RepositoryTestSetup()
{
WithObjectDb();
Storage = Mocker.Resolve<BasicRepository<TModel>>();
}
}
public abstract class ObjectDbTest<TSubject> : ObjectDbTest where TSubject : class public abstract class ObjectDbTest<TSubject> : ObjectDbTest where TSubject : class
{ {
private TSubject _subject; private TSubject _subject;
@ -56,7 +73,7 @@ namespace NzbDrone.Core.Test.Framework
//} //}
//else //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); Mocker.SetConstant(Db);

@ -8,6 +8,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;

@ -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<JobController>
{
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<IJob> jobs = new List<IJob> { _fakeJob, _slowJob, _brokenJob, _disabledJob };
Mocker.SetConstant(jobs);
Mocker.GetMock<IJobRepository>()
.Setup(c => c.Update(It.IsAny<JobDefinition>()))
.Callback<JobDefinition>(c => _updatedJob = c);
Mocker.GetMock<IJobRepository>()
.Setup(c => c.GetDefinition(It.IsAny<Type>()))
.Returns(new JobDefinition());
}
private void GivenPendingJob(IList<JobDefinition> jobDefinition)
{
Mocker.GetMock<IJobRepository>().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<JobController>().StopWatch;
while (stopWatch.IsRunning)
{
Thread.Sleep(10);
}
}
[Test]
public void running_scheduled_jobs_should_updates_last_execution_time()
{
GivenPendingJob(new List<JobDefinition> { 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<JobDefinition> { 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<IJobRepository>().Verify(c=>c.Update(It.IsAny<JobDefinition>()),Times.Never());
_updatedJob.Should().BeNull();
}
[Test]
public void Item_added_to_queue_while_scheduler_runs_should_be_executed()
{
GivenPendingJob(new List<JobDefinition> { 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<JobDefinition> { 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();
}
}
}

@ -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<JobRepository, JobDefinition>
{
FakeJob _fakeJob;
DisabledJob _disabledJob;
[SetUp]
public void Setup()
{
_fakeJob = new FakeJob();
_disabledJob = new DisabledJob();
}
[Test]
public void Init_should_add_defintaions()
{
IEnumerable<IJob> baseFakeJobs = new List<IJob> { _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<IJob> fakeJobs = new List<IJob> { _fakeJob };
Mocker.SetConstant(fakeJobs);
Subject.Init();
var deletedJob = Builder<JobDefinition>.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<IJob> fakeJobs = new List<IJob> { _fakeJob };
Mocker.SetConstant(fakeJobs);
Subject.Init();
var deletedJob = Builder<JobDefinition>.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<JobDefinition>.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<IJob> fakeJobs = new List<IJob> { 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<IJob> fakeJobs = new List<IJob> { _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<IJob> BaseFakeJobs = new List<IJob> { disabledJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobController>();
jobProvider.QueueScheduled();
WaitForQueue();
disabledJob.ExecutionCount.Should().Be(0);
}*/
}
}

@ -4,7 +4,7 @@ using System.Threading;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Test.ProviderTests.JobProviderTests namespace NzbDrone.Core.Test.JobTests
{ {
public class FakeJob : IJob public class FakeJob : IJob

@ -141,6 +141,7 @@
<Compile Include="Framework\ObjectDbTest.cs" /> <Compile Include="Framework\ObjectDbTest.cs" />
<Compile Include="HelperTests\XElementHelperTests\ConvertToTFixture.cs" /> <Compile Include="HelperTests\XElementHelperTests\ConvertToTFixture.cs" />
<Compile Include="IndexerTests\NzbxFixture.cs" /> <Compile Include="IndexerTests\NzbxFixture.cs" />
<Compile Include="JobTests\JobRepositoryFixture.cs" />
<Compile Include="JobTests\RenameSeasonJobFixture.cs" /> <Compile Include="JobTests\RenameSeasonJobFixture.cs" />
<Compile Include="ProviderTests\DecisionEngineTests\LanguageSpecificationFixture.cs" /> <Compile Include="ProviderTests\DecisionEngineTests\LanguageSpecificationFixture.cs" />
<Compile Include="ProviderTests\DownloadClientTests\NzbgetProviderTests\DownloadNzbFixture.cs" /> <Compile Include="ProviderTests\DownloadClientTests\NzbgetProviderTests\DownloadNzbFixture.cs" />
@ -212,7 +213,7 @@
<Compile Include="ProviderTests\PostDownloadProviderTests\GetFolderNameWithStatusFixture.cs" /> <Compile Include="ProviderTests\PostDownloadProviderTests\GetFolderNameWithStatusFixture.cs" />
<Compile Include="JobTests\EpisodeSearchJobTest.cs" /> <Compile Include="JobTests\EpisodeSearchJobTest.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\ProcessDownloadFixture.cs" /> <Compile Include="ProviderTests\PostDownloadProviderTests\ProcessDownloadFixture.cs" />
<Compile Include="ProviderTests\JobProviderTests\TestJobs.cs" /> <Compile Include="JobTests\TestJobs.cs" />
<Compile Include="JobTests\AppUpdateJobFixture.cs" /> <Compile Include="JobTests\AppUpdateJobFixture.cs" />
<Compile Include="ProviderTests\UpdateProviderTests\GetUpdateLogFixture.cs" /> <Compile Include="ProviderTests\UpdateProviderTests\GetUpdateLogFixture.cs" />
<Compile Include="ProviderTests\UpdateProviderTests\GetAvilableUpdateFixture.cs" /> <Compile Include="ProviderTests\UpdateProviderTests\GetAvilableUpdateFixture.cs" />
@ -245,7 +246,7 @@
<Compile Include="JobTests\DiskScanJobTest.cs" /> <Compile Include="JobTests\DiskScanJobTest.cs" />
<Compile Include="IndexerTests\IndexerFixture.cs" /> <Compile Include="IndexerTests\IndexerFixture.cs" />
<Compile Include="ProviderTests\DecisionEngineTests\AllowedDownloadSpecificationFixture.cs" /> <Compile Include="ProviderTests\DecisionEngineTests\AllowedDownloadSpecificationFixture.cs" />
<Compile Include="ProviderTests\JobProviderTests\JobProviderFixture.cs" /> <Compile Include="JobTests\JobControllerFixture.cs" />
<Compile Include="QualityTest.cs" /> <Compile Include="QualityTest.cs" />
<Compile Include="RootFolderTests\RootFolderServiceFixture.cs" /> <Compile Include="RootFolderTests\RootFolderServiceFixture.cs" />
<Compile Include="ProviderTests\IndexerProviderTest.cs" /> <Compile Include="ProviderTests\IndexerProviderTest.cs" />

@ -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<JobProvider>().Queue.Should().BeEmpty();
}
}
private void ResetLastExecution()
{
var jobProvider = Mocker.Resolve<JobProvider>();
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<JobProvider>().StopWatch;
while (stopWatch.IsRunning)
{
Thread.Sleep(10);
}
}
[Test]
public void running_scheduled_jobs_should_updates_last_execution_time()
{
IEnumerable<IJob> BaseFakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(BaseFakeJobs);
ResetLastExecution();
Mocker.Resolve<JobProvider>().QueueScheduled();
WaitForQueue();
var settings = Mocker.Resolve<JobProvider>().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<IJob> BaseFakeJobs = new List<IJob> { brokenJob };
Mocker.SetConstant(BaseFakeJobs);
ResetLastExecution();
Mocker.Resolve<JobProvider>().QueueScheduled();
WaitForQueue();
var settings = Mocker.Resolve<JobProvider>().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<IJob> BaseFakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(BaseFakeJobs);
ResetLastExecution();
Mocker.Resolve<JobProvider>().QueueScheduled();
WaitForQueue();
Mocker.Resolve<JobProvider>().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<IJob> BaseFakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> BaseFakeJobs = new List<IJob> { slowJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> BaseFakeJobs = new List<IJob> { brokenJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> fakeJobs = new List<IJob> { slowJob, fakeJob };
Mocker.SetConstant(fakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> BaseFakeJobs = new List<IJob> { slowJob, fakeJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> BaseFakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> fakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(fakeJobs);
WithRealDb();
var deletedJob = Builder<JobDefinition>.CreateNew().Build();
Db.Insert(deletedJob);
var jobProvider = Mocker.Resolve<JobProvider>();
var registeredJobs = Db.Fetch<JobDefinition>();
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<IJob> fakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(fakeJobs);
WithRealDb();
var deletedJob = Builder<JobDefinition>.CreateNew()
.With(c => c.Name = fakeJob.Name).Build();
Db.Insert(deletedJob);
var jobProvider = Mocker.Resolve<JobProvider>();
var registeredJobs = Db.Fetch<JobDefinition>();
registeredJobs.Should().HaveCount(1);
registeredJobs.Should().NotContain(c => c.TypeName == deletedJob.TypeName);
}
[Test]
public void init_should_update_existing_job()
{
IEnumerable<IJob> fakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(fakeJobs);
WithRealDb();
var initialFakeJob = Builder<JobDefinition>.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<JobProvider>();
var registeredJobs = Db.Fetch<JobDefinition>();
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<IJob> fakeJobs = new List<IJob> { disabledJob };
Mocker.SetConstant(fakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
jobProvider.All().Should().HaveCount(1);
jobProvider.All().First().Enable.Should().BeFalse();
}
[Test]
public void disabled_jobs_arent_run_by_scheduler()
{
IEnumerable<IJob> BaseFakeJobs = new List<IJob> { disabledJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
jobProvider.QueueScheduled();
WaitForQueue();
disabledJob.ExecutionCount.Should().Be(0);
}
[Test]
public void job_with_specific_target_should_not_update_last_execution()
{
IEnumerable<IJob> BaseFakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> BaseFakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> BaseFakeJobs = new List<IJob> { fakeJob };
Mocker.SetConstant(BaseFakeJobs);
var stuckQueueItem = new JobQueueItem
{
JobType = fakeJob.GetType(),
Options = new { TargetId = 12 }
};
var jobProvider = Mocker.Resolve<JobProvider>();
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<IJob> BaseFakeJobs = new List<IJob> { slowJob, disabledJob };
Mocker.SetConstant(BaseFakeJobs);
ResetLastExecution();
var _jobThread = new Thread(Mocker.Resolve<JobProvider>().QueueScheduled);
_jobThread.Start();
Thread.Sleep(200);
Mocker.Resolve<JobProvider>().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<IJob> BaseFakeJobs = new List<IJob> { slowJob, disabledJob };
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
jobProvider.QueueJob(typeof(string));
WaitForQueue();
ExceptionVerification.ExpectedErrors(1);
}
[Test]
public void scheduled_job_should_have_scheduler_as_source()
{
IEnumerable<IJob> BaseFakeJobs = new List<IJob> { slowJob, fakeJob};
Mocker.SetConstant(BaseFakeJobs);
var jobProvider = Mocker.Resolve<JobProvider>();
ResetLastExecution();
jobProvider.QueueScheduled();
jobProvider.Queue.Should().OnlyContain(c => c.Source == JobQueueItem.JobSourceType.Scheduler);
WaitForQueue();
}
}
}

@ -8,6 +8,8 @@ namespace NzbDrone.Core.Datastore
List<TModel> All(); List<TModel> All();
TModel Get(int id); TModel Get(int id);
TModel Add(TModel model); TModel Add(TModel model);
TModel Update(TModel model);
TModel Upsert(TModel model);
void Delete(int id); void Delete(int id);
} }
@ -18,7 +20,7 @@ namespace NzbDrone.Core.Datastore
ObjectDatabase = objectDatabase; ObjectDatabase = objectDatabase;
} }
protected IObjectDatabase ObjectDatabase { get; private set; } public IObjectDatabase ObjectDatabase { get; private set; }
protected IEnumerable<TModel> Queryable { get { return ObjectDatabase.AsQueryable<TModel>(); } } protected IEnumerable<TModel> Queryable { get { return ObjectDatabase.AsQueryable<TModel>(); } }
@ -37,6 +39,20 @@ namespace NzbDrone.Core.Datastore
return ObjectDatabase.Insert(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) public void Delete(int id)
{ {
var itemToDelete = Get(id); var itemToDelete = Get(id);

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Configuration; using System.Configuration;
using System.Data;
using System.Data.Common; using System.Data.Common;
using System.Reflection; using System.Reflection;
using NLog; using NLog;
@ -21,7 +22,7 @@ namespace NzbDrone.Core.Datastore
if (EnvironmentProvider.IsMono) return; 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" dataSet.Tables[0].Rows.Add("Microsoft SQL Server Compact Data Provider 4.0"
, "System.Data.SqlServerCe.4.0" , "System.Data.SqlServerCe.4.0"
, ".NET Framework Data Provider for Microsoft SQL Server Compact" , ".NET Framework Data Provider for Microsoft SQL Server Compact"

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
// ReSharper disable CheckNamespace
namespace PetaPoco namespace PetaPoco
{ {
public class EpisodeSeasonRelator public class EpisodeSeasonRelator

@ -1,9 +1,9 @@
using System; using System;
using System.Linq; using System.Linq;
using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Jobs namespace NzbDrone.Core.Instrumentation
{ {
public class TrimLogsJob : IJob public class TrimLogsJob : IJob
{ {

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -8,21 +7,24 @@ using NLog;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using PetaPoco;
namespace NzbDrone.Core.Jobs namespace NzbDrone.Core.Jobs
{ {
/// <summary> public interface IJobController
/// Provides a background task runner, tasks could be queue either by the scheduler using QueueScheduled() {
/// or by explicitly calling QueueJob(type,int) Stopwatch StopWatch { get; }
/// </summary> List<JobQueueItem> Queue { get; }
public class JobProvider 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 NotificationProvider _notificationProvider;
private readonly IEnumerable<IJob> _jobs; private readonly IEnumerable<IJob> _jobs;
private readonly IJobRepository _jobRepository;
private readonly Logger logger;
private Thread _jobThread; private Thread _jobThread;
public Stopwatch StopWatch { get; private set; } public Stopwatch StopWatch { get; private set; }
@ -33,26 +35,16 @@ namespace NzbDrone.Core.Jobs
private ProgressNotification _notification; private ProgressNotification _notification;
public JobProvider(IDatabase database, NotificationProvider notificationProvider, IEnumerable<IJob> jobs) public JobController(NotificationProvider notificationProvider, IEnumerable<IJob> jobs, IJobRepository jobRepository, Logger logger)
{ {
StopWatch = new Stopwatch(); StopWatch = new Stopwatch();
_database = database;
_notificationProvider = notificationProvider; _notificationProvider = notificationProvider;
_jobs = jobs; _jobs = jobs;
_jobRepository = jobRepository;
this.logger = logger;
ResetThread(); ResetThread();
Initialize();
} }
/// <summary>
/// Initializes a new instance of the <see cref="JobProvider"/> class. by AutoMoq
/// </summary>
/// <remarks>Should only be used by AutoMoq</remarks>
[EditorBrowsable(EditorBrowsableState.Never)]
public JobProvider() { }
/// <summary>
/// Gets the active queue.
/// </summary>
public List<JobQueueItem> Queue public List<JobQueueItem> Queue
{ {
get get
@ -61,62 +53,6 @@ namespace NzbDrone.Core.Jobs
} }
} }
public virtual List<JobDefinition> All()
{
return _database.Fetch<JobDefinition>().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);
}
}
/// <summary>
/// Adds/Updates definitions for a job
/// </summary>
/// <param name="definitions">Settings to be added/updated</param>
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() public virtual void QueueScheduled()
{ {
@ -131,14 +67,13 @@ namespace NzbDrone.Core.Jobs
} }
} }
var pendingJobTypes = All().Where( var pendingJobs = _jobRepository.GetPendingJobs()
t => t.Enable && .Select(c => _jobs.Single(t => t.GetType().ToString() == c.TypeName)
(DateTime.Now - t.LastExecution) > TimeSpan.FromMinutes(t.Interval) .GetType()).ToList();
).Select(c => _jobs.Single(t => t.GetType().ToString() == c.TypeName).GetType()).ToList();
pendingJobTypes.ForEach(jobType => QueueJob(jobType, source: JobQueueItem.JobSourceType.Scheduler)); pendingJobs.ForEach(jobType => QueueJob(jobType, source: JobQueueItem.JobSourceType.Scheduler));
logger.Trace("{0} Scheduled tasks have been added to the queue", pendingJobTypes.Count); 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) public virtual void QueueJob(Type jobType, dynamic options = null, JobQueueItem.JobSourceType source = JobQueueItem.JobSourceType.User)
@ -193,11 +128,6 @@ namespace NzbDrone.Core.Jobs
return true; return true;
} }
public virtual JobDefinition GetDefinition(Type type)
{
return _database.Single<JobDefinition>("WHERE TypeName = @0", type.ToString());
}
private void ProcessQueue() private void ProcessQueue()
{ {
try try
@ -261,13 +191,12 @@ namespace NzbDrone.Core.Jobs
return; return;
} }
var settings = All().Single(j => j.TypeName == queueItem.JobType.ToString()); var jobDefinition = _jobRepository.GetDefinition(queueItem.JobType);
using (_notification = new ProgressNotification(jobImplementation.Name)) using (_notification = new ProgressNotification(jobImplementation.Name))
{ {
try 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(); var sw = Stopwatch.StartNew();
@ -275,8 +204,8 @@ namespace NzbDrone.Core.Jobs
jobImplementation.Start(_notification, queueItem.Options); jobImplementation.Start(_notification, queueItem.Options);
_notification.Status = ProgressNotificationStatus.Completed; _notification.Status = ProgressNotificationStatus.Completed;
settings.LastExecution = DateTime.Now; jobDefinition.LastExecution = DateTime.Now;
settings.Success = true; jobDefinition.Success = true;
sw.Stop(); sw.Stop();
logger.Debug("Job {0} successfully completed in {1:0}.{2} seconds.", queueItem, sw.Elapsed.TotalSeconds, sw.Elapsed.Milliseconds / 100, logger.Debug("Job {0} successfully completed in {1:0}.{2} seconds.", queueItem, sw.Elapsed.TotalSeconds, sw.Elapsed.Milliseconds / 100,
@ -292,15 +221,15 @@ namespace NzbDrone.Core.Jobs
_notification.Status = ProgressNotificationStatus.Failed; _notification.Status = ProgressNotificationStatus.Failed;
_notification.CurrentMessage = jobImplementation.Name + " Failed."; _notification.CurrentMessage = jobImplementation.Name + " Failed.";
settings.LastExecution = DateTime.Now; jobDefinition.LastExecution = DateTime.Now;
settings.Success = false; jobDefinition.Success = false;
} }
} }
//Only update last execution status if was triggered by the scheduler //Only update last execution status if was triggered by the scheduler
if (queueItem.Options == null) if (queueItem.Options == null)
{ {
SaveDefinition(settings); _jobRepository.Update(jobDefinition);
} }
} }

@ -1,14 +1,11 @@
using System; using System.Linq;
using PetaPoco; using System;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Repository namespace NzbDrone.Core.Jobs
{ {
[TableName("JobDefinitions")] public class JobDefinition : ModelBase
[PrimaryKey("Id", autoIncrement = true)]
public class JobDefinition
{ {
public Int32 Id { get; set; }
public Boolean Enable { get; set; } public Boolean Enable { get; set; }
public String TypeName { get; set; } public String TypeName { get; set; }

@ -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<JobDefinition>
{
IList<JobDefinition> GetPendingJobs();
JobDefinition GetDefinition(Type type);
}
public class JobRepository : BasicRepository<JobDefinition>, IJobRepository
{
private readonly IEnumerable<IJob> _jobs;
private readonly Logger _logger;
public JobRepository(IObjectDatabase objectDatabase, IEnumerable<IJob> jobs, Logger logger)
: base(objectDatabase)
{
_jobs = jobs;
_logger = logger;
}
public JobDefinition GetDefinition(Type type)
{
return Queryable.Single(c => c.TypeName == type.FullName);
}
public IList<JobDefinition> 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);
}
}
}
}

@ -2,9 +2,10 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Jobs namespace NzbDrone.Core.Lifecycle
{ {
public class AppRestartJob : IJob public class AppRestartJob : IJob
{ {

@ -1,12 +1,11 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Diagnostics;
using System.IO;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Jobs namespace NzbDrone.Core.Lifecycle
{ {
public class AppShutdownJob : IJob public class AppShutdownJob : IJob
{ {

@ -4,11 +4,12 @@ using System.Diagnostics;
using System.IO; using System.IO;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
namespace NzbDrone.Core.Jobs namespace NzbDrone.Core.Lifecycle
{ {
public class AppUpdateJob : IJob public class AppUpdateJob : IJob
{ {

@ -0,0 +1,7 @@
namespace NzbDrone.Core.Lifecycle
{
public interface IInitializable
{
void Init();
}
}

@ -270,13 +270,15 @@
<Compile Include="Helpers\XElementHelper.cs" /> <Compile Include="Helpers\XElementHelper.cs" />
<Compile Include="Instrumentation\LogInjectionModule.cs" /> <Compile Include="Instrumentation\LogInjectionModule.cs" />
<Compile Include="Jobs\CleanupRecycleBinJob.cs" /> <Compile Include="Jobs\CleanupRecycleBinJob.cs" />
<Compile Include="Jobs\AppShutdownJob.cs" /> <Compile Include="Jobs\JobRepository.cs" />
<Compile Include="Jobs\AppRestartJob.cs" /> <Compile Include="Lifecycle\AppShutdownJob.cs" />
<Compile Include="Lifecycle\AppRestartJob.cs" />
<Compile Include="Jobs\XemUpdateJob.cs" /> <Compile Include="Jobs\XemUpdateJob.cs" />
<Compile Include="Jobs\EmptyRecycleBinJob.cs" /> <Compile Include="Jobs\EmptyRecycleBinJob.cs" />
<Compile Include="Jobs\RefreshEpsiodeMetadata.cs" /> <Compile Include="Jobs\RefreshEpsiodeMetadata.cs" />
<Compile Include="Jobs\PastWeekBacklogSearchJob.cs" /> <Compile Include="Jobs\PastWeekBacklogSearchJob.cs" />
<Compile Include="Jobs\SearchHistoryCleanupJob.cs" /> <Compile Include="Jobs\SearchHistoryCleanupJob.cs" />
<Compile Include="Lifecycle\IInitilizable.cs" />
<Compile Include="Model\HistoryQueryModel.cs" /> <Compile Include="Model\HistoryQueryModel.cs" />
<Compile Include="Model\DownloadClientType.cs" /> <Compile Include="Model\DownloadClientType.cs" />
<Compile Include="Instrumentation\LogProvider.cs" /> <Compile Include="Instrumentation\LogProvider.cs" />
@ -353,11 +355,11 @@
<Compile Include="Providers\Search\SearchBase.cs" /> <Compile Include="Providers\Search\SearchBase.cs" />
<Compile Include="Providers\SeasonProvider.cs" /> <Compile Include="Providers\SeasonProvider.cs" />
<Compile Include="Jobs\RecentBacklogSearchJob.cs" /> <Compile Include="Jobs\RecentBacklogSearchJob.cs" />
<Compile Include="Jobs\TrimLogsJob.cs" /> <Compile Include="Instrumentation\TrimLogsJob.cs" />
<Compile Include="Jobs\JobProvider.cs" /> <Compile Include="Jobs\JobController.cs" />
<Compile Include="Jobs\RenameSeasonJob.cs" /> <Compile Include="Jobs\RenameSeasonJob.cs" />
<Compile Include="Jobs\RenameSeriesJob.cs" /> <Compile Include="Jobs\RenameSeriesJob.cs" />
<Compile Include="Jobs\AppUpdateJob.cs" /> <Compile Include="Lifecycle\AppUpdateJob.cs" />
<Compile Include="Jobs\BacklogSearchJob.cs" /> <Compile Include="Jobs\BacklogSearchJob.cs" />
<Compile Include="Jobs\BannerDownloadJob.cs" /> <Compile Include="Jobs\BannerDownloadJob.cs" />
<Compile Include="Jobs\ConvertEpisodeJob.cs" /> <Compile Include="Jobs\ConvertEpisodeJob.cs" />
@ -570,7 +572,7 @@
</Compile> </Compile>
<Compile Include="Repository\NewznabDefinition.cs" /> <Compile Include="Repository\NewznabDefinition.cs" />
<Compile Include="Repository\ExternalNotificationDefinition.cs" /> <Compile Include="Repository\ExternalNotificationDefinition.cs" />
<Compile Include="Repository\JobDefinition.cs" /> <Compile Include="Jobs\JobDefinition.cs" />
<Compile Include="Repository\IndexerDefinition.cs" /> <Compile Include="Repository\IndexerDefinition.cs" />
<Compile Include="Model\EpisodeParseResult.cs" /> <Compile Include="Model\EpisodeParseResult.cs" />
<Compile Include="Model\EpisodeSortingType.cs" /> <Compile Include="Model\EpisodeSortingType.cs" />

@ -29,7 +29,7 @@ namespace NzbDrone.Core.Providers.Converting
throw new NotImplementedException(); throw new NotImplementedException();
var atomicParsleyLocation = _configProvider.GetValue("AtomicParsleyLocation", ""); 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}\"", 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); outputFile, episode.Title, episode.Series.Title, episode.EpisodeNumber, episode.SeasonNumber);

@ -81,7 +81,7 @@ namespace NzbDrone.Core.Providers.Converting
_currentEpisode.SeasonNumber, _currentEpisode.SeasonNumber,
_currentEpisode.EpisodeNumber); _currentEpisode.EpisodeNumber);
var percent = System.Convert.ToDecimal(match[0].Groups["percent"].Value); var percent = Convert.ToDecimal(match[0].Groups["percent"].Value);
int hours; int hours;
int minutes; int minutes;
int seconds; int seconds;

@ -1,18 +1,21 @@
namespace NzbDrone.Core.Providers.Xbmc using System.Drawing;
using System.IO;
namespace NzbDrone.Core.Providers.Xbmc
{ {
public class ResourceManager 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; if (stm == null) return null;
return new System.Drawing.Icon(stm); return new Icon(stm);
} }
public static byte[] GetRawData(string Name) public static byte[] GetRawData(string Name)
{ {
byte[] data; 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; if (stm == null) return null;
data = new byte[stm.Length]; data = new byte[stm.Length];
@ -25,7 +28,7 @@
public static byte[] GetRawLogo(string Name) public static byte[] GetRawLogo(string Name)
{ {
byte[] data; 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; if (stm == null) return null;
data = new byte[stm.Length]; data = new byte[stm.Length];
@ -35,15 +38,15 @@
return data; 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; if (stm == null) return null;
System.Drawing.Bitmap bmp; Bitmap bmp;
using (System.Drawing.Icon ico = new System.Drawing.Icon(stm)) using (Icon ico = new Icon(stm))
{ {
bmp = new System.Drawing.Bitmap(ico.Width, ico.Height); bmp = new Bitmap(ico.Width, ico.Height);
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp)) using (Graphics g = Graphics.FromImage(bmp))
{ {
g.DrawIcon(ico, 0, 0); g.DrawIcon(ico, 0, 0);
} }

@ -8,7 +8,7 @@ namespace NzbDrone.Core
{ {
public class WebTimer public class WebTimer
{ {
private readonly JobProvider _jobProvider; private readonly JobController _jobProvider;
private static CacheItemRemovedCallback _onCacheRemove; private static CacheItemRemovedCallback _onCacheRemove;
private static bool _stop; private static bool _stop;
@ -17,7 +17,7 @@ namespace NzbDrone.Core
public WebTimer(JobProvider jobProvider) public WebTimer(JobController jobProvider)
{ {
_jobProvider = jobProvider; _jobProvider = jobProvider;
} }

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using Moq; using Moq;
using NLog;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common.AutoMoq;
@ -45,6 +46,8 @@ namespace NzbDrone.Test.Common
public void TestBaseSetup() public void TestBaseSetup()
{ {
Mocker.SetConstant(LogManager.GetLogger("TestLogger"));
TempFolder = Path.Combine(Directory.GetCurrentDirectory(), "_temp_" + DateTime.Now.Ticks); TempFolder = Path.Combine(Directory.GetCurrentDirectory(), "_temp_" + DateTime.Now.Ticks);
MockedRestProvider = new Mock<RestProvider>(); MockedRestProvider = new Mock<RestProvider>();

@ -11,7 +11,7 @@ namespace NzbDrone.Web.Controllers
{ {
public class CommandController : Controller public class CommandController : Controller
{ {
private readonly JobProvider _jobProvider; private readonly JobController _jobProvider;
private readonly SabProvider _sabProvider; private readonly SabProvider _sabProvider;
private readonly SmtpProvider _smtpProvider; private readonly SmtpProvider _smtpProvider;
private readonly TwitterProvider _twitterProvider; private readonly TwitterProvider _twitterProvider;
@ -25,7 +25,7 @@ namespace NzbDrone.Web.Controllers
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public CommandController(JobProvider jobProvider, SabProvider sabProvider, public CommandController(JobController jobProvider, SabProvider sabProvider,
SmtpProvider smtpProvider, TwitterProvider twitterProvider, SmtpProvider smtpProvider, TwitterProvider twitterProvider,
EpisodeProvider episodeProvider, GrowlProvider growlProvider, EpisodeProvider episodeProvider, GrowlProvider growlProvider,
SeasonProvider seasonProvider, ProwlProvider prowlProvider, SeasonProvider seasonProvider, ProwlProvider prowlProvider,

@ -9,10 +9,10 @@ namespace NzbDrone.Web.Controllers
{ {
public class EpisodeController : Controller public class EpisodeController : Controller
{ {
private readonly JobProvider _jobProvider; private readonly JobController _jobProvider;
private readonly MediaFileProvider _mediaFileProvider; private readonly MediaFileProvider _mediaFileProvider;
public EpisodeController(JobProvider jobProvider, MediaFileProvider mediaFileProvider) public EpisodeController(JobController jobProvider, MediaFileProvider mediaFileProvider)
{ {
_jobProvider = jobProvider; _jobProvider = jobProvider;
_mediaFileProvider = mediaFileProvider; _mediaFileProvider = mediaFileProvider;

@ -16,10 +16,10 @@ namespace NzbDrone.Web.Controllers
public class HistoryController : Controller public class HistoryController : Controller
{ {
private readonly HistoryProvider _historyProvider; private readonly HistoryProvider _historyProvider;
private readonly JobProvider _jobProvider; private readonly JobController _jobProvider;
private readonly ConfigProvider _configProvider; private readonly ConfigProvider _configProvider;
public HistoryController(HistoryProvider historyProvider, JobProvider jobProvider, public HistoryController(HistoryProvider historyProvider, JobController jobProvider,
ConfigProvider configProvider) ConfigProvider configProvider)
{ {
_historyProvider = historyProvider; _historyProvider = historyProvider;

@ -24,14 +24,14 @@ namespace NzbDrone.Web.Controllers
{ {
private readonly QualityProvider _qualityProvider; private readonly QualityProvider _qualityProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
private readonly JobProvider _jobProvider; private readonly JobController _jobProvider;
private readonly SeasonProvider _seasonProvider; private readonly SeasonProvider _seasonProvider;
private readonly ConfigProvider _configProvider; private readonly ConfigProvider _configProvider;
// //
// GET: /Series/ // GET: /Series/
public SeriesController(SeriesProvider seriesProvider, QualityProvider qualityProvider, public SeriesController(SeriesProvider seriesProvider, QualityProvider qualityProvider,
JobProvider jobProvider, SeasonProvider seasonProvider, JobController jobProvider, SeasonProvider seasonProvider,
ConfigProvider configProvider) ConfigProvider configProvider)
{ {
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;

@ -34,7 +34,7 @@ namespace NzbDrone.Web.Controllers
private readonly ConfigFileProvider _configFileProvider; private readonly ConfigFileProvider _configFileProvider;
private readonly NewznabProvider _newznabProvider; private readonly NewznabProvider _newznabProvider;
private readonly MetadataProvider _metadataProvider; private readonly MetadataProvider _metadataProvider;
private readonly JobProvider _jobProvider; private readonly JobController _jobProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@ -43,7 +43,7 @@ namespace NzbDrone.Web.Controllers
SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider,
QualityTypeProvider qualityTypeProvider, ConfigFileProvider configFileProvider, QualityTypeProvider qualityTypeProvider, ConfigFileProvider configFileProvider,
NewznabProvider newznabProvider, MetadataProvider metadataProvider, NewznabProvider newznabProvider, MetadataProvider metadataProvider,
JobProvider jobProvider) JobController jobProvider)
{ {
_externalNotificationProvider = externalNotificationProvider; _externalNotificationProvider = externalNotificationProvider;
_qualityTypeProvider = qualityTypeProvider; _qualityTypeProvider = qualityTypeProvider;
@ -439,9 +439,9 @@ namespace NzbDrone.Web.Controllers
//Save the interval to config and immediately apply it the the job (to avoid a restart) //Save the interval to config and immediately apply it the the job (to avoid a restart)
_configProvider.RssSyncInterval = data.RssSyncInterval; _configProvider.RssSyncInterval = data.RssSyncInterval;
var rssSyncJob = _jobProvider.GetDefinition(typeof(RssSyncJob)); /* var rssSyncJob = _jobProvider.GetDefinition(typeof(RssSyncJob));
rssSyncJob.Interval = data.RssSyncInterval; rssSyncJob.Interval = data.RssSyncInterval;
_jobProvider.SaveDefinition(rssSyncJob); _jobProvider.SaveDefinition(rssSyncJob);*/
try try
{ {

@ -8,6 +8,7 @@ using NzbDrone.Common;
using NzbDrone.Core; using NzbDrone.Core;
using NzbDrone.Core.Helpers; using NzbDrone.Core.Helpers;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DownloadClients; using NzbDrone.Core.Providers.DownloadClients;
@ -17,16 +18,17 @@ namespace NzbDrone.Web.Controllers
{ {
public class SystemController : Controller public class SystemController : Controller
{ {
private readonly JobProvider _jobProvider; private readonly JobController _jobProvider;
private readonly IndexerProvider _indexerProvider; private readonly IndexerProvider _indexerProvider;
private readonly ConfigProvider _configProvider; private readonly ConfigProvider _configProvider;
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
private readonly BackupProvider _backupProvider; private readonly BackupProvider _backupProvider;
private readonly StatsProvider _statsProvider; private readonly StatsProvider _statsProvider;
private readonly IJobRepository _jobRepository;
public SystemController(JobProvider jobProvider, IndexerProvider indexerProvider, public SystemController(JobController jobProvider, IndexerProvider indexerProvider,
ConfigProvider configProvider, DiskProvider diskProvider, ConfigProvider configProvider, DiskProvider diskProvider,
BackupProvider backupProvider, StatsProvider statsProvider) BackupProvider backupProvider, StatsProvider statsProvider,IJobRepository jobRepository)
{ {
_jobProvider = jobProvider; _jobProvider = jobProvider;
_indexerProvider = indexerProvider; _indexerProvider = indexerProvider;
@ -34,6 +36,7 @@ namespace NzbDrone.Web.Controllers
_diskProvider = diskProvider; _diskProvider = diskProvider;
_backupProvider = backupProvider; _backupProvider = backupProvider;
_statsProvider = statsProvider; _statsProvider = statsProvider;
_jobRepository = jobRepository;
} }
public ActionResult Index() public ActionResult Index()
@ -58,9 +61,9 @@ namespace NzbDrone.Web.Controllers
ViewData["Queue"] = serializedQueue; 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, Enable = j.Enable,
TypeName = j.TypeName, TypeName = j.TypeName,
Name = j.Name, Name = j.Name,

@ -3,6 +3,7 @@ using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Web.Models; using NzbDrone.Web.Models;
@ -11,11 +12,11 @@ namespace NzbDrone.Web.Controllers
public class UpdateController : Controller public class UpdateController : Controller
{ {
private readonly UpdateProvider _updateProvider; private readonly UpdateProvider _updateProvider;
private readonly JobProvider _jobProvider; private readonly JobController _jobProvider;
private readonly EnvironmentProvider _environmentProvider; private readonly EnvironmentProvider _environmentProvider;
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
public UpdateController(UpdateProvider updateProvider, JobProvider jobProvider, public UpdateController(UpdateProvider updateProvider, JobController jobProvider,
EnvironmentProvider environmentProvider, DiskProvider diskProvider) EnvironmentProvider environmentProvider, DiskProvider diskProvider)
{ {
_updateProvider = updateProvider; _updateProvider = updateProvider;

@ -1,6 +1,6 @@
<SolutionConfiguration> <SolutionConfiguration>
<FileVersion>1</FileVersion> <FileVersion>1</FileVersion>
<AutoEnableOnStartup>False</AutoEnableOnStartup> <AutoEnableOnStartup>True</AutoEnableOnStartup>
<AllowParallelTestExecution>true</AllowParallelTestExecution> <AllowParallelTestExecution>true</AllowParallelTestExecution>
<AllowTestsToRunInParallelWithThemselves>true</AllowTestsToRunInParallelWithThemselves> <AllowTestsToRunInParallelWithThemselves>true</AllowTestsToRunInParallelWithThemselves>
<FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit> <FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit>

@ -1,4 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfStatementToReturnStatement/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FPARAMETER/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FPARAMETER/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/Environment/Editor/MatchingBraceHighlighting/Position/@EntryValue">BOTH_SIDES</s:String> <s:String x:Key="/Default/Environment/Editor/MatchingBraceHighlighting/Position/@EntryValue">BOTH_SIDES</s:String>
<s:String x:Key="/Default/Environment/Editor/MatchingBraceHighlighting/Style/@EntryValue">OUTLINE</s:String> <s:String x:Key="/Default/Environment/Editor/MatchingBraceHighlighting/Style/@EntryValue">OUTLINE</s:String>

Binary file not shown.
Loading…
Cancel
Save