using System; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Data.SqlServerCe; using System.IO; using System.Linq; using System.Web.Routing; using System.Xml; using Moq; using NUnit.Framework; using SQLCE4Umbraco; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.ObjectResolution; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Publishing; using Umbraco.Core.Services; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; using Umbraco.Web.Security; using umbraco.BusinessLogic; namespace Umbraco.Tests.TestHelpers { /// /// Use this abstract class for tests that requires a Sql Ce database populated with the umbraco db schema. /// The PetaPoco Database class should be used through the . /// [TestFixture, RequiresSTA] public abstract class BaseDatabaseFactoryTest : BaseUmbracoApplicationTest { //This is used to indicate that this is the first test to run in the test session, if so, we always //ensure a new database file is used. private static volatile bool _firstRunInTestSession = true; private static readonly object Locker = new object(); private bool _firstTestInFixture = true; //Used to flag if its the first test in the current session private bool _isFirstRunInTestSession = false; //Used to flag if its the first test in the current fixture private bool _isFirstTestInFixture = false; private ApplicationContext _appContext; private string _dbPath; //used to store (globally) the pre-built db with schema and initial data private static Byte[] _dbBytes; [SetUp] public override void Initialize() { InitializeFirstRunFlags(); var path = TestHelper.CurrentAssemblyDirectory; AppDomain.CurrentDomain.SetData("DataDirectory", path); //disable cache var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); var dbFactory = new DefaultDatabaseFactory( GetDbConnectionString(), GetDbProviderName(), Logger); var repositoryFactory = new RepositoryFactory(cacheHelper, Logger, SqlSyntax, SettingsForTests.GenerateMockSettings()); _appContext = new ApplicationContext( //assign the db context new DatabaseContext(dbFactory, Logger, SqlSyntax, "System.Data.SqlServerCe.4.0"), //assign the service context new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper, Logger), cacheHelper, ProfilingLogger) { IsReady = true }; base.Initialize(); using (ProfilingLogger.TraceDuration("init")) { //TODO: Somehow make this faster - takes 5s + DatabaseContext.Initialize(dbFactory.ProviderName, dbFactory.ConnectionString); CreateSqlCeDatabase(); InitializeDatabase(); //ensure the configuration matches the current version for tests SettingsForTests.ConfigurationStatus = UmbracoVersion.GetSemanticVersion().ToSemanticString(); } } protected virtual ISqlSyntaxProvider SqlSyntax { get { return new SqlCeSyntaxProvider(); } } protected override void SetupApplicationContext() { ApplicationContext.Current = _appContext; } /// /// The database behavior to use for the test/fixture /// protected DatabaseBehavior DatabaseTestBehavior { get { var att = this.GetType().GetCustomAttribute(false); return att != null ? att.Behavior : DatabaseBehavior.NoDatabasePerFixture; } } protected virtual string GetDbProviderName() { return "System.Data.SqlServerCe.4.0"; } /// /// Get the db conn string /// protected virtual string GetDbConnectionString() { return @"Datasource=|DataDirectory|UmbracoPetaPocoTests.sdf;Flush Interval=1;"; } /// /// Creates the SqlCe database if required /// protected virtual void CreateSqlCeDatabase() { if (DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture) return; var path = TestHelper.CurrentAssemblyDirectory; //Get the connectionstring settings from config var settings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; ConfigurationManager.AppSettings.Set( Core.Configuration.GlobalSettings.UmbracoConnectionName, GetDbConnectionString()); _dbPath = string.Concat(path, "\\UmbracoPetaPocoTests.sdf"); //create a new database file if // - is the first test in the session // - the database file doesn't exist // - NewDbFileAndSchemaPerTest // - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture //if this is the first test in the session, always ensure a new db file is created if (_isFirstRunInTestSession || File.Exists(_dbPath) == false || (DatabaseTestBehavior == DatabaseBehavior.NewDbFileAndSchemaPerTest || DatabaseTestBehavior == DatabaseBehavior.EmptyDbFilePerTest) || (_isFirstTestInFixture && DatabaseTestBehavior == DatabaseBehavior.NewDbFileAndSchemaPerFixture)) { using (ProfilingLogger.TraceDuration("Remove database file")) { RemoveDatabaseFile(ex => { //if this doesn't work we have to make sure everything is reset! otherwise // well run into issues because we've already set some things up TearDown(); throw ex; }); } //Create the Sql CE database using (ProfilingLogger.TraceDuration("Create database file")) { if (DatabaseTestBehavior != DatabaseBehavior.EmptyDbFilePerTest && _dbBytes != null) { File.WriteAllBytes(_dbPath, _dbBytes); } else { var engine = new SqlCeEngine(settings.ConnectionString); engine.CreateDatabase(); } } } } /// /// sets up resolvers before resolution is frozen /// protected override void FreezeResolution() { PropertyEditorResolver.Current = new PropertyEditorResolver( new ActivatorServiceProvider(), Logger, () => PluginManager.Current.ResolvePropertyEditors()); DataTypesResolver.Current = new DataTypesResolver( new ActivatorServiceProvider(), Logger, () => PluginManager.Current.ResolveDataTypes()); MappingResolver.Current = new MappingResolver( new ActivatorServiceProvider(), Logger, () => PluginManager.Current.ResolveAssignedMapperTypes()); if (PropertyValueConvertersResolver.HasCurrent == false) PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(new ActivatorServiceProvider(), Logger); if (PublishedContentModelFactoryResolver.HasCurrent == false) PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver(); base.FreezeResolution(); } /// /// Creates the tables and data for the database /// protected virtual void InitializeDatabase() { if (DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture || DatabaseTestBehavior == DatabaseBehavior.EmptyDbFilePerTest) return; //create the schema and load default data if: // - is the first test in the session // - NewDbFileAndSchemaPerTest // - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture if (_dbBytes == null && (_isFirstRunInTestSession || DatabaseTestBehavior == DatabaseBehavior.NewDbFileAndSchemaPerTest || (_isFirstTestInFixture && DatabaseTestBehavior == DatabaseBehavior.NewDbFileAndSchemaPerFixture))) { var schemaHelper = new DatabaseSchemaHelper(DatabaseContext.Database, Logger, SqlSyntax); //Create the umbraco database and its base data schemaHelper.CreateDatabaseSchema(false, ApplicationContext); //close the connections, we're gonna read this baby in as a byte array so we don't have to re-initialize the // damn db for each test CloseDbConnections(); _dbBytes = File.ReadAllBytes(_dbPath); } } [TestFixtureTearDown] public void FixtureTearDown() { RemoveDatabaseFile(); } [TearDown] public override void TearDown() { using (ProfilingLogger.TraceDuration("teardown")) { _isFirstTestInFixture = false; //ensure this is false before anything! if (DatabaseTestBehavior == DatabaseBehavior.NewDbFileAndSchemaPerTest) { RemoveDatabaseFile(); } AppDomain.CurrentDomain.SetData("DataDirectory", null); SqlSyntaxContext.SqlSyntaxProvider = null; } base.TearDown(); } private void CloseDbConnections() { //Ensure that any database connections from a previous test is disposed. //This is really just double safety as its also done in the TearDown. if (ApplicationContext != null && DatabaseContext != null && DatabaseContext.Database != null) DatabaseContext.Database.Dispose(); SqlCeContextGuardian.CloseBackgroundConnection(); } private void InitializeFirstRunFlags() { //this needs to be thread-safe _isFirstRunInTestSession = false; if (_firstRunInTestSession) { lock (Locker) { if (_firstRunInTestSession) { _isFirstRunInTestSession = true; //set the flag _firstRunInTestSession = false; } } } if (_firstTestInFixture) { lock (Locker) { if (_firstTestInFixture) { _isFirstTestInFixture = true; //set the flag _firstTestInFixture = false; } } } } private void RemoveDatabaseFile(Action onFail = null) { CloseDbConnections(); string path = TestHelper.CurrentAssemblyDirectory; try { string filePath = string.Concat(path, "\\UmbracoPetaPocoTests.sdf"); if (File.Exists(filePath)) { File.Delete(filePath); } } catch (Exception ex) { LogHelper.Error("Could not remove the old database file", ex); //We will swallow this exception! That's because a sub class might require further teardown logic. if (onFail != null) { onFail(ex); } } } protected ServiceContext ServiceContext { get { return ApplicationContext.Services; } } protected DatabaseContext DatabaseContext { get { return ApplicationContext.DatabaseContext; } } protected UmbracoContext GetUmbracoContext(string url, int templateId, RouteData routeData = null, bool setSingleton = false) { var cache = new PublishedContentCache(); cache.GetXmlDelegate = (context, preview) => { var doc = new XmlDocument(); doc.LoadXml(GetXmlContent(templateId)); return doc; }; PublishedContentCache.UnitTesting = true; var httpContext = GetHttpContextFactory(url, routeData).HttpContext; var ctx = new UmbracoContext( httpContext, ApplicationContext, new PublishedCaches(cache, new PublishedMediaCache(ApplicationContext)), new WebSecurity(httpContext, ApplicationContext)); if (setSingleton) { UmbracoContext.Current = ctx; } return ctx; } protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) { var factory = routeData != null ? new FakeHttpContextFactory(url, routeData) : new FakeHttpContextFactory(url); //set the state helper StateHelper.HttpContext = factory.HttpContext; return factory; } protected virtual string GetXmlContent(int templateId) { return @" ]> 1 This is some content]]> "; } } }