762 lines
36 KiB
C#
762 lines
36 KiB
C#
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||
|
|
||
|
namespace System.Data.Entity.Config
|
||
|
{
|
||
|
using System.Collections.Concurrent;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Configuration;
|
||
|
using System.Data.Entity.Internal;
|
||
|
using System.Data.Entity.Resources;
|
||
|
using System.Linq;
|
||
|
using System.Data.Entity.TestHelpers;
|
||
|
using Moq;
|
||
|
using SimpleModel;
|
||
|
using Xunit;
|
||
|
|
||
|
public class DbConfigurationManagerTests : TestBase
|
||
|
{
|
||
|
public class Instance : TestBase
|
||
|
{
|
||
|
[Fact]
|
||
|
public void Instance_returns_the_Singleton_instance()
|
||
|
{
|
||
|
Assert.NotNull(DbConfigurationManager.Instance);
|
||
|
Assert.Same(DbConfigurationManager.Instance, DbConfigurationManager.Instance);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class GetConfiguration : TestBase
|
||
|
{
|
||
|
[Fact]
|
||
|
public void GetConfiguration_returns_the_configuration_at_the_top_of_the_stack_if_overriding_configurations_have_been_pushed()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
var mockInternalConfiguration = new Mock<InternalConfiguration>
|
||
|
{
|
||
|
CallBase = true
|
||
|
};
|
||
|
|
||
|
var mockDbConfiguration = new Mock<DbConfiguration>();
|
||
|
mockDbConfiguration.Setup(m => m.InternalConfiguration).Returns(mockInternalConfiguration.Object);
|
||
|
mockInternalConfiguration.Setup(m => m.Owner).Returns(mockDbConfiguration.Object);
|
||
|
|
||
|
var configuration = mockInternalConfiguration.Object;
|
||
|
|
||
|
manager.SetConfiguration(configuration);
|
||
|
manager.PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
|
||
|
var pushed1 = manager.GetConfiguration();
|
||
|
Assert.NotSame(configuration, pushed1);
|
||
|
|
||
|
manager.PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
|
||
|
var pushed2 = manager.GetConfiguration();
|
||
|
Assert.NotSame(pushed1, pushed2);
|
||
|
Assert.NotSame(configuration, pushed2);
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void GetConfiguration_returns_the_previously_set_configuration()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
var mockInternalConfiguration = CreateMockInternalConfiguration();
|
||
|
|
||
|
manager.SetConfiguration(mockInternalConfiguration.Object);
|
||
|
|
||
|
Assert.Same(mockInternalConfiguration.Object, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void GetConfiguration_sets_and_returns_a_new_configuration_if_none_was_previously_set()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
|
||
|
var configuration = manager.GetConfiguration();
|
||
|
|
||
|
Assert.NotNull(configuration);
|
||
|
Assert.Same(configuration, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This test makes calls from multiple threads such that we have at least some chance of finding threading
|
||
|
/// issues. As with any test of this type just because the test passes does not mean that the code is
|
||
|
/// correct. On the other hand if this test ever fails (EVEN ONCE) then we know there is a problem to
|
||
|
/// be investigated. DON'T just re-run and think things are okay if the test then passes.
|
||
|
/// </summary>
|
||
|
[Fact]
|
||
|
public void GetConfiguration_for_default_value_can_be_called_from_multiple_threads_concurrently()
|
||
|
{
|
||
|
ConfigurationThreadTest(m => { }, m => { });
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This test makes calls from multiple threads such that we have at least some chance of finding threading
|
||
|
/// issues. As with any test of this type just because the test passes does not mean that the code is
|
||
|
/// correct. On the other hand if this test ever fails (EVEN ONCE) then we know there is a problem to
|
||
|
/// be investigated. DON'T just re-run and think things are okay if the test then passes.
|
||
|
/// </summary>
|
||
|
[Fact]
|
||
|
public void GetConfiguration_for_pushed_config_can_be_called_from_multiple_threads_concurrently()
|
||
|
{
|
||
|
ConfigurationThreadTest(m => m.PushConfiguration(AppConfig.DefaultInstance, typeof(SimpleModelContext)), m => { });
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This test makes calls from multiple threads such that we have at least some chance of finding threading
|
||
|
/// issues. As with any test of this type just because the test passes does not mean that the code is
|
||
|
/// correct. On the other hand if this test ever fails (EVEN ONCE) then we know there is a problem to
|
||
|
/// be investigated. DON'T just re-run and think things are okay if the test then passes.
|
||
|
/// </summary>
|
||
|
[Fact]
|
||
|
public void GetConfiguration_for_pushed_and_popped_config_can_be_called_from_multiple_threads_concurrently()
|
||
|
{
|
||
|
ConfigurationThreadTest(
|
||
|
m =>
|
||
|
{
|
||
|
m.PushConfiguration(AppConfig.DefaultInstance, typeof(SimpleModelContext));
|
||
|
m.PopConfiguration(AppConfig.DefaultInstance);
|
||
|
},
|
||
|
m => { });
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class SetConfiguration : TestBase
|
||
|
{
|
||
|
[Fact]
|
||
|
public void SetConfiguration_sets_and_locks_the_configuration_if_none_was_previously_set_and_config_file_has_none()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
var mockInternalConfiguration = CreateMockInternalConfiguration();
|
||
|
|
||
|
manager.SetConfiguration(mockInternalConfiguration.Object);
|
||
|
|
||
|
Assert.Same(mockInternalConfiguration.Object, manager.GetConfiguration());
|
||
|
|
||
|
mockInternalConfiguration.Verify(m => m.Lock());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void The_same_type_of_configuration_can_be_set_multiple_times()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
var mockInternalConfiguration1 = CreateMockInternalConfiguration();
|
||
|
var mockInternalConfiguration2 = CreateMockInternalConfiguration();
|
||
|
|
||
|
manager.SetConfiguration(mockInternalConfiguration1.Object);
|
||
|
manager.SetConfiguration(mockInternalConfiguration2.Object);
|
||
|
|
||
|
Assert.Same(mockInternalConfiguration1.Object, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void SetConfiguration_discards_the_given_configuration_and_uses_the_configuration_from_the_config_file_if_it_exists()
|
||
|
{
|
||
|
var mockLoadedConfig = CreateMockInternalConfiguration();
|
||
|
var mockLoader = new Mock<DbConfigurationLoader>();
|
||
|
mockLoader.Setup(m => m.AppConfigContainsDbConfigurationType(It.IsAny<AppConfig>())).Returns(true);
|
||
|
mockLoader.Setup(m => m.TryLoadFromConfig(It.IsAny<AppConfig>())).Returns(mockLoadedConfig.Object);
|
||
|
|
||
|
var manager = CreateManager(mockLoader);
|
||
|
var mockConfiguration = CreateMockInternalConfiguration();
|
||
|
|
||
|
manager.SetConfiguration(mockConfiguration.Object);
|
||
|
|
||
|
Assert.Same(mockLoadedConfig.Object, manager.GetConfiguration());
|
||
|
|
||
|
mockLoadedConfig.Verify(m => m.Lock());
|
||
|
mockConfiguration.Verify(m => m.Lock(), Times.Never());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void SetConfiguration_throws_if_an_attempt_is_made_to_set_a_different_configuration_type()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
|
||
|
var configuration1 = new FakeConfiguration();
|
||
|
var mockInternalConfiguration1 = CreateMockInternalConfiguration(configuration1);
|
||
|
|
||
|
var configuration2 = new Mock<DbConfiguration>().Object;
|
||
|
var mockInternalConfiguration2 = CreateMockInternalConfiguration(configuration2);
|
||
|
|
||
|
manager.SetConfiguration(mockInternalConfiguration1.Object);
|
||
|
|
||
|
Assert.Equal(
|
||
|
Strings.ConfigurationSetTwice(configuration2.GetType().Name, configuration1.GetType().Name),
|
||
|
Assert.Throws<InvalidOperationException>(() => manager.SetConfiguration(mockInternalConfiguration2.Object)).Message);
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void SetConfiguration_throws_if_an_attempt_is_made_to_set_a_configuration_after_the_default_has_already_been_used()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
|
||
|
var dbConfiguration = new Mock<DbConfiguration>().Object;
|
||
|
var mockInternalConfiguration = CreateMockInternalConfiguration(dbConfiguration);
|
||
|
|
||
|
manager.GetConfiguration(); // Initialize default
|
||
|
|
||
|
Assert.Equal(
|
||
|
Strings.DefaultConfigurationUsedBeforeSet(dbConfiguration.GetType().Name),
|
||
|
Assert.Throws<InvalidOperationException>(() => manager.SetConfiguration(mockInternalConfiguration.Object)).Message);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This test makes calls from multiple threads such that we have at least some chance of finding threading
|
||
|
/// issues. As with any test of this type just because the test passes does not mean that the code is
|
||
|
/// correct. On the other hand if this test ever fails (EVEN ONCE) then we know there is a problem to
|
||
|
/// be investigated. DON'T just re-run and think things are okay if the test then passes.
|
||
|
/// </summary>
|
||
|
[Fact]
|
||
|
public void SetConfiguration_can_be_called_from_multiple_threads_concurrently_and_only_one_will_win()
|
||
|
{
|
||
|
ConfigurationThreadTest(
|
||
|
m => { },
|
||
|
m => m.SetConfiguration(new FunctionalTestsConfiguration().InternalConfiguration));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class EnsureLoadedForContext : TestBase
|
||
|
{
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_loads_configuration_from_context_assembly_if_none_was_previously_used()
|
||
|
{
|
||
|
var foundConfiguration = CreateMockInternalConfiguration().Object;
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
mockFinder.Setup(m => m.TryCreateConfiguration(typeof(FakeContext), It.IsAny<IEnumerable<Type>>())).Returns(
|
||
|
foundConfiguration);
|
||
|
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()));
|
||
|
Assert.Same(foundConfiguration, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_given_context_is_exactly_DbContext()
|
||
|
{
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
|
||
|
CreateManager(null, mockFinder).EnsureLoadedForContext(typeof(DbContext));
|
||
|
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(DbContext), It.IsAny<IEnumerable<Type>>()), Times.Never());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_assembly_has_already_been_checked()
|
||
|
{
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()), Times.Once());
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
// Finder has not been used again
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()), Times.Once());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_an_override_configuration_has_been_pushed()
|
||
|
{
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
manager.PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(DbContext), It.IsAny<IEnumerable<Type>>()), Times.Once());
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
// Finder has not been used again
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(DbContext), It.IsAny<IEnumerable<Type>>()), Times.Once());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_none_was_previously_used_and_no_configuration_is_found_in_assembly()
|
||
|
{
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()));
|
||
|
|
||
|
var mockInternalConfiguration = CreateMockInternalConfiguration();
|
||
|
manager.SetConfiguration(mockInternalConfiguration.Object);
|
||
|
|
||
|
Assert.Same(mockInternalConfiguration.Object, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_one_was_previously_used_but_no_configuration_is_found_in_assembly()
|
||
|
{
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
var configuration = manager.GetConfiguration();
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
mockFinder.Verify(m => m.TryFindConfigurationType(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()));
|
||
|
|
||
|
Assert.Same(configuration, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_one_was_previously()
|
||
|
{
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
var configuration = manager.GetConfiguration();
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
mockFinder.Verify(m => m.TryFindConfigurationType(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()));
|
||
|
|
||
|
Assert.Same(configuration, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_configuration_in_assembly_is_the_same_as_was_previously_used()
|
||
|
{
|
||
|
var mockInternalConfiguration = CreateMockInternalConfiguration();
|
||
|
|
||
|
var configuration = mockInternalConfiguration.Object;
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
mockFinder.Setup(m => m.TryFindConfigurationType(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()))
|
||
|
.Returns(configuration.Owner.GetType());
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
manager.SetConfiguration(configuration);
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
mockFinder.Verify(m => m.TryFindConfigurationType(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()));
|
||
|
|
||
|
Assert.Same(configuration, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_throws_if_found_configuration_does_not_match_previously_used_configuration()
|
||
|
{
|
||
|
var mockInternalConfiguration = CreateMockInternalConfiguration();
|
||
|
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
mockFinder.Setup(m => m.TryFindConfigurationType(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()))
|
||
|
.Returns(typeof(FakeConfiguration));
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
manager.SetConfiguration(mockInternalConfiguration.Object);
|
||
|
|
||
|
Assert.Equal(
|
||
|
Strings.SetConfigurationNotDiscovered(mockInternalConfiguration.Object.Owner.GetType().Name, typeof(FakeContext).Name),
|
||
|
Assert.Throws<InvalidOperationException>(
|
||
|
() => manager.EnsureLoadedForContext(typeof(FakeContext))).Message);
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_throws_if_configuration_is_found_but_default_was_previously_used()
|
||
|
{
|
||
|
var configuration = new Mock<DbConfiguration>().Object;
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
mockFinder.Setup(m => m.TryFindConfigurationType(typeof(FakeContext), It.IsAny<IEnumerable<Type>>()))
|
||
|
.Returns(configuration.GetType());
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
manager.GetConfiguration();
|
||
|
|
||
|
Assert.Equal(
|
||
|
Strings.ConfigurationNotDiscovered(configuration.GetType().Name),
|
||
|
Assert.Throws<InvalidOperationException>(
|
||
|
() => manager.EnsureLoadedForContext(typeof(FakeContext))).Message);
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_configuration_was_set_but_is_not_found_in_context_assembly()
|
||
|
{
|
||
|
var configuration = CreateMockInternalConfiguration().Object;
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
var manager = CreateManager(null, mockFinder);
|
||
|
|
||
|
manager.SetConfiguration(configuration);
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
Assert.Same(configuration, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_if_configuration_is_specified_in_config_file()
|
||
|
{
|
||
|
var mockLoadedConfig = CreateMockInternalConfiguration();
|
||
|
var mockLoader = new Mock<DbConfigurationLoader>();
|
||
|
mockLoader.Setup(m => m.AppConfigContainsDbConfigurationType(It.IsAny<AppConfig>())).Returns(true);
|
||
|
mockLoader.Setup(m => m.TryLoadFromConfig(It.IsAny<AppConfig>())).Returns(mockLoadedConfig.Object);
|
||
|
|
||
|
var mockInternalConfiguration = CreateMockInternalConfiguration();
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
var manager = CreateManager(mockLoader, mockFinder);
|
||
|
|
||
|
manager.SetConfiguration(mockInternalConfiguration.Object);
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
Assert.Same(mockLoadedConfig.Object, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_does_not_throw_even_if_finder_would_throw_if_configuration_is_specified_in_config_file()
|
||
|
{
|
||
|
var mockLoadedConfig = CreateMockInternalConfiguration();
|
||
|
var mockLoader = new Mock<DbConfigurationLoader>();
|
||
|
mockLoader.Setup(m => m.AppConfigContainsDbConfigurationType(It.IsAny<AppConfig>())).Returns(true);
|
||
|
mockLoader.Setup(m => m.TryLoadFromConfig(It.IsAny<AppConfig>())).Returns(mockLoadedConfig.Object);
|
||
|
|
||
|
var mockInternalConfiguration = CreateMockInternalConfiguration();
|
||
|
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
mockFinder
|
||
|
.Setup(m => m.TryCreateConfiguration(It.IsAny<Type>(), It.IsAny<IEnumerable<Type>>()))
|
||
|
.Throws<InvalidOperationException>();
|
||
|
mockFinder
|
||
|
.Setup(m => m.TryFindConfigurationType(It.IsAny<Type>(), It.IsAny<IEnumerable<Type>>()))
|
||
|
.Throws<InvalidOperationException>();
|
||
|
|
||
|
var manager = CreateManager(mockLoader, mockFinder);
|
||
|
manager.SetConfiguration(mockInternalConfiguration.Object);
|
||
|
|
||
|
manager.EnsureLoadedForContext(typeof(FakeContext));
|
||
|
|
||
|
Assert.Same(mockLoadedConfig.Object, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This test makes calls from multiple threads such that we have at least some chance of finding threading
|
||
|
/// issues. As with any test of this type just because the test passes does not mean that the code is
|
||
|
/// correct. On the other hand if this test ever fails (EVEN ONCE) then we know there is a problem to
|
||
|
/// be investigated. DON'T just re-run and think things are okay if the test then passes.
|
||
|
/// </summary>
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_can_be_called_from_multiple_threads_concurrently_before_configuration_has_been_used()
|
||
|
{
|
||
|
ConfigurationThreadTest(
|
||
|
m => { },
|
||
|
m => m.EnsureLoadedForContext(typeof(SimpleModelContext)));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This test makes calls from multiple threads such that we have at least some chance of finding threading
|
||
|
/// issues. As with any test of this type just because the test passes does not mean that the code is
|
||
|
/// correct. On the other hand if this test ever fails (EVEN ONCE) then we know there is a problem to
|
||
|
/// be investigated. DON'T just re-run and think things are okay if the test then passes.
|
||
|
/// </summary>
|
||
|
[Fact]
|
||
|
public void EnsureLoadedForContext_can_be_called_from_multiple_threads_concurrently_after_push_and_pop()
|
||
|
{
|
||
|
ConfigurationThreadTest(
|
||
|
m =>
|
||
|
{
|
||
|
m.PushConfiguration(AppConfig.DefaultInstance, typeof(SimpleModelContext));
|
||
|
m.PopConfiguration(AppConfig.DefaultInstance);
|
||
|
},
|
||
|
m => m.EnsureLoadedForContext(typeof(SimpleModelContext)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class PushConfiguration : TestBase
|
||
|
{
|
||
|
[Fact]
|
||
|
public void PushConfugiration_pushes_and_locks_configuration_from_config_if_found()
|
||
|
{
|
||
|
var mockConfiguration = new Mock<InternalConfiguration>();
|
||
|
var mockLoader = new Mock<DbConfigurationLoader>();
|
||
|
mockLoader.Setup(m => m.TryLoadFromConfig(AppConfig.DefaultInstance)).Returns(mockConfiguration.Object);
|
||
|
mockLoader.Setup(m => m.AppConfigContainsDbConfigurationType(It.IsAny<AppConfig>())).Returns(true);
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
|
||
|
var manager = CreateManager(mockLoader, mockFinder);
|
||
|
|
||
|
manager.PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
|
||
|
mockConfiguration.Verify(m => m.Lock());
|
||
|
Assert.Same(mockConfiguration.Object, manager.GetConfiguration());
|
||
|
mockLoader.Verify(m => m.TryLoadFromConfig(AppConfig.DefaultInstance));
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(DbContext), It.IsAny<IEnumerable<Type>>()), Times.Never());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void PushConfugiration_pushes_and_locks_configuration_discovered_in_context_assembly_if_found()
|
||
|
{
|
||
|
var mockConfiguration = new Mock<InternalConfiguration>();
|
||
|
var mockLoader = new Mock<DbConfigurationLoader>();
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
mockFinder.Setup(m => m.TryCreateConfiguration(typeof(DbContext), It.IsAny<IEnumerable<Type>>())).Returns(
|
||
|
mockConfiguration.Object);
|
||
|
|
||
|
var manager = CreateManager(mockLoader, mockFinder);
|
||
|
|
||
|
manager.PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
|
||
|
mockConfiguration.Verify(m => m.Lock());
|
||
|
Assert.Same(mockConfiguration.Object, manager.GetConfiguration());
|
||
|
mockLoader.Verify(m => m.TryLoadFromConfig(AppConfig.DefaultInstance));
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(DbContext), It.IsAny<IEnumerable<Type>>()));
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void PushConfugiration_pushes_default_configuration_if_no_other_found()
|
||
|
{
|
||
|
var mockLoader = new Mock<DbConfigurationLoader>();
|
||
|
var mockFinder = new Mock<DbConfigurationFinder>();
|
||
|
|
||
|
var manager = CreateManager(mockLoader, mockFinder);
|
||
|
|
||
|
var defaultConfiguration = manager.GetConfiguration();
|
||
|
|
||
|
manager.PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
|
||
|
Assert.NotSame(defaultConfiguration, manager.GetConfiguration());
|
||
|
mockLoader.Verify(m => m.TryLoadFromConfig(AppConfig.DefaultInstance));
|
||
|
mockFinder.Verify(m => m.TryCreateConfiguration(typeof(DbContext), It.IsAny<IEnumerable<Type>>()));
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void AppConfigResolver_is_added_to_pushed_configuration()
|
||
|
{
|
||
|
var mockConfiguration = new Mock<InternalConfiguration>();
|
||
|
var mockLoader = new Mock<DbConfigurationLoader>();
|
||
|
mockLoader.Setup(m => m.TryLoadFromConfig(AppConfig.DefaultInstance)).Returns(mockConfiguration.Object);
|
||
|
mockLoader.Setup(m => m.AppConfigContainsDbConfigurationType(It.IsAny<AppConfig>())).Returns(true);
|
||
|
|
||
|
CreateManager(mockLoader).PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
|
||
|
mockConfiguration.Verify(m => m.AddAppConfigResolver(It.IsAny<AppConfigDependencyResolver>()));
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void PushConfugiration_switches_in_original_root_resolver()
|
||
|
{
|
||
|
var mockConfiguration = new Mock<InternalConfiguration>();
|
||
|
var mockLoader = new Mock<DbConfigurationLoader>();
|
||
|
mockLoader.Setup(m => m.TryLoadFromConfig(AppConfig.DefaultInstance)).Returns(mockConfiguration.Object);
|
||
|
mockLoader.Setup(m => m.AppConfigContainsDbConfigurationType(It.IsAny<AppConfig>())).Returns(true);
|
||
|
|
||
|
var manager = CreateManager(mockLoader);
|
||
|
var defaultConfiguration = manager.GetConfiguration();
|
||
|
|
||
|
manager.PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
|
||
|
mockConfiguration.Verify(m => m.SwitchInRootResolver(defaultConfiguration.RootResolver));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This test makes calls from multiple threads such that we have at least some chance of finding threading
|
||
|
/// issues. As with any test of this type just because the test passes does not mean that the code is
|
||
|
/// correct. On the other hand if this test ever fails (EVEN ONCE) then we know there is a problem to
|
||
|
/// be investigated. DON'T just re-run and think things are okay if the test then passes.
|
||
|
/// </summary>
|
||
|
[Fact]
|
||
|
public void Configurations_can_be_pushed_and_popped_from_multiple_threads_concurrently()
|
||
|
{
|
||
|
for (var i = 0; i < 30; i++)
|
||
|
{
|
||
|
var manager = new DbConfigurationManager(new DbConfigurationLoader(), new DbConfigurationFinder());
|
||
|
var config = manager.GetConfiguration();
|
||
|
|
||
|
ExecuteInParallel(
|
||
|
() =>
|
||
|
{
|
||
|
var appConfig = new AppConfig(new ConnectionStringSettingsCollection());
|
||
|
manager.PushConfiguration(appConfig, typeof(SimpleModelContext));
|
||
|
manager.PopConfiguration(appConfig);
|
||
|
});
|
||
|
|
||
|
Assert.Same(config, manager.GetConfiguration());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class PopConfiguration : TestBase
|
||
|
{
|
||
|
[Fact]
|
||
|
public void PopConfiguration_removes_the_first_configuration_associated_with_the_given_AppConfig()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
var mockInternalConfiguration = new Mock<InternalConfiguration>
|
||
|
{
|
||
|
CallBase = true
|
||
|
};
|
||
|
|
||
|
var mockDbConfiguration = new Mock<DbConfiguration>();
|
||
|
mockDbConfiguration.Setup(m => m.InternalConfiguration).Returns(mockInternalConfiguration.Object);
|
||
|
mockInternalConfiguration.Setup(m => m.Owner).Returns(mockDbConfiguration.Object);
|
||
|
|
||
|
var appConfig1 = AppConfig.DefaultInstance;
|
||
|
var appConfig2 = new AppConfig(ConfigurationManager.ConnectionStrings);
|
||
|
|
||
|
manager.SetConfiguration(mockInternalConfiguration.Object);
|
||
|
|
||
|
manager.PushConfiguration(appConfig1, typeof(DbContext));
|
||
|
var pushed1 = manager.GetConfiguration();
|
||
|
manager.PushConfiguration(appConfig2, typeof(DbContext));
|
||
|
|
||
|
manager.PopConfiguration(appConfig2);
|
||
|
Assert.Same(pushed1, manager.GetConfiguration());
|
||
|
|
||
|
manager.PopConfiguration(appConfig1);
|
||
|
Assert.Same(mockInternalConfiguration.Object, manager.GetConfiguration());
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void PopConfiguration_does_nothing_if_no_configuration_is_associated_with_the_given_AppConfig()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
|
||
|
manager.PushConfiguration(AppConfig.DefaultInstance, typeof(DbContext));
|
||
|
var pushed1 = manager.GetConfiguration();
|
||
|
|
||
|
manager.PopConfiguration(new AppConfig(ConfigurationManager.ConnectionStrings));
|
||
|
Assert.Same(pushed1, manager.GetConfiguration());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class AddOnLockingHandler
|
||
|
{
|
||
|
[Fact]
|
||
|
public void Adding_handler_after_configuration_is_in_use_throws()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
manager.GetConfiguration();
|
||
|
|
||
|
Assert.Equal(
|
||
|
Strings.AddHandlerToInUseConfiguration,
|
||
|
Assert.Throws<InvalidOperationException>(() => manager.AddOnLockingHandler((_, __) => { })).Message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class OnLocking
|
||
|
{
|
||
|
private readonly IDbDependencyResolver _snapshot;
|
||
|
private readonly Mock<InternalConfiguration> _configuration;
|
||
|
|
||
|
public OnLocking()
|
||
|
{
|
||
|
_snapshot = new Mock<IDbDependencyResolver>().Object;
|
||
|
_configuration = CreateMockInternalConfiguration(null, _snapshot);
|
||
|
}
|
||
|
|
||
|
[Fact]
|
||
|
public void OnLocking_calls_all_added_handlers_and_passes_in_correct_configuration()
|
||
|
{
|
||
|
var manager = CreateManager();
|
||
|
|
||
|
manager.OnLocking(_configuration.Object); // No throw when no handlers registered
|
||
|
|
||
|
manager.AddOnLockingHandler(Handler1);
|
||
|
manager.AddOnLockingHandler(Handler2);
|
||
|
manager.AddOnLockingHandler(Handler3);
|
||
|
|
||
|
manager.OnLocking(_configuration.Object);
|
||
|
Assert.Equal(1, Handler1Called);
|
||
|
Assert.Equal(1, Handler2Called);
|
||
|
Assert.Equal(1, Handler3Called);
|
||
|
|
||
|
manager.RemoveOnLockingHandler(Handler2);
|
||
|
|
||
|
manager.OnLocking(_configuration.Object);
|
||
|
Assert.Equal(2, Handler1Called);
|
||
|
Assert.Equal(1, Handler2Called);
|
||
|
Assert.Equal(2, Handler3Called);
|
||
|
}
|
||
|
|
||
|
private int Handler1Called { get; set; }
|
||
|
|
||
|
private void Handler1(object sender, DbConfigurationEventArgs args)
|
||
|
{
|
||
|
Assert.Same(_configuration.Object.Owner, sender);
|
||
|
Assert.Same(_snapshot, args.ResolverSnapshot);
|
||
|
Handler1Called++;
|
||
|
}
|
||
|
|
||
|
private int Handler2Called { get; set; }
|
||
|
|
||
|
private void Handler2(object sender, DbConfigurationEventArgs args)
|
||
|
{
|
||
|
Assert.Same(_configuration.Object.Owner, sender);
|
||
|
Assert.Same(_snapshot, args.ResolverSnapshot);
|
||
|
Handler2Called++;
|
||
|
}
|
||
|
|
||
|
private int Handler3Called { get; set; }
|
||
|
|
||
|
private void Handler3(object sender, DbConfigurationEventArgs args)
|
||
|
{
|
||
|
Assert.Same(_configuration.Object.Owner, sender);
|
||
|
Assert.Same(_snapshot, args.ResolverSnapshot);
|
||
|
Handler3Called++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static DbConfigurationManager CreateManager(
|
||
|
Mock<DbConfigurationLoader> mockLoader = null,
|
||
|
Mock<DbConfigurationFinder> mockFinder = null)
|
||
|
{
|
||
|
return new DbConfigurationManager(
|
||
|
(mockLoader ?? new Mock<DbConfigurationLoader>()).Object,
|
||
|
(mockFinder ?? new Mock<DbConfigurationFinder>()).Object);
|
||
|
}
|
||
|
|
||
|
public class FakeConfiguration : DbConfiguration
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public class FakeContext : DbContext
|
||
|
{
|
||
|
}
|
||
|
|
||
|
private static Mock<InternalConfiguration> CreateMockInternalConfiguration(
|
||
|
DbConfiguration dbConfiguration = null, IDbDependencyResolver snapshot = null)
|
||
|
{
|
||
|
var mockInternalConfiguration = new Mock<InternalConfiguration>();
|
||
|
|
||
|
if (dbConfiguration == null)
|
||
|
{
|
||
|
var mockDbConfiguration = new Mock<DbConfiguration>();
|
||
|
mockDbConfiguration.Setup(m => m.InternalConfiguration).Returns(mockInternalConfiguration.Object);
|
||
|
dbConfiguration = mockDbConfiguration.Object;
|
||
|
}
|
||
|
|
||
|
mockInternalConfiguration.Setup(m => m.Owner).Returns(dbConfiguration);
|
||
|
mockInternalConfiguration.Setup(m => m.ResolverSnapshot).Returns(snapshot);
|
||
|
|
||
|
return mockInternalConfiguration;
|
||
|
}
|
||
|
|
||
|
private static void ConfigurationThreadTest(Action<DbConfigurationManager> beforeThreads, Action<DbConfigurationManager> inThreads)
|
||
|
{
|
||
|
for (var i = 0; i < 30; i++)
|
||
|
{
|
||
|
var configurationBag = new ConcurrentBag<InternalConfiguration>();
|
||
|
var manager = new DbConfigurationManager(new DbConfigurationLoader(), new DbConfigurationFinder());
|
||
|
manager.SetConfiguration(new FunctionalTestsConfiguration().InternalConfiguration);
|
||
|
beforeThreads(manager);
|
||
|
|
||
|
ExecuteInParallel(
|
||
|
() =>
|
||
|
{
|
||
|
inThreads(manager);
|
||
|
configurationBag.Add(manager.GetConfiguration());
|
||
|
});
|
||
|
|
||
|
Assert.Equal(20, configurationBag.Count);
|
||
|
Assert.True(configurationBag.All(c => manager.GetConfiguration() == c));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|