// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

namespace System.Data.Entity.Metadata
{
    using System.Data.Entity.Core.EntityClient;
    using System.Data.Entity.Core.Metadata.Edm;
    using System.Data.Entity.Migrations;
    using Xunit;

    public class MetadataCachingTests : FunctionalTestBase, IUseFixture<MetadataCachingTestsFixture>
    {
        private static readonly string connectionString = string.Format(
            @"metadata=res://EntityFramework.FunctionalTests/System.Data.Entity.Metadata.MetadataCachingModel.csdl|res://EntityFramework.FunctionalTests/System.Data.Entity.Metadata.MetadataCachingModel.ssdl|res://EntityFramework.FunctionalTests/System.Data.Entity.Metadata.MetadataCachingModel.msl;provider=System.Data.SqlClient;provider connection string=""{0}""",
            ModelHelpers.SimpleConnectionString("MetadataCachingTests"));

        public void SetFixture(MetadataCachingTestsFixture data)
        {
        }

        [Fact]
        public void Verify_that_metadata_is_the_same_for_two_workspaces_created_from_two_entity_connections_with_same_connection_strings()
        {
            var connection1 = new EntityConnection(connectionString);
            var connection2 = new EntityConnection(connectionString);
            var workspace1 = connection1.GetMetadataWorkspace();
            var workspace2 = connection2.GetMetadataWorkspace();

            Assert.Same(workspace1.GetItemCollection(DataSpace.CSpace), workspace2.GetItemCollection(DataSpace.CSpace));
            Assert.Same(workspace1.GetItemCollection(DataSpace.SSpace), workspace2.GetItemCollection(DataSpace.SSpace));
            Assert.Same(workspace1.GetItemCollection(DataSpace.CSSpace), workspace2.GetItemCollection(DataSpace.CSSpace));
        }

        [Fact]
        public void Verify_that_metadata_is_the_same_for_two_workspaces_created_from_two_entity_connections_with_equivalent_connection_strings()
        {
            var connection1 = new EntityConnection(connectionString);
            var connection2 = new EntityConnection(connectionString + ";;");

            var workspace1 = connection1.GetMetadataWorkspace();
            var workspace2 = connection2.GetMetadataWorkspace();

            Assert.Same(workspace1.GetItemCollection(DataSpace.CSpace), workspace2.GetItemCollection(DataSpace.CSpace));
            Assert.Same(workspace1.GetItemCollection(DataSpace.SSpace), workspace2.GetItemCollection(DataSpace.SSpace));
            Assert.Same(workspace1.GetItemCollection(DataSpace.CSSpace), workspace2.GetItemCollection(DataSpace.CSSpace));
        }

        [Fact]
        public void Verify_that_conceptual_metadata_is_the_same_for_two_workspaces_created_from_two_entity_connections_with_reordered_metadata_in_connection_strings()
        {
            var connectionString2 = string.Format(
                 @"metadata=res://EntityFramework.FunctionalTests/System.Data.Entity.Metadata.MetadataCachingModel.msl|res://EntityFramework.FunctionalTests/System.Data.Entity.Metadata.MetadataCachingModel.ssdl|res://EntityFramework.FunctionalTests/System.Data.Entity.Metadata.MetadataCachingModel.csdl;provider=System.Data.SqlClient;provider connection string=""{0}""",
                ModelHelpers.SimpleConnectionString("MetadataCachingTests"));

            var connection1 = new EntityConnection(connectionString);
            var connection2 = new EntityConnection(connectionString2);
            var workspace1 = connection1.GetMetadataWorkspace();
            var workspace2 = connection2.GetMetadataWorkspace();

            Assert.Same(workspace1.GetItemCollection(DataSpace.CSpace), workspace2.GetItemCollection(DataSpace.CSpace));
        }

        [Fact]
        public void Verify_that_opening_connection_does_not_create_new_MetadataWorkspace()
        {
            var connection = new EntityConnection(connectionString);
            var workspace = connection.GetMetadataWorkspace();

            connection.Open();
            Assert.Same(workspace, connection.GetMetadataWorkspace());
        }

        [Fact]
        public void Metadata_does_not_get_garbage_collected_if_references_are_alive()
        {
            Action garbageCollection = () =>
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            };

            MetadataCachingWithGarbageCollectionTemplate(garbageCollection);
        }

        private void MetadataCachingWithGarbageCollectionTemplate(Action garbageCollection)
        {
            MetadataWorkspace.ClearCache();
            var weakReferences = new WeakReference[3];

            // load metadata
            using (var connection1 = new EntityConnection(connectionString))
            {
                connection1.Open();

                weakReferences[0] = new WeakReference(connection1.GetMetadataWorkspace().GetItemCollection(DataSpace.CSpace));
                weakReferences[1] = new WeakReference(connection1.GetMetadataWorkspace().GetItemCollection(DataSpace.SSpace));
                weakReferences[2] = new WeakReference(connection1.GetMetadataWorkspace().GetItemCollection(DataSpace.CSSpace));
            }

            // perform necessary garbage collection steps
            garbageCollection();

            // verify that metadata was cached
            using (var connection2 = new EntityConnection(connectionString))
            {
                connection2.Open();

                Assert.Same(weakReferences[0].Target, connection2.GetMetadataWorkspace().GetItemCollection(DataSpace.CSpace));
                Assert.Same(weakReferences[1].Target, connection2.GetMetadataWorkspace().GetItemCollection(DataSpace.SSpace));
                Assert.Same(weakReferences[2].Target, connection2.GetMetadataWorkspace().GetItemCollection(DataSpace.CSSpace));
            }
        }
    }

    public class MetadataCachingTestsFixture
    {
        public MetadataCachingTestsFixture()
        {
            var database = new SqlTestDatabase("MetadataCachingTests");
            database.EnsureDatabase();
        }
    }
}
