Merge branch 'master' of git://github.com/kayone/NzbDrone

This commit is contained in:
Mark McDowall 2011-06-04 23:56:17 -07:00
commit 320cf0b217
11 changed files with 1139 additions and 903 deletions

View File

@ -13,7 +13,7 @@ namespace Migrator.Framework
/// <summary>
/// Get this provider or a NoOp provider if you are not running in the context of 'provider'.
/// </summary>
ITransformationProvider this[string provider] { get;}
ITransformationProvider this[string provider] { get; }
/// <summary>
/// The list of Migrations currently applied to the database.
@ -194,6 +194,15 @@ namespace Migrator.Framework
/// <param name="constraint"></param>
void GenerateForeignKey(string foreignTable, string primaryTable, ForeignKeyConstraint constraint);
/// <summary>
/// Add an Index to a table
/// </summary>
/// <param name="name">The name of the index to add.</param>
/// <param name="table">The name of the table that will get the index.</param>
/// <param name="unique">If the index will be unique</param>
/// <param name="columns">The name of the column or columns that are in the index.</param>
void AddIndex(string name, string table, bool unique, params string[] columns);
/// <summary>
/// Add a primary key to a table
/// </summary>
@ -376,6 +385,13 @@ namespace Migrator.Framework
/// <param name="name">The name of the constraint to remove</param>
void RemoveConstraint(string table, string name);
/// <summary>
/// Remove an existing index
/// </summary>
/// <param name="table">The table that contains the index.</param>
/// <param name="name">The name of the index to remove</param>
void RemoveIndex(string table, string name);
/// <summary>
/// Remove an existing table
/// </summary>

View File

