diff --git a/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs b/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs index aeb95f0ef1..5593a4aa69 100644 --- a/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Umbraco.Tests.Integration/Extensions/ApplicationBuilderExtensions.cs @@ -53,11 +53,11 @@ namespace Umbraco.Tests.Integration.Extensions { case UmbracoTestOptions.Database.NewSchemaPerTest: - // Add teardown callback - integrationTest.OnTestTearDown(() => db.Detach()); - // New DB + Schema - db.AttachSchema(); + var newSchemaDbId = db.AttachSchema(); + + // Add teardown callback + integrationTest.OnTestTearDown(() => db.Detach(newSchemaDbId)); // We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings var dbFactory = app.ApplicationServices.GetRequiredService(); @@ -89,22 +89,27 @@ namespace Umbraco.Tests.Integration.Extensions break; case UmbracoTestOptions.Database.NewEmptyPerTest: - // Add teardown callback - integrationTest.OnTestTearDown(() => db.Detach()); + var newEmptyDbId = db.AttachEmpty(); + + // Add teardown callback + integrationTest.OnTestTearDown(() => db.Detach(newEmptyDbId)); - db.AttachEmpty(); break; case UmbracoTestOptions.Database.NewSchemaPerFixture: - // Add teardown callback - integrationTest.OnFixtureTearDown(() => db.Detach()); + throw new NotImplementedException(); + + //// Add teardown callback + //integrationTest.OnFixtureTearDown(() => db.Detach()); break; case UmbracoTestOptions.Database.NewEmptyPerFixture: - // Add teardown callback - integrationTest.OnFixtureTearDown(() => db.Detach()); + throw new NotImplementedException(); + + //// Add teardown callback + //integrationTest.OnFixtureTearDown(() => db.Detach()); break; default: diff --git a/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs b/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs index 1d41cdf8b2..fda9b99583 100644 --- a/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs +++ b/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs @@ -78,27 +78,29 @@ namespace Umbraco.Tests.Integration.Testing _schemaPool = new DatabasePool(_localDb, _instance, DatabaseName + "-Schema", tempName, _filesPath, schemaSize, schemaParallel, delete: true, prepare: RebuildSchema); } - public void AttachEmpty() + public int AttachEmpty() { if (_emptyPool == null) Create(); - _currentCstr = _emptyPool.AttachDatabase(); + _currentCstr = _emptyPool.AttachDatabase(out var id); _currentPool = _emptyPool; + return id; } - public void AttachSchema() + public int AttachSchema() { if (_schemaPool == null) Create(); - _currentCstr = _schemaPool.AttachDatabase(); + _currentCstr = _schemaPool.AttachDatabase(out var id); _currentPool = _schemaPool; + return id; } - public void Detach() + public void Detach(int id) { - _currentPool.DetachDatabase(); + _currentPool.DetachDatabase(id); } private void RebuildSchema(DbConnection conn, IDbCommand cmd) @@ -188,7 +190,25 @@ namespace Umbraco.Tests.Integration.Testing from sys.tables; exec sp_executesql @stmt; "; - cmd.ExecuteNonQuery(); + + // rudimentary retry policy since a db can still be in use when we try to drop + for (var i = 0; i < 5; i++) + { + try + { + cmd.ExecuteNonQuery(); + return; + } + catch (SqlException ex) + { + // 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(1000); + if (i == 4) + throw; + } + } + } public static void KillLocalDb() @@ -266,22 +286,19 @@ namespace Umbraco.Tests.Integration.Testing } } - public string AttachDatabase() + public string AttachDatabase(out int id) { - try - { - _current = _readyQueue.Take(); - } - catch (InvalidOperationException) - { - _current = 0; - return null; - } + _current = _readyQueue.Take(); + id = _current; + return ConnectionString(_current); } - public void DetachDatabase() + public void DetachDatabase(int id) { + if (id != _current) + throw new InvalidOperationException("Cannot detatch the non-current db"); + _prepareQueue.Add(_current); } diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 16343a40c5..3657853e10 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using NPoco.Expressions; using NUnit.Framework; using Umbraco.Core.Cache; using Umbraco.Core.Composing; @@ -70,37 +71,29 @@ namespace Umbraco.Tests.Integration.Testing private static readonly object _dbLocker = new object(); private static LocalDbTestDatabase _dbInstance; - private readonly List _testTeardown = new List(); - private readonly List _fixtureTeardown = new List(); + private Action _testTeardown = null; + private Action _fixtureTeardown = null; public void OnTestTearDown(Action tearDown) { - _testTeardown.Add(tearDown); + _testTeardown = tearDown; } public void OnFixtureTearDown(Action tearDown) { - _fixtureTeardown.Add(tearDown); + _fixtureTeardown = tearDown; } [OneTimeTearDown] public void FixtureTearDown() { - // call all registered callbacks - foreach (var action in _fixtureTeardown) - { - action(); - } + _fixtureTeardown?.Invoke(); } [TearDown] public void TearDown() { - // call all registered callbacks - foreach (var action in _testTeardown) - { - action(); - } + _testTeardown?.Invoke(); } [SetUp]