Files
Umbraco-CMS/tests/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs
Bjarke Berg fe1c2f5d4f Merge remote-tracking branch 'origin/v9/dev' into v10/dev
# Conflicts:
#	build/templates/UmbracoPackage/.template.config/template.json
#	build/templates/UmbracoProject/.template.config/template.json
#	src/Directory.Build.props
#	src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
#	src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs
#	src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs
#	src/Umbraco.Infrastructure/HostedServices/HealthCheckNotifier.cs
#	src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs
#	src/Umbraco.Infrastructure/HostedServices/ServerRegistration/TouchServerTask.cs
#	src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs
#	src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs
#	src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs
#	tests/Umbraco.Tests.Integration.SqlCe/Umbraco.Infrastructure/Persistence/DatabaseBuilderTests.cs
#	tests/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs
#	tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs
#	tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SchemaValidationTest.cs
#	tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SqlServerTableByTableTest.cs
#	tests/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs
2022-03-31 12:51:55 +02:00

148 lines
4.6 KiB
C#

// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Threading;
using Microsoft.Extensions.Logging;
using Moq;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Infrastructure.Migrations.Install;
using Umbraco.Cms.Infrastructure.Persistence;
namespace Umbraco.Cms.Tests.Integration.Testing
{
public abstract class BaseTestDatabase
{
public static bool IsSqlite() => BaseTestDatabase.Instance is SqliteTestDatabase;
public static bool IsSqlServer() => BaseTestDatabase.Instance is SqlServerBaseTestDatabase;
protected ILoggerFactory _loggerFactory;
protected IUmbracoDatabaseFactory _databaseFactory;
protected IList<TestDbMeta> _testDatabases;
protected BlockingCollection<TestDbMeta> _prepareQueue;
protected BlockingCollection<TestDbMeta> _readySchemaQueue;
protected BlockingCollection<TestDbMeta> _readyEmptyQueue;
public static BaseTestDatabase Instance { get; private set; }
public BaseTestDatabase() => Instance = this;
protected abstract void Initialize();
public virtual TestDbMeta AttachEmpty()
{
if (_prepareQueue == null)
{
Initialize();
}
return _readyEmptyQueue.Take();
}
public virtual TestDbMeta AttachSchema()
{
if (_prepareQueue == null)
{
Initialize();
}
return _readySchemaQueue.Take();
}
public virtual void Detach(TestDbMeta meta)
{
_prepareQueue.TryAdd(meta);
}
protected virtual void PrepareDatabase() =>
Retry(10, () =>
{
while (_prepareQueue.IsCompleted == false)
{
TestDbMeta meta;
try
{
meta = _prepareQueue.Take();
}
catch (InvalidOperationException)
{
continue;
}
ResetTestDatabase(meta);
if (!meta.IsEmpty)
{
using (var conn = GetConnection(meta))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
RebuildSchema(cmd, meta);
}
}
_readySchemaQueue.TryAdd(meta);
}
else
{
_readyEmptyQueue.TryAdd(meta);
}
}
});
protected static void AddParameter(IDbCommand cmd, UmbracoDatabase.ParameterInfo parameterInfo)
{
IDbDataParameter p = cmd.CreateParameter();
p.ParameterName = parameterInfo.Name;
p.Value = parameterInfo.Value;
p.DbType = parameterInfo.DbType;
p.Size = parameterInfo.Size;
cmd.Parameters.Add(p);
}
protected abstract DbConnection GetConnection(TestDbMeta meta);
protected abstract void RebuildSchema(IDbCommand command, TestDbMeta meta);
protected abstract void ResetTestDatabase(TestDbMeta meta);
protected static void Retry(int maxIterations, Action action)
{
for (int i = 0; i < maxIterations; i++)
{
try
{
action();
return;
}
catch (DbException ex)
{
// Console.Error.WriteLine($"SqlException occured, but we try again {i+1}/{maxIterations}.\n{e}");
// This can occur when there's a transaction deadlock which means (i think) that the database is still in use and hasn't been closed properly yet
// so we need to just wait a little bit
Thread.Sleep(100 * i);
if (i == maxIterations - 1)
{
Debugger.Launch();
throw;
}
}
catch (InvalidOperationException)
{
// Ignore
}
}
}
public abstract void TearDown();
}
}