@ -1,3 +1,4 @@
using System;
using System.Data;
using Migrator.Framework;
using ForeignKeyConstraint=Migrator.Framework.ForeignKeyConstraint;
@ -54,6 +55,11 @@ namespace Migrator.Providers
// No Op
}
public void RemoveIndex(string table, string name)
{
// No Op
}
public void AddTable(string name, params Column[] columns)
{
// No Op
@ -129,6 +135,16 @@ namespace Migrator.Providers
// No Op
}
public void AddIndex(string name, string table, params string[] columns)
{
//No Op
}
public void AddIndex(string name, string table, bool unique, params string[] columns)
{
//No Op
}
public void AddPrimaryKey(string name, string table, params string[] columns)
{
// No Op

View File

@ -134,6 +134,11 @@ namespace Migrator.Providers
}
}
public void RemoveIndex(string table, string name)
{
throw new NotImplementedException();
}
public virtual void AddTable(string table, string engine, string columns)
{
table = _dialect.TableNameNeedsQuote ? _dialect.Quote(table) : table;
@ -409,6 +414,23 @@ namespace Migrator.Providers
AddColumn(table, column, type, size, property, null);
}
public void AddIndex(string name, string table, bool unique, params string[] columns)
{
try
{
var uniqueText = "";
if (unique) uniqueText = "UNIQUE";
var command = String.Format("CREATE {0} INDEX {1} ON {2} ({3})", uniqueText, name, table, String.Join(",", columns));
ExecuteNonQuery(command);
}
catch (Exception e)
{
Logger.Exception("Unable to add index", e);
}
}
/// <summary>
/// Append a primary key to a table.
/// </summary>
@ -562,7 +584,8 @@ namespace Migrator.Providers
Logger.Warn(ex.Message);
throw;
}
} }
}
}
private IDbCommand BuildCommand(string sql)
{
@ -595,7 +618,8 @@ namespace Migrator.Providers
Logger.Warn("query failed: {0}", cmd.CommandText);
throw;
}
} }
}
}
public object ExecuteScalar(string sql)
{
@ -611,7 +635,8 @@ namespace Migrator.Providers
Logger.Warn("Query failed: {0}", cmd.CommandText);
throw;
}
} }
}
}
public IDataReader Select(string what, string from)
{
@ -658,7 +683,7 @@ namespace Migrator.Providers
public virtual int Delete(string table)
{
return Delete(table, (string[])null, (string[]) null);
return Delete(table, (string[])null, (string[])null);
}
public virtual int Delete(string table, string[] columns, string[] values)
@ -743,12 +768,14 @@ namespace Migrator.Providers
{
get
{
if(_appliedMigrations == null)
if (_appliedMigrations == null)
{
_appliedMigrations = new List<long>();
CreateSchemaInfoTable();
using(IDataReader reader = Select("version","SchemaInfo")){
while(reader.Read()){
using (IDataReader reader = Select("version", "SchemaInfo"))
{
while (reader.Read())
{
_appliedMigrations.Add(Convert.ToInt64(reader.GetValue(0)));
}
}
@ -764,7 +791,7 @@ namespace Migrator.Providers
public void MigrationApplied(long version)
{
CreateSchemaInfoTable();
Insert("SchemaInfo",new string[]{"version"},new string[]{version.ToString()});
Insert("SchemaInfo", new string[] { "version" }, new string[] { version.ToString() });
_appliedMigrations.Add(version);
}
@ -816,13 +843,14 @@ namespace Migrator.Providers
public virtual string QuoteValues(string values)
{
return QuoteValues(new string[] {values})[0];
return QuoteValues(new string[] { values })[0];
}
public virtual string[] QuoteValues(string[] values)
{
return Array.ConvertAll<string, string>(values,
delegate(string val) {
delegate(string val)
{
if (null == val)
return "null";
else

View File

@ -256,6 +256,92 @@ namespace NzbDrone.Core.Test
}
[Test]
public void IsSeasonIgnored_should_return_true_if_all_episodes_ignored()
{
var repo = MockLib.GetEmptyRepository();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.SetConstant(repo);
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.Ignored = true)
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 2)
.Build();
repo.AddMany(episodes);
//Act
var result = mocker.Resolve<EpisodeProvider>().IsIgnored(10, 2);
//Assert
result.Should().BeTrue();
}
[Test]
public void IsSeasonIgnored_should_return_false_if_none_of_episodes_are_ignored()
{
var repo = MockLib.GetEmptyRepository();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.SetConstant(repo);
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.Ignored = false)
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 2)
.Build();
repo.AddMany(episodes);
//Act
var result = mocker.Resolve<EpisodeProvider>().IsIgnored(10, 2);
//Assert
result.Should().BeFalse();
}
[Test]
public void IsSeasonIgnored_should_return_false_if_some_of_episodes_are_ignored()
{
var repo = MockLib.GetEmptyRepository();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.SetConstant(repo);
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 2)
.Have(c => c.Ignored = true)
.Build();
episodes[2].Ignored = false;
repo.AddMany(episodes);
//Act
var result = mocker.Resolve<EpisodeProvider>().IsIgnored(10, 2);
//Assert
result.Should().BeFalse();
}
[Test]
public void IsSeasonIgnored_should_return_true_if_invalid_series()
{
var repo = MockLib.GetEmptyRepository();
var mocker = new AutoMoqer(MockBehavior.Strict);
mocker.SetConstant(repo);
//Act
var result = mocker.Resolve<EpisodeProvider>().IsIgnored(10, 2);
//Assert
result.Should().BeTrue();
}
[Test]
[Explicit]
public void Add_daily_show_episodes()
@ -276,5 +362,7 @@ namespace NzbDrone.Core.Test
var episodes = episodeProvider.GetEpisodeBySeries(tvDbSeriesId);
episodes.Should().NotBeEmpty();
}
}
}

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
@ -34,25 +35,30 @@ namespace NzbDrone.Core.Test.Framework
}
}
public static IRepository GetEmptyRepository()
{
return GetEmptyRepository(false);
}
public static IRepository GetEmptyRepository(bool enableLogging)
public static IRepository GetEmptyRepository(bool enableLogging = false, string fileName = "")
{
Console.WriteLine("Creating an empty SQLite database");
var provider = ProviderFactory.GetProvider("Data Source=" + Guid.NewGuid() + ".db;Version=3;New=True",
"System.Data.SQLite");
var repo = new SimpleRepository(provider, SimpleRepositoryOptions.RunMigrations);
if (String.IsNullOrWhiteSpace(fileName))
{
fileName = Guid.NewGuid() + ".db";
}
var provider = Connection.GetDataProvider(Connection.GetConnectionString(fileName));
var repo = Connection.CreateSimpleRepository(provider);
ForceMigration(repo);
Migrations.Run(Connection.GetConnectionString(fileName), false);
if (enableLogging)
{
provider.Log = new NlogWriter();
}
Console.WriteLine("**********************************************************************************");
Console.WriteLine("*****************************REPO IS READY****************************************");
Console.WriteLine("**********************************************************************************");
return repo;
}

View File

