// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using Xunit;

namespace Microsoft.Extensions.ObjectPool
{
    public class DisposableObjectPoolTest
    {
        [Fact]
        public void DisposableObjectPoolWithDefaultPolicy_GetAnd_ReturnObject_SameInstance()
        {
            // Arrange
            var pool = new DisposableObjectPool<object>(new DefaultPooledObjectPolicy<object>());

            var obj1 = pool.Get();
            pool.Return(obj1);

            // Act
            var obj2 = pool.Get();

            // Assert
            Assert.Same(obj1, obj2);
        }

        [Fact]
        public void DisposableObjectPool_GetAndReturnObject_SameInstance()
        {
            // Arrange
            var pool = new DisposableObjectPool<List<int>>(new ListPolicy());

            var list1 = pool.Get();
            pool.Return(list1);

            // Act
            var list2 = pool.Get();

            // Assert
            Assert.Same(list1, list2);
        }

        [Fact]
        public void DisposableObjectPool_Return_RejectedByPolicy()
        {
            // Arrange
            var pool = new DisposableObjectPool<List<int>>(new ListPolicy());
            var list1 = pool.Get();
            list1.Capacity = 20;

            // Act
            pool.Return(list1);
            var list2 = pool.Get();

            // Assert
            Assert.NotSame(list1, list2);
        }

        [Fact]
        public void DisposableObjectPoolWithOneElement_Dispose_ObjectDisposed()
        {
            // Arrange
            var pool = new DisposableObjectPool<DisposableObject>(new DefaultPooledObjectPolicy<DisposableObject>());
            var obj = pool.Get();
            pool.Return(obj);

            // Act
            pool.Dispose();

            // Assert
            Assert.True(obj.IsDisposed);
        }

        [Fact]
        public void DisposableObjectPoolWithTwoElements_Dispose_ObjectsDisposed()
        {
            // Arrange
            var pool = new DisposableObjectPool<DisposableObject>(new DefaultPooledObjectPolicy<DisposableObject>());
            var obj1 = pool.Get();
            var obj2 = pool.Get();
            pool.Return(obj1);
            pool.Return(obj2);

            // Act
            pool.Dispose();

            // Assert
            Assert.True(obj1.IsDisposed);
            Assert.True(obj2.IsDisposed);
        }

        [Fact]
        public void DisposableObjectPool_DisposeAndGet_ThrowsObjectDisposed()
        {
            // Arrange
            var pool = new DisposableObjectPool<DisposableObject>(new DefaultPooledObjectPolicy<DisposableObject>());
            var obj1 = pool.Get();
            var obj2 = pool.Get();
            pool.Return(obj1);
            pool.Return(obj2);

            // Act
            pool.Dispose();

            // Assert
            Assert.Throws<ObjectDisposedException>(() => pool.Get());
        }

        [Fact]
        public void DisposableObjectPool_DisposeAndReturn_DisposesObject()
        {
            // Arrange
            var pool = new DisposableObjectPool<DisposableObject>(new DefaultPooledObjectPolicy<DisposableObject>());
            var obj = pool.Get();

            // Act
            pool.Dispose();
            pool.Return(obj);

            // Assert
            Assert.True(obj.IsDisposed);
        }

        private class ListPolicy : IPooledObjectPolicy<List<int>>
        {
            public List<int> Create()
            {
                return new List<int>(17);
            }

            public bool Return(List<int> obj)
            {
                return obj.Capacity == 17;
            }
        }

        private class DisposableObject : IDisposable
        {
            public bool IsDisposed { get; private set; }

            public void Dispose() => IsDisposed = true;
        }
    }
}
