// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. namespace EntityFramework.PowerShell.UnitTests { using System; using System.Collections.Generic; using System.Data.Common; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.ServiceProcess; using System.Xml.Linq; using EnvDTE; using System.Data.Entity.TestHelpers; using Microsoft.Win32; using Moq; using Xunit; public class ConnectionFactoryConfigTests : TestBase { #region Well-known values // Hard-coding this rather than getting it dynamically because the product code gets it dynamically // and the tests need to make sure it gets the correct thing. This will need to be updated when the // assembly version number is incremented. private static readonly Version _builtEntityFrameworkVersion = new Version("6.0.0.0"); private static readonly Version _net45EntityFrameworkVersion = new Version("6.0.0.0"); private static readonly Version _net40EntityFrameworkVersion = new Version("6.0.0.0"); private const string EntityFrameworkSectionFormat = "System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version={0}, Culture=neutral, PublicKeyToken=b77a5c561934e089"; private static readonly string _net45EntityFrameworkSectionName = string.Format( CultureInfo.InvariantCulture, EntityFrameworkSectionFormat, _net45EntityFrameworkVersion); private static readonly string _net40EntityFrameworkSectionName = string.Format( CultureInfo.InvariantCulture, EntityFrameworkSectionFormat, _net40EntityFrameworkVersion); #endregion #region Updating entityFramework section and adding defaultConnectionFactory [Fact] public void ConfigFileProcessor_calls_both_AddConnectionFactoryToConfig_and_AddOrUpdateConfigSection_even_if_both_return_true() { // Test ensures code does not skip calling one method if the other returns true through the use of // short-circuit evaluation. RunTestWithTempFilename( tempFileName => { var mockManipulator = new Mock(); mockManipulator .Setup( m => m.AddConnectionFactoryToConfig( It.IsAny(), It.IsAny())). Returns(true); mockManipulator .Setup(m => m.AddOrUpdateConfigSection(It.IsAny(), It.IsAny())). Returns(true); new XDocument(new XElement("fake")).Save(tempFileName); var mockedItem = new Mock(); mockedItem.Setup(p => p.get_FileNames(0)).Returns(tempFileName); new System.Data.Entity.ConnectionFactoryConfig.ConfigFileProcessor() .ProcessConfigFile( mockedItem.Object, new Func[] { c => mockManipulator.Object.AddOrUpdateConfigSection(c, _net45EntityFrameworkVersion), c => mockManipulator.Object.AddConnectionFactoryToConfig( c, new System.Data.Entity.ConnectionFactoryConfig. ConnectionFactorySpecification("F")) }); mockManipulator.Verify( m => m.AddConnectionFactoryToConfig( It.IsAny(), It.IsAny())); mockManipulator.Verify(m => m.AddOrUpdateConfigSection(It.IsAny(), _net45EntityFrameworkVersion)); }); } [Fact] public void AddSqlCompactConnectionFactoryToConfig_does_nothing_if_correct_SQL_Compact_entry_already_exists() { var config = CreateConnectionFactoryConfigDoc( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator() .AddOrUpdateConnectionFactoryInConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName)); Assert.False(factoryAdded); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, GetFactoryName(config)); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName, GetArgument(config)); } [Fact] public void AddSqlCompactConnectionFactoryToConfig_adds_factory_if_no_factory_name_already_exists() { var config = CreateConnectionFactoryConfigDoc(null); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator() .AddOrUpdateConnectionFactoryInConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName)); Assert.True(factoryAdded); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, GetFactoryName(config)); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName, GetArgument(config)); } [Fact] public void AddSqlCompactConnectionFactoryToConfig_adds_factory_if_entityFramework_element_is_missing() { var config = new XDocument( new XElement(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName)); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator() .AddOrUpdateConnectionFactoryInConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName)); Assert.True(factoryAdded); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, GetFactoryName(config)); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName, GetArgument(config)); } [Fact] public void AddSqlCompactConnectionFactoryToConfig_adds_factory_if_configuration_element_is_missing() { var config = new XDocument(); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator() .AddOrUpdateConnectionFactoryInConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName)); Assert.True(factoryAdded); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, GetFactoryName(config)); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName, GetArgument(config)); } [Fact] public void AddSqlCompactConnectionFactoryToConfig_sets_factory_to_SQL_Compact_even_if_entry_already_exists() { var config = CreateConnectionFactoryConfigDoc("SomeConnectionFactory"); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator() .AddOrUpdateConnectionFactoryInConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName)); Assert.True(factoryAdded); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, GetFactoryName(config)); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName, GetArgument(config)); } [Fact] public void AddSqlCompactConnectionFactoryToConfig_sets_factory_to_SQL_Compact_even_if_entry_with_param_already_exists() { var config = CreateConnectionFactoryConfigDoc( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlConnectionFactoryName, "Database=Bob"); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator() .AddOrUpdateConnectionFactoryInConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName)); Assert.True(factoryAdded); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCeConnectionFactoryName, GetFactoryName(config)); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlCompactProviderName, GetArgument(config)); } [Fact] public void AddConnectionFactoryToConfig_does_nothing_if_factory_name_already_exists() { var config = CreateConnectionFactoryConfigDoc("SomeConnectionFactory"); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator(). AddConnectionFactoryToConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( "NewConnectionFactory", "NewBaseConnectionString")); Assert.False(factoryAdded); Assert.Equal("SomeConnectionFactory", GetFactoryName(config)); } [Fact] public void AddConnectionFactoryToConfig_adds_factory_if_no_factory_name_already_exists() { var config = CreateConnectionFactoryConfigDoc(null); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator(). AddConnectionFactoryToConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( "NewConnectionFactory", "NewBaseConnectionString")); Assert.True(factoryAdded); Assert.Equal("NewConnectionFactory", GetFactoryName(config)); Assert.Equal("NewBaseConnectionString", GetArgument(config)); } [Fact] public void AddConnectionFactoryToConfig_adds_factory_if_entityFramework_element_is_missing() { var config = new XDocument( new XElement(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName)); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator(). AddConnectionFactoryToConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( "NewConnectionFactory", "NewBaseConnectionString")); Assert.True(factoryAdded); Assert.Equal("NewConnectionFactory", GetFactoryName(config)); Assert.Equal("NewBaseConnectionString", GetArgument(config)); } [Fact] public void AddConnectionFactoryToConfig_adds_factory_if_configuration_element_is_missing() { var config = new XDocument(); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator(). AddConnectionFactoryToConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( "NewConnectionFactory", "NewBaseConnectionString")); Assert.True(factoryAdded); Assert.Equal("NewConnectionFactory", GetFactoryName(config)); Assert.Equal("NewBaseConnectionString", GetArgument(config)); } [Fact] public void AddConnectionFactoryToConfig_adds_factory_with_no_parameters() { var config = new XDocument(); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator(). AddConnectionFactoryToConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification("NewConnectionFactory")); Assert.True(factoryAdded); Assert.Equal("NewConnectionFactory", GetFactoryName(config)); Assert.Null( config.Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName) .Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.EntityFrameworkElementName) .Element( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.DefaultConnectionFactoryElementName) .Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ParametersElementName)); } [Fact] public void AddConnectionFactoryToConfig_adds_factory_with_many_parameters() { var config = new XDocument(); var factoryAdded = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator(). AddConnectionFactoryToConfig( config, new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification( "NewConnectionFactory", "1", "2", "3")); Assert.True(factoryAdded); Assert.Equal("NewConnectionFactory", GetFactoryName(config)); Assert.Equal("1", GetArguments(config).First()); Assert.Equal("2", GetArguments(config).Skip(1).First()); Assert.Equal("3", GetArguments(config).Skip(2).First()); } private XDocument CreateConnectionFactoryConfigDoc(string factoryName) { return new XDocument( new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName, new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.EntityFrameworkElementName, factoryName != null ? new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator. DefaultConnectionFactoryElementName, new XAttribute("type", factoryName)) : null))); } private XDocument CreateConnectionFactoryConfigDoc(string factoryName, string param) { return new XDocument( new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName, new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.EntityFrameworkElementName, new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.DefaultConnectionFactoryElementName, new XAttribute("type", factoryName), new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ParametersElementName, new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ParameterElementName, new XAttribute("value", param))))))); } private string GetFactoryName(XDocument config) { return config.Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName) .Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.EntityFrameworkElementName) .Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.DefaultConnectionFactoryElementName) .Attribute("type") .Value; } private string GetArgument(XDocument config) { return GetArguments(config).Single(); } private IEnumerable GetArguments(XDocument config) { return config.Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName) .Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.EntityFrameworkElementName) .Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.DefaultConnectionFactoryElementName) .Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ParametersElementName) .Elements(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ParameterElementName) .Select(e => e.Attribute("value").Value); } [Fact] public void AddOrUpdateConfigSection_does_nothing_if_EF_assembly_name_is_up_to_date() { var config = CreateConfigSectionDoc(_net45EntityFrameworkSectionName, addRequirePermission: true); var sectionModified = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net45EntityFrameworkVersion); Assert.False(sectionModified); Assert.Equal(_net45EntityFrameworkSectionName, GetEfSectionName(config)); } [Fact] public void AddOrUpdateConfigSection_adds_EF_section_if_configuration_element_is_missing() { var config = new XDocument(); var sectionModified = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net45EntityFrameworkVersion); Assert.True(sectionModified); Assert.Equal(_net45EntityFrameworkSectionName, GetEfSectionName(config)); } [Fact] public void AddOrUpdateConfigSection_adds_EF_section_if_configSections_element_is_missing() { var config = new XDocument( new XElement(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName)); var sectionModified = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net45EntityFrameworkVersion); Assert.True(sectionModified); Assert.Equal(_net45EntityFrameworkSectionName, GetEfSectionName(config)); } [Fact] public void AddOrUpdateConfigSection_adds_EF_section_if_configSections_element_contains_no_entries() { var config = new XDocument( new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName, new XElement(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigSectionsElementName))); var sectionModified = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net45EntityFrameworkVersion); Assert.True(sectionModified); Assert.Equal(_net45EntityFrameworkSectionName, GetEfSectionName(config)); } [Fact] public void AddOrUpdateConfigSection_adds_EF_section_if_configSections_element_has_no_entityFramework_entry() { var config = CreateConfigSectionDoc(assemblyName: null); var sectionModified = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net45EntityFrameworkVersion); Assert.True(sectionModified); Assert.Equal(_net45EntityFrameworkSectionName, GetEfSectionName(config)); } [Fact] public void AddOrUpdateConfigSection_updates_EF_section_if_configSections_element_is_out_of_date() { var config = CreateConfigSectionDoc(_net40EntityFrameworkSectionName); var sectionModified = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net45EntityFrameworkVersion); Assert.True(sectionModified); Assert.Equal(_net45EntityFrameworkSectionName, GetEfSectionName(config)); } [Fact] public void AddOrUpdateConfigSection_when_using_NET4_EF_assembly_does_nothing_if_EF_assembly_name_is_up_to_date() { var config = CreateConfigSectionDoc(_net40EntityFrameworkSectionName, addRequirePermission: true); var sectionModified = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net40EntityFrameworkVersion); Assert.False(sectionModified); Assert.Equal(_net40EntityFrameworkSectionName, GetEfSectionName(config)); } [Fact] public void AddOrUpdateConfigSection_when_using_NET4_EF_assembly_updates_EF_section_if_configSections_element_is_too_new() { var config = CreateConfigSectionDoc(_net45EntityFrameworkSectionName); var sectionModified = new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net40EntityFrameworkVersion); Assert.True(sectionModified); Assert.Equal(_net40EntityFrameworkSectionName, GetEfSectionName(config)); } [Fact] public void AddOrUpdateConfigSection_on_add_yields_result_that_can_load_in_partial_trust() { AddOrUpdateConfigSection_result_can_load_in_partial_trust( new XDocument( new XElement(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName))); } [Fact] public void AddOrUpdateConfigSection_on_update_yields_result_that_can_load_in_partial_trust() { AddOrUpdateConfigSection_result_can_load_in_partial_trust( CreateConfigSectionDoc(_net40EntityFrameworkSectionName)); } private void AddOrUpdateConfigSection_result_can_load_in_partial_trust(XDocument config) { new System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator().AddOrUpdateConfigSection( config, _net45EntityFrameworkVersion); config.Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName).Add( new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.EntityFrameworkElementName, new XAttribute("codeConfigurationType", typeof(FunctionalTestsConfiguration).AssemblyQualifiedName))); var configurationFile = Path.Combine(Environment.CurrentDirectory, "Temp.config"); config.Save(configurationFile); try { using (var sandbox = new PartialTrustSandbox(configurationFile: configurationFile)) { sandbox.CreateInstance() .LoadConfiguration(); } } finally { File.Delete(configurationFile); } } private void LoadConfiguration() { new PartialTrustContext(); } private class PartialTrustContext : DbContext { } private XDocument CreateConfigSectionDoc(string assemblyName, bool addRequirePermission = false) { var dummyElement = new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.SectionElementName, new XAttribute("name", "SamVimes"), new XAttribute("type", "Treacle Mine Road")); XElement sectionElement; if (assemblyName == null) { sectionElement = dummyElement; } else { sectionElement = new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.SectionElementName, new XAttribute( "name", System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.EntityFrameworkElementName), new XAttribute("type", assemblyName)); if (addRequirePermission) { sectionElement.Add(new XAttribute("requirePermission", false)); } } return new XDocument( new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName, new XElement( System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigSectionsElementName, dummyElement, sectionElement))); } private string GetEfSectionName(XDocument config) { return config.Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigurationElementName) .Element(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.ConfigSectionsElementName) .Elements(System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.SectionElementName) .Where( e => e.Attributes("name").Any( a => a.Value == System.Data.Entity.ConnectionFactoryConfig.ConfigFileManipulator.EntityFrameworkElementName)) .Select(e => e.Attribute("type").Value) .Single(); } #endregion #region Config file processing tests [Fact] public void ConfigFileFinder_finds_items_named_app_config_and_web_config_in_Visual_Studio_ProjectItems() { var items = new List(); new System.Data.Entity.ConnectionFactoryConfig.ConfigFileFinder() .FindConfigFiles( CreateMockedItems("app.config", "App.config", "Foo.config", "web.config", "Web.config", "Bar"), i => items.Add(i)); Assert.Equal(4, items.Count); Assert.True(items.Any(i => i.Name == "app.config")); Assert.True(items.Any(i => i.Name == "App.config")); Assert.True(items.Any(i => i.Name == "web.config")); Assert.True(items.Any(i => i.Name == "Web.config")); } private ProjectItems CreateMockedItems(params string[] itemNames) { var items = new List(); foreach (var name in itemNames) { var mockedItem = new Mock(); mockedItem.Setup(p => p.Name).Returns(name); items.Add(mockedItem.Object); } var mockedProjectItems = new Mock(); mockedProjectItems.Setup(p => p.GetEnumerator()).Returns(items.GetEnumerator()); return mockedProjectItems.Object; } [Fact] public void ConfigFileProcessor_saves_config_file_if_config_document_is_modified() { ConfigFileSaveTest(shouldSave: true); } [Fact] public void ConfigFileProcessor_does_not_save_config_file_if_config_document_is_not_modified() { ConfigFileSaveTest(shouldSave: false); } [Fact] public void If_config_file_cannot_be_saved_when_it_needs_to_be_then_an_exception_is_thrown() { ConfigFileSaveTest(shouldSave: true, writeProtectFile: true); } private void RunTestWithTempFilename(Action test) { var tempFileName = Path.GetTempFileName(); try { test(tempFileName); } finally { try { File.SetAttributes(tempFileName, File.GetAttributes(tempFileName) & ~FileAttributes.ReadOnly); File.Delete(tempFileName); } catch (FileNotFoundException) { } } } private void ConfigFileSaveTest(bool shouldSave, bool writeProtectFile = false) { RunTestWithTempFilename( tempFileName => { new XDocument(new XElement("fake")).Save(tempFileName); if (writeProtectFile) { File.SetAttributes(tempFileName, File.GetAttributes(tempFileName) | FileAttributes.ReadOnly); } var mockedItem = new Mock(); mockedItem.Setup(p => p.get_FileNames(0)).Returns(tempFileName); var mockedManipulator = new Mock(); mockedManipulator .Setup( m => m.AddConnectionFactoryToConfig( It.IsAny(), It.IsAny())) .Callback( (XDocument config, System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification _) => config.Element("fake").Add(new XElement("modified"))) .Returns(shouldSave); Assert.ThrowsDelegate test = () => new System.Data.Entity.ConnectionFactoryConfig.ConfigFileProcessor() .ProcessConfigFile( mockedItem.Object, new Func[] { c => mockedManipulator.Object.AddOrUpdateConfigSection(c, _net45EntityFrameworkVersion), c => mockedManipulator.Object.AddConnectionFactoryToConfig( c, new System.Data.Entity.ConnectionFactoryConfig. ConnectionFactorySpecification("F")) }); if (shouldSave && writeProtectFile) { Assert.Equal( System.Data.Entity.Migrations.Resources.Strings.SaveConnectionFactoryInConfigFailed( tempFileName), Assert.Throws(test).Message); } else { test(); } var doc = XDocument.Load(tempFileName); Assert.Equal(shouldSave && !writeProtectFile, doc.Element("fake").Elements("modified").Any()); }); } #endregion #region SQL Server detection tests [Fact] public void SqlServerDetector_detects_SQL_Express_when_service_is_running() { Assert.True( new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( new Mock().Object, CreateMockedController()).IsSqlExpressInstalled()); } [Fact] public void SqlServerDetector_detects_no_SQL_Express_when_service_is_present_but_not_running() { Assert.False( new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( new Mock().Object, CreateMockedController(ServiceControllerStatus.Stopped)).IsSqlExpressInstalled()); } [Fact] public void SqlServerDetector_detects_no_SQL_Express_when_service_is_not_found() { Assert.False( new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( new Mock().Object, CreateMockedController(status: null)).IsSqlExpressInstalled()); } private System.Data.Entity.ConnectionFactoryConfig.ServiceControllerProxy CreateMockedController( ServiceControllerStatus? status = ServiceControllerStatus.Running) { var mockController = new Mock(); if (status != null) { mockController.Setup(m => m.Status).Returns(status.Value); } else { mockController.Setup(m => m.Status).Throws(new InvalidOperationException()); } return mockController.Object; } [Fact] public void SqlServerDetector_detects_the_version_of_LocalDB_in_the_registry_when_only_one_version_is_installed() { Assert.Equal( "some version", new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey("some version"), new Mock().Object) .TryGetLocalDBVersionInstalled()); } [Fact] public void SqlServerDetector_detects_the_highest_orderable_version_of_LocalDB_in_the_registry_when_multiple_versions_are_installed( ) { Assert.Equal( "12.0", new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey("11.0", "12.0", "11.5"), new Mock().Object) .TryGetLocalDBVersionInstalled()); } [Fact] public void SqlServerDetector_orders_LocalDB_versions_numerically_when_multiple_versions_are_installed() { Assert.Equal( "100", new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey("20", "100"), new Mock().Object) .TryGetLocalDBVersionInstalled()); } [Fact] public void SqlServerDetector_ignores_LocalDB_versions_that_are_not_numeric_when_multiple_versions_are_installed() { Assert.Equal( "12.0", new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey("11.0", "12.0", "dingo", "11.5"), new Mock().Object) .TryGetLocalDBVersionInstalled()); } [Fact] public void SqlServerDetector_returns_null_if_multiple_non_numeric_LocalDB_versions_are_installed() { Assert.Null( new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey("kangaroo", "dingo", "wallaby"), new Mock().Object) .TryGetLocalDBVersionInstalled()); } [Fact] public void SqlServerDetector_returns_null_if_LocalDB_registry_exists_but_no_version_keys_are_found() { Assert.Null( new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey(new string[0]), new Mock().Object) .TryGetLocalDBVersionInstalled()); } private System.Data.Entity.ConnectionFactoryConfig.RegistryKeyProxy CreatedMockedRegistryKey(params string[] versions) { var mockedRegistryKey = new Mock(); mockedRegistryKey.Setup(k => k.OpenSubKey("SOFTWARE")).Returns(mockedRegistryKey.Object); mockedRegistryKey.Setup(k => k.OpenSubKey("Microsoft")).Returns(mockedRegistryKey.Object); mockedRegistryKey.Setup(k => k.OpenSubKey("Microsoft SQL Server Local DB")).Returns(mockedRegistryKey.Object); mockedRegistryKey.Setup(k => k.OpenSubKey("Installed Versions")).Returns(mockedRegistryKey.Object); mockedRegistryKey.Setup(k => k.SubKeyCount).Returns(versions.Length); mockedRegistryKey.Setup(k => k.GetSubKeyNames()).Returns(versions); mockedRegistryKey.Setup(k => k.OpenSubKey("Wow6432Node")).Returns( new System.Data.Entity.ConnectionFactoryConfig.RegistryKeyProxy(null)); return mockedRegistryKey.Object; } [Fact] public void SqlServerDetector_returns_null_if_LocalDB_registry_keys_do_not_exist() { var mockedRegistryKey = new Mock(); mockedRegistryKey.Setup(k => k.OpenSubKey("SOFTWARE")).Returns(mockedRegistryKey.Object); mockedRegistryKey.Setup(k => k.OpenSubKey("Microsoft")).Returns(mockedRegistryKey.Object); mockedRegistryKey.Setup(k => k.OpenSubKey("Microsoft SQL Server Local DB")).Returns( new System.Data.Entity.ConnectionFactoryConfig.RegistryKeyProxy(null)); mockedRegistryKey.Setup(k => k.OpenSubKey("Wow6432Node")).Returns( new System.Data.Entity.ConnectionFactoryConfig.RegistryKeyProxy(null)); Assert.Null( new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( mockedRegistryKey.Object, new Mock().Object) .TryGetLocalDBVersionInstalled()); } [Fact] public void SqlServerDetector_detects_LocalDB_installation_in_Wow6432Node_if_not_found_in_normal_hive() { var mockedRegistryKey = new Mock(); mockedRegistryKey.Setup(k => k.OpenSubKey("SOFTWARE")).Returns(mockedRegistryKey.Object); mockedRegistryKey.Setup(k => k.OpenSubKey("Microsoft")).Returns(mockedRegistryKey.Object); mockedRegistryKey.Setup(k => k.OpenSubKey("Microsoft SQL Server Local DB")).Returns( new System.Data.Entity.ConnectionFactoryConfig.RegistryKeyProxy(null)); var mockedWow64RegistryKey = new Mock(); mockedRegistryKey.Setup(k => k.OpenSubKey("Wow6432Node")).Returns(mockedWow64RegistryKey.Object); mockedWow64RegistryKey.Setup(k => k.OpenSubKey("Microsoft")).Returns(mockedWow64RegistryKey.Object); mockedWow64RegistryKey.Setup(k => k.OpenSubKey("Microsoft SQL Server Local DB")).Returns(mockedWow64RegistryKey.Object); mockedWow64RegistryKey.Setup(k => k.OpenSubKey("Installed Versions")).Returns(mockedWow64RegistryKey.Object); mockedWow64RegistryKey.Setup(k => k.SubKeyCount).Returns(1); mockedWow64RegistryKey.Setup(k => k.GetSubKeyNames()).Returns(new[] { "64BitVersion" }); Assert.Equal( "64BitVersion", new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( mockedRegistryKey.Object, new Mock().Object) .TryGetLocalDBVersionInstalled()); } [Fact] public void SqlServerDetector_disposes_RegisterKey_and_ManagementObjectSearcher_when_it_is_disposed() { var mockedRegistryKey = new Mock(); var mockedSearcher = new Mock(); new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector(mockedRegistryKey.Object, mockedSearcher.Object). Dispose(); mockedRegistryKey.Verify(k => k.Dispose()); mockedSearcher.Verify(s => s.Dispose()); } [Fact] public void SqlServerDetector_generates_SQL_Express_base_connection_string_if_both_Express_and_LocalDB_are_installed() { var specification = new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey("11.0"), CreateMockedController()) .BuildConnectionFactorySpecification(); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlConnectionFactoryName, specification.ConnectionFactoryName); Assert.Empty(specification.ConstructorArguments); } [Fact] public void SqlServerDetector_generates_SQL_Express_base_connection_string_if_Express_is_installed_and_LocalDB_is_not() { var specification = new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey(new string[0]), CreateMockedController()) .BuildConnectionFactorySpecification(); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlConnectionFactoryName, specification.ConnectionFactoryName); Assert.Empty(specification.ConstructorArguments); } [Fact] public void SqlServerDetector_generates_LocalDB_base_connection_string_if_LocalDB_is_installed_and_Express_is_not() { var specification = new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey("12.0"), CreateMockedController(status: null)) .BuildConnectionFactorySpecification(); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.LocalDbConnectionFactoryName, specification.ConnectionFactoryName); Assert.Equal("v12.0", specification.ConstructorArguments.Single()); } [Fact] public void SqlServerDetector_generates_LocalDB_11_base_connection_string_if_neither_LocalDB_or_Express_are_installed() { var specification = new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( CreatedMockedRegistryKey(new string[0]), CreateMockedController(status: null)) .BuildConnectionFactorySpecification(); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.LocalDbConnectionFactoryName, specification.ConnectionFactoryName); Assert.Equal("v11.0", specification.ConstructorArguments.Single()); } [Fact] public void SqlServerDetector_detects_SQL_Express_on_dev_machine() { using ( var detector = new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( new Mock().Object, new System.Data.Entity.ConnectionFactoryConfig.ServiceControllerProxy( new ServiceController("MSSQL$SQLEXPRESS")))) { Assert.True(detector.IsSqlExpressInstalled()); } } #if !NET40 [Fact] public void SqlServerDetector_detects_LocalDB_v11_0_on_dev_machine() { using ( var detector = new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( Registry.LocalMachine, new Mock().Object) ) { Assert.Equal("11.0", detector.TryGetLocalDBVersionInstalled()); } } #endif [Fact] public void Base_connection_string_on_dev_box_with_SQL_Express_installed_has_SQL_Express_connection_string() { using ( var detector = new System.Data.Entity.ConnectionFactoryConfig.SqlServerDetector( Registry.LocalMachine, new System.Data.Entity.ConnectionFactoryConfig.ServiceControllerProxy( new ServiceController("MSSQL$SQLEXPRESS")))) { var specification = detector.BuildConnectionFactorySpecification(); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig.ConnectionFactorySpecification.SqlConnectionFactoryName, specification.ConnectionFactoryName); Assert.Empty(specification.ConstructorArguments); } } [Fact] public void ConnectionFactoryConfigurator_throws_when_passed_null_Project() { Assert.Equal( "project", Assert.Throws( () => new System.Data.Entity.ConnectionFactoryConfig.ConnectionFactoryConfigurator(null)).ParamName); } #endregion #region Tests using real Visual Studio objects [Fact] public void Default_connection_factory_is_added_to_real_Visual_Studio_project_and_config_file() { var configFilesFound = new List(); Run_Project_test_if_Visual_Studio_is_running( p => { new System.Data.Entity.ConnectionFactoryConfig.ConfigFileFinder().FindConfigFiles( p.ProjectItems, i => { configFilesFound.Add(i.Name); var config = XDocument.Load(i.FileNames[0]); // Checked in app.config for unit tests has no connection factory, so one should be added var modified = new System.Data.Entity.ConnectionFactoryConfig. ConfigFileManipulator().AddConnectionFactoryToConfig( config, new System.Data.Entity.ConnectionFactoryConfig. ConnectionFactorySpecification( System.Data.Entity.ConnectionFactoryConfig. ConnectionFactorySpecification.SqlConnectionFactoryName, "SomeConnectionString")); Assert.True(modified); Assert.Equal( System.Data.Entity.ConnectionFactoryConfig. ConnectionFactorySpecification.SqlConnectionFactoryName, GetFactoryName(config)); Assert.Equal("SomeConnectionString", GetArgument(config)); }); Assert.Equal(1, configFilesFound.Count); Assert.Equal("App.config", configFilesFound.Single()); }); } [Fact] public void GetProjectTypes_returns_project_types() { Run_Project_test_if_Visual_Studio_is_running( p => { var types = System.Data.Entity.Migrations.Extensions.ProjectExtensions.GetProjectTypes(p); Assert.Equal(new[] { "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" }, types); }); } private void Run_Project_test_if_Visual_Studio_is_running(Action test) { MessageFilter.Register(); try { var dte = (DTE)Marshal.GetActiveObject("VisualStudio.DTE.11.0"); var project = TryGetPowerShellUnitTests(dte); if (project != null) { test(project); } } catch (COMException) { // This is thrown when running as part of a razzle build. The test doesn't work in // the razzle environment. } finally { MessageFilter.Revoke(); } } private static Project TryGetPowerShellUnitTests(DTE dte) { return dte .Solution .Projects .OfType() .Where(p => p.Name == "Tests") .SelectMany(p => p.ProjectItems.OfType()) .Where(p => p.Name == "UnitTests") .Select(p => p.SubProject) .FirstOrDefault(); } #endregion } #region Fake connection factories public abstract class FakeBaseConnectionFactory : IDbConnectionFactory { public DbConnection CreateConnection(string nameOrConnectionString) { throw new NotImplementedException(); } } public class FakeConnectionFactoryNoParams : FakeBaseConnectionFactory { } public class FakeConnectionFactoryOneParam : FakeBaseConnectionFactory { public string Arg { get; set; } public FakeConnectionFactoryOneParam(string arg) { Arg = arg; } } public class FakeConnectionFactoryManyParams : FakeBaseConnectionFactory { public List Args { get; set; } public FakeConnectionFactoryManyParams( string arg0, string arg1, string arg2, string arg3, string arg4, string arg5, string arg6, string arg7, string arg8, string arg9, string arg10) { Args = new List { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 }; } } public class FakeNonConnectionFactory { } #endregion #region Visual Studio threading helpers /// /// This class handles re-tries that can be required when calling into Visual Studio /// from a non-VS thread. /// See http://msdn.microsoft.com/en-us/library/ms228772(v=VS.100).aspx /// public class MessageFilter : IOleMessageFilter { public static void Register() { IOleMessageFilter _; CoRegisterMessageFilter(new MessageFilter(), out _); } public static void Revoke() { IOleMessageFilter _; CoRegisterMessageFilter(null, out _); } int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo) { return 0; } int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType) { if (dwRejectType == 2) { // Retry the thread call immediately if return >= 0 & < 100. return 99; } // Too busy; cancel call. return -1; } int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType) { return 2; } [DllImport("Ole32.dll")] private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter); } [ComImport] [Guid("00000016-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IOleMessageFilter { [PreserveSig] int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); [PreserveSig] int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType); [PreserveSig] int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType); } #endregion }