@ -17,8 +17,8 @@ namespace NzbDrone.Core.Test
public class DbBenchmark : TestBase
{
const int Episodes_Per_Season = 20;
private readonly List<int> seasonsNumbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
private readonly List<int> seriesIds = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
private readonly List<int> seasonsNumbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
private readonly List<int> seriesIds = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
private readonly List<Episode> episodes = new List<Episode>();
private readonly List<EpisodeFile> files = new List<EpisodeFile>();
private readonly IRepository repo = MockLib.GetEmptyRepository();
@ -68,15 +68,40 @@ namespace NzbDrone.Core.Test
}
}
}
repo.AddMany(episodes);
repo.AddMany(files);
}
}
[Test]
public void get_episode_by_series_seasons_episode_x1000()
public void get_episode_by_series_seasons_episode_x5000()
{
var epProvider = new EpisodeProvider(repo, null);
Thread.Sleep(1000);
var random = new Random();
Console.WriteLine("Starting Test");
var sw = Stopwatch.StartNew();
for (int i = 0; i < 5000; i++)
{
epProvider.GetEpisode(6, random.Next(2, 5), random.Next(2, Episodes_Per_Season - 10)).Should().NotBeNull();
}
sw.Stop();
Console.WriteLine("Took " + sw.Elapsed);
}
[Test]
public void get_episode_by_series_seasons_x1000()
{
var epProvider = new EpisodeProvider(repo, null);
@ -89,30 +114,6 @@ namespace NzbDrone.Core.Test
var sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++)
{
epProvider.GetEpisode(6, random.Next(2, 5), random.Next(2, Episodes_Per_Season - 10)).Should().NotBeNull();
}
sw.Stop();
Console.WriteLine("Took " + sw.Elapsed);
}
[Test]
public void get_episode_by_series_seasons_x500()
{
var epProvider = new EpisodeProvider(repo, null);
Thread.Sleep(1000);
var random = new Random();
Console.WriteLine("Starting Test");
var sw = Stopwatch.StartNew();
for (int i = 0; i < 500; i++)
{
epProvider.GetEpisodesBySeason(6, random.Next(2, 5)).Should().NotBeNull();
}
@ -124,7 +125,7 @@ namespace NzbDrone.Core.Test
}
[Test]
public void get_episode_file_count_x50()
public void get_episode_file_count_x100()
{
var mocker = new AutoMoq.AutoMoqer();
mocker.SetConstant(repo);
@ -139,12 +140,39 @@ namespace NzbDrone.Core.Test
Console.WriteLine("Starting Test");
var sw = Stopwatch.StartNew();
for (int i = 0; i < 50; i++)
for (int i = 0; i < 100; i++)
{
mediaProvider.GetEpisodeFilesCount(random.Next(1, 5)).Should().NotBeNull();
}
sw.Stop();
Console.WriteLine("Took " + sw.Elapsed);
}
[Test]
public void get_season_count_x5000()
{
var mocker = new AutoMoq.AutoMoqer();
mocker.SetConstant(repo);
var provider = mocker.Resolve<EpisodeProvider>();
Thread.Sleep(1000);
var random = new Random();
Console.WriteLine("Starting Test");
var sw = Stopwatch.StartNew();
for (int i = 0; i < 5000; i++)
{
provider.GetSeasons(random.Next(1, 10)).Should().HaveSameCount(seasonsNumbers);
}
sw.Stop();
Console.WriteLine("Took " + sw.Elapsed);

View File

@ -56,8 +56,7 @@ namespace NzbDrone.Core
LogConfiguration.Setup();
Migrations.Run(Connection.MainConnectionString);
ForceMigration(_kernel.Get<IRepository>());
Migrations.Run(Connection.MainConnectionString, true);
SetupDefaultQualityProfiles(_kernel.Get<IRepository>()); //Setup the default QualityProfiles on start-up
@ -134,16 +133,6 @@ namespace NzbDrone.Core
_kernel.Get<ExternalNotificationProvider>().InitializeNotifiers(notifiers.ToList());
}
private static void ForceMigration(IRepository repository)
{
repository.All<Series>().Count();
repository.All<Episode>().Count();
repository.All<EpisodeFile>().Count();
repository.All<QualityProfile>().Count();
repository.All<History>().Count();
repository.All<IndexerSetting>().Count();
}
/// <summary>
/// Forces IISExpress process to exit with the host application
/// </summary>

View File

