// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. namespace ProductivityApiUnitTests { using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Internal; using System.Data.Entity.Internal.MockingProxies; using System.Data.SqlClient; using System.Reflection; using Moq; using Xunit; public class ClonedObjectContextTests : TestBase { [Fact] public void Creating_a_cloned_ObjectContext_causes_the_store_and_entity_connection_to_be_cloned_and_given_connection_string_applied( ) { var storeConnection = new SqlConnection(); var mockConnection = CreateMockConnection(storeConnection); var mockContext = CreateMockObjectContext(mockConnection); new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo"); mockConnection.Verify( m => m.CreateNew( It.Is( p => p != null && !ReferenceEquals(p, storeConnection) && p.ConnectionString == "Database=PinkyDinkyDo"))); } [Fact] public void Cloning_an_ObjectContext_with_a_default_container_name_copies_that_container_name() { var mockClonedContext = new Mock(); var mockContext = CreateMockObjectContext(CreateMockConnection(), mockClonedContext); mockContext.Setup(m => m.DefaultContainerName).Returns("Kipper"); var clonedContext = new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo"); mockClonedContext.VerifySet(m => m.DefaultContainerName = It.IsAny()); Assert.Equal("Kipper", clonedContext.ObjectContext.DefaultContainerName); } [Fact] public void Cloning_an_ObjectContext_with_a_null_default_container_name_results_in_a_clone_without_a_default_container_name() { var mockClonedContext = new Mock(); var mockContext = CreateMockObjectContext(CreateMockConnection(), mockClonedContext); var clonedContext = new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo"); mockClonedContext.VerifySet(m => m.DefaultContainerName = It.IsAny(), Times.Never()); Assert.Null(clonedContext.ObjectContext.DefaultContainerName); } [Fact] public void Cloning_an_ObjectContext_with_a_whitespace_default_container_name_results_in_a_clone_without_a_default_container_name() { var mockClonedContext = new Mock(); var mockContext = CreateMockObjectContext(CreateMockConnection(), mockClonedContext); mockContext.Setup(m => m.DefaultContainerName).Returns(" "); var clonedContext = new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo"); mockClonedContext.VerifySet(m => m.DefaultContainerName = It.IsAny(), Times.Never()); Assert.Null(clonedContext.ObjectContext.DefaultContainerName); } [Fact] public void ClonedObjectContext_Connection_returns_the_cloned_store_connection() { var storeConnection = new SqlConnection(); var mockContext = CreateMockObjectContext(CreateMockConnection(storeConnection)); var clonedConnection = new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo").Connection; Assert.NotSame(storeConnection, clonedConnection); Assert.Equal("Database=PinkyDinkyDo", clonedConnection.ConnectionString); Assert.Same(storeConnection.GetType(), clonedConnection.GetType()); } [Fact] public void ClonedObjectContext_ObjectContext_returns_the_cloned_ObjectContext() { var mockClonedContext = new Mock(); var mockContext = CreateMockObjectContext(CreateMockConnection(), mockClonedContext); var clonedContext = new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo"); mockContext.Verify(m => m.CreateNew(It.IsAny())); Assert.Same(mockClonedContext.Object, clonedContext.ObjectContext); } [Fact] public void When_cloning_an_ObjectContext_without_transfering_assemblies_no_assemblies_are_transfered() { var mockClonedContext = new Mock(); var mockContext = CreateMockObjectContext(CreateMockConnection(), mockClonedContext); new ClonedObjectContext( mockContext.Object, "Database=PinkyDinkyDo", transferLoadedAssemblies: false); mockContext.Verify(m => m.GetObjectItemCollection(), Times.Never()); mockClonedContext.Verify(m => m.LoadFromAssembly(It.IsAny()), Times.Never()); } [Fact] public void When_cloning_an_ObjectContext_with_assemblies_enum_complex_and_entity_type_assemblies_are_transfered_but_others_are_ignored() { var mockClonedContext = new Mock(); var mockContext = CreateMockObjectContext(CreateMockConnection(), mockClonedContext); new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo"); mockContext.Verify(m => m.GetObjectItemCollection(), Times.Once()); mockClonedContext.Verify(m => m.LoadFromAssembly(It.Is(a => a.FullName == "ComplexType Assembly"))); mockClonedContext.Verify(m => m.LoadFromAssembly(It.Is(a => a.FullName == "EntityType Assembly"))); mockClonedContext.Verify(m => m.LoadFromAssembly(It.Is(a => a.FullName == "EnumType Assembly"))); } [Fact] public void Disposing_a_cloned_ObjectContext_disposes_both_the_context_and_the_connection() { var mockClonedContext = new Mock(); var mockContext = CreateMockObjectContext(CreateMockConnection(), mockClonedContext); var clonedContext = new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo"); var connectionIsDisposed = false; clonedContext.Connection.Disposed += (_, __) => connectionIsDisposed = true; clonedContext.Dispose(); mockClonedContext.Verify(m => m.Dispose()); Assert.True(connectionIsDisposed); } [Fact] public void Calling_Dispose_on_an_already_disposed_cloned_ObjectContext_does_nothing() { var mockClonedContext = new Mock(); var mockContext = CreateMockObjectContext(CreateMockConnection(), mockClonedContext); var clonedContext = new ClonedObjectContext(mockContext.Object, "Database=PinkyDinkyDo"); var connectionIsDisposed = false; clonedContext.Connection.Disposed += (_, __) => connectionIsDisposed = true; clonedContext.Dispose(); connectionIsDisposed = false; clonedContext.Dispose(); mockClonedContext.Verify(m => m.Dispose(), Times.Once()); Assert.False(connectionIsDisposed); } private Mock CreateMockConnection(SqlConnection storeConnection = null) { storeConnection = storeConnection ?? new SqlConnection(); var mockConnection = new Mock(); mockConnection.Setup(m => m.StoreConnection).Returns(storeConnection); mockConnection.Setup(m => m.CreateNew(It.IsAny())).Returns( c => { var mockClonedConnection = new Mock(); mockClonedConnection.Setup(cc => cc.StoreConnection).Returns(c); return mockClonedConnection.Object; }); return mockConnection; } private Mock CreateMockObjectContext( Mock mockConnection = null, Mock mockClonedContext = null) { mockConnection = mockConnection ?? CreateMockConnection(); mockClonedContext = mockClonedContext ?? new Mock(); mockClonedContext.SetupProperty(m => m.DefaultContainerName); var mockContext = new Mock(); mockContext.Setup(m => m.Connection).Returns(mockConnection.Object); mockContext.Setup(m => m.GetObjectItemCollection()).Returns( new List { CreateFakeComplexType(), CreateFakeEntityType(), CreateFakeEnumType(), CreateFakePrimitiveType(), }); // Ensure fake EDM types have fake CLR types and assemblies SetupMockStructuralType(mockContext); SetupMockStructuralType(mockContext); SetupMockEnumType(mockContext); mockContext.Setup(m => m.CreateNew(It.IsAny())).Returns( c => { mockClonedContext.Setup(cc => cc.Connection).Returns(c); return mockClonedContext.Object; }); return mockContext; } private void SetupMockStructuralType(Mock mockContext) where TEdmType : StructuralType { var mockClrType = new Mock(); mockClrType.Setup(m => m.Assembly).Returns(new FakeAssembly(typeof(TEdmType).Name + " Assembly")); mockContext.Setup(m => m.GetClrType(It.IsAny())).Returns(mockClrType.Object); } private void SetupMockEnumType(Mock mockContext) { var mockClrType = new Mock(); mockClrType.Setup(m => m.Assembly).Returns(new FakeAssembly("EnumType Assembly")); mockContext.Setup(m => m.GetClrType(It.IsAny())).Returns(mockClrType.Object); } private ComplexType CreateFakeComplexType() { return (ComplexType)Activator.CreateInstance(typeof(ComplexType), nonPublic: true); } private EnumType CreateFakeEnumType() { return (EnumType)Activator.CreateInstance(typeof(EnumType), nonPublic: true); } private PrimitiveType CreateFakePrimitiveType() { return (PrimitiveType)Activator.CreateInstance(typeof(PrimitiveType), nonPublic: true); } private EntityType CreateFakeEntityType() { const BindingFlags bindingFlags = BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Instance; return (EntityType)Activator.CreateInstance( typeof(EntityType), bindingFlags, null, new object[] { "FakeEntity", "FakeNamespace", DataSpace.OSpace }, null); } /// /// Used because Moq cannot mock Assembly. /// public class FakeAssembly : Assembly { private readonly string _name; public FakeAssembly(string name) { _name = name; } public override string FullName { get { return _name; } } } } }