@ -44,6 +44,11 @@ namespace NzbDrone.Core.Datastore
return ProviderFactory.GetProvider(connectionString, "System.Data.SQLite");
}
public static IRepository CreateSimpleRepository(IDataProvider dataProvider)
{
return new SimpleRepository(dataProvider, SimpleRepositoryOptions.RunMigrations);
}
public static IRepository CreateSimpleRepository(string connectionString)
{
return new SimpleRepository(GetDataProvider(connectionString), SimpleRepositoryOptions.RunMigrations);

View File

@ -7,7 +7,9 @@ using System.Text;
using Migrator.Framework;
using NLog;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using SubSonic.Extensions;
using SubSonic.Repository;
using SubSonic.Schema;
namespace NzbDrone.Core.Datastore
@ -16,18 +18,31 @@ namespace NzbDrone.Core.Datastore
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public static void Run(string connetionString)
public static void Run(string connetionString, bool trace)
{
Logger.Info("Preparing run database migration");
try
{
var migrator = new Migrator.Migrator("Sqlite", connetionString,
Assembly.GetAssembly(typeof(Migrations)), true, new MigrationLogger());
Migrator.Migrator migrator;
if (trace)
{
migrator = new Migrator.Migrator("Sqlite", connetionString, Assembly.GetAssembly(typeof(Migrations)), true, new MigrationLogger());
}
else
{
migrator = new Migrator.Migrator("Sqlite", connetionString, Assembly.GetAssembly(typeof(Migrations)));
}
migrator.MigrateToLastVersion();
ForceSubSonicMigration(Connection.CreateSimpleRepository(connetionString));
Logger.Info("Database migration completed");
}
catch (Exception e)
{
@ -35,6 +50,16 @@ namespace NzbDrone.Core.Datastore
}
}
public static void ForceSubSonicMigration(IRepository repository)
{
repository.Single<Series>(1);
repository.Single<Episode>(1);
repository.Single<EpisodeFile>(1);
repository.Single<QualityProfile>(1);
repository.Single<History>(1);
repository.Single<IndexerSetting>(1);
}
public static void RemoveDeletedColumns(ITransformationProvider transformationProvider)
{
@ -93,11 +118,7 @@ namespace NzbDrone.Core.Datastore
{
public override void Up()
{
//Remove jobs table forcing it to repopulate
var repoProvider = new RepositoryProvider();
var jobTable = repoProvider.GetSchemaFromType(typeof(JobSetting));
Database.RemoveTable(jobTable.Name);
Database.RemoveTable(RepositoryProvider.JobsSchema.Name);
}
public override void Down()
@ -122,4 +143,34 @@ namespace NzbDrone.Core.Datastore
throw new NotImplementedException();
}
}
[Migration(20110604)]
public class Migration20110604 : Migration
{
public override void Up()
{
Migrations.ForceSubSonicMigration(Connection.CreateSimpleRepository(Connection.MainConnectionString));
var episodesTable = RepositoryProvider.EpisodesSchema;
//Database.AddIndex("idx_episodes_series_season_episode", episodesTable.Name, true,
// episodesTable.GetColumnByPropertyName("SeriesId").Name,
// episodesTable.GetColumnByPropertyName("SeasonNumber").Name,
// episodesTable.GetColumnByPropertyName("EpisodeNumber").Name);
Database.AddIndex("idx_episodes_series_season", episodesTable.Name, false,
episodesTable.GetColumnByPropertyName("SeriesId").Name,
episodesTable.GetColumnByPropertyName("SeasonNumber").Name);
Database.AddIndex("idx_episodes_series", episodesTable.Name, false,
episodesTable.GetColumnByPropertyName("SeriesId").Name);
Migrations.RemoveDeletedColumns(Database);
Migrations.AddNewColumns(Database);
}
public override void Down()
{
throw new NotImplementedException();
}
}
}

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using Migrator.Providers;
using Migrator.Providers.SQLite;
using NzbDrone.Core.Repository;
using SubSonic.DataProviders;
using SubSonic.Extensions;
using SubSonic.Schema;
@ -15,6 +16,12 @@ namespace NzbDrone.Core.Datastore
{
public class RepositoryProvider
{
public static readonly ITable EpisodesSchema = new RepositoryProvider().GetSchemaFromType(typeof(Episode));
public static readonly ITable SeriesSchema = new RepositoryProvider().GetSchemaFromType(typeof(Series));
public static readonly ITable EpisodeFilesSchema = new RepositoryProvider().GetSchemaFromType(typeof(EpisodeFile));
public static readonly ITable JobsSchema = new RepositoryProvider().GetSchemaFromType(typeof(JobSetting));
public virtual IList<Type> GetRepositoryTypes()
{
var coreAssembly = Assembly.GetExecutingAssembly();
@ -85,5 +92,7 @@ namespace NzbDrone.Core.Datastore
return migColumn;
}
}
}