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

#nullable disable

using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Reflection;
using System.Runtime.Loader;
using Moq;

namespace System.Drawing.Design.Tests;

public class ToolboxItemTests
{
    [Fact]
    public void ToolboxItem_Ctor_Default()
    {
        ToolboxItem item = new();
        Assert.Null(item.AssemblyName);
        Assert.Null(item.Bitmap);
        Assert.Null(item.Company);
        Assert.Equal(".NET Component", item.ComponentType);
        Assert.Null(item.DependentAssemblies);
        Assert.Null(item.Description);
        Assert.Empty(item.DisplayName);
        Assert.Empty(item.Filter);
        Assert.Same(item.Filter, item.Filter);
        Assert.False(item.IsTransient);
        Assert.False(item.Locked);
        Assert.Null(item.OriginalBitmap);
        Assert.Empty(item.Properties);
        Assert.Same(item.Properties, item.Properties);
        Assert.False(item.Properties.IsFixedSize);
        Assert.False(item.Properties.IsReadOnly);
        Assert.Empty(item.TypeName);
        Assert.Empty(item.Version);
    }

    public static IEnumerable<object[]> AssemblyName_Set_TestData()
    {
        yield return new object[] { null };
        yield return new object[] { new AssemblyName() };
        yield return new object[] { new AssemblyName(typeof(int).Assembly.FullName) };
    }

    [Theory]
    [MemberData(nameof(AssemblyName_Set_TestData))]
    public void ToolboxItem_AssemblyName_Set_GetReturnsExpected(AssemblyName value)
    {
        ToolboxItem item = new()
        {
            AssemblyName = value
        };
        if (value is null)
        {
            Assert.Null(item.AssemblyName);
            Assert.Null(item.Properties["AssemblyName"]);
            Assert.Empty(item.Version);
        }
        else
        {
            Assert.Equal(value.FullName, item.AssemblyName.FullName);
            Assert.NotSame(value, item.AssemblyName);
            Assert.NotSame(item.AssemblyName, item.AssemblyName);
            Assert.Equal(value.FullName, ((AssemblyName)item.Properties["AssemblyName"]).FullName);
            Assert.NotSame(value, item.Properties["AssemblyName"]);
            Assert.NotSame(item.Properties["AssemblyName"], item.Properties["AssemblyName"]);
            Assert.Equal(value.Version?.ToString() ?? string.Empty, item.Version);
        }

        // Set same.
        item.AssemblyName = value;
        if (value is null)
        {
            Assert.Null(item.AssemblyName);
            Assert.Null(item.Properties["AssemblyName"]);
            Assert.Empty(item.Version);
        }
        else
        {
            Assert.Equal(value.FullName, item.AssemblyName.FullName);
            Assert.NotSame(value, item.AssemblyName);
            Assert.NotSame(item.AssemblyName, item.AssemblyName);
            Assert.Equal(value.FullName, ((AssemblyName)item.Properties["AssemblyName"]).FullName);
            Assert.NotSame(value, item.Properties["AssemblyName"]);
            Assert.NotSame(item.Properties["AssemblyName"], item.Properties["AssemblyName"]);
            Assert.Equal(value.Version?.ToString() ?? string.Empty, item.Version);
        }
    }

    [Fact]
    public void ToolboxItem_AssemblyName_SetWithInvalidPropertyType_ThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("AssemblyName", new object()));
    }

    public static IEnumerable<object[]> Bitmap_Set_TestData()
    {
        yield return new object[] { null };
        yield return new object[] { new Bitmap(10, 10) };
    }

    [Theory]
    [MemberData(nameof(Bitmap_Set_TestData))]
    public void ToolboxItem_Bitmap_Set_GetReturnsExpected(Bitmap value)
    {
        ToolboxItem item = new()
        {
            Bitmap = value
        };
        Assert.Same(value, item.Bitmap);
        Assert.Same(value, item.Properties["Bitmap"]);

        // Set same.
        item.Bitmap = value;
        Assert.Same(value, item.Bitmap);
        Assert.Same(value, item.Properties["Bitmap"]);
    }

    [Fact]
    public void ToolboxItem_Bitmap_SetWithInvalidPropertyType_ThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("Bitmap", new object()));
    }

    [Theory]
    [NormalizedStringData]
    public void ToolboxItem_Company_Set_GetReturnsExpected(string value, string expected)
    {
        ToolboxItem item = new()
        {
            Company = value
        };
        Assert.Equal(expected, item.Company);
        Assert.Equal(expected, item.Properties["Company"]);

        // Set same.
        item.Company = value;
        Assert.Equal(expected, item.Company);
        Assert.Equal(expected, item.Properties["Company"]);
    }

    [Fact]
    public void ToolboxItem_Company_SetWithInvalidPropertyType_ThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("Company", new object()));
    }

    public static IEnumerable<object[]> DependentAssemblies_Set_TestData()
    {
        yield return new object[] { null };
        yield return new object[] { Array.Empty<AssemblyName>() };
        yield return new object[] { new AssemblyName[] { null } };
        yield return new object[] { new AssemblyName[] { new() } };
    }

    [Theory]
    [MemberData(nameof(DependentAssemblies_Set_TestData))]
    public void ToolboxItem_DependentAssemblies_Set_GetReturnsExpected(AssemblyName[] value)
    {
        ToolboxItem item = new()
        {
            DependentAssemblies = value
        };
        if (value is null)
        {
            Assert.Null(item.DependentAssemblies);
            Assert.Null(item.Properties["DependentAssemblies"]);
        }
        else
        {
            Assert.Equal(value, item.DependentAssemblies);
            Assert.NotSame(value, item.DependentAssemblies);
            Assert.Equal(value, item.Properties["DependentAssemblies"]);
            Assert.NotSame(value, item.Properties["DependentAssemblies"]);
        }

        // Set same.
        item.DependentAssemblies = value;
        if (value is null)
        {
            Assert.Null(item.DependentAssemblies);
            Assert.Null(item.Properties["DependentAssemblies"]);
        }
        else
        {
            Assert.Equal(value, item.DependentAssemblies);
            Assert.NotSame(value, item.DependentAssemblies);
            Assert.Equal(value, item.Properties["DependentAssemblies"]);
            Assert.NotSame(value, item.Properties["DependentAssemblies"]);
        }
    }

    [Fact]
    public void ToolboxItem_DependentAssemblies_SetWithInvalidPropertyType_GetThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("DependentAssemblies", new object()));
    }

    [Theory]
    [NormalizedStringData]
    public void ToolboxItem_Description_Set_GetReturnsExpected(string value, string expected)
    {
        ToolboxItem item = new()
        {
            Description = value
        };
        Assert.Equal(expected, item.Description);
        Assert.Equal(expected, item.Properties["Description"]);

        // Set same.
        item.Description = value;
        Assert.Equal(expected, item.Description);
        Assert.Equal(expected, item.Properties["Description"]);
    }

    [Fact]
    public void ToolboxItem_Description_SetWithInvalidPropertyType_ThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("Description", new object()));
    }

    [Theory]
    [NormalizedStringData]
    public void ToolboxItem_DisplayName_Set_GetReturnsExpected(string value, string expected)
    {
        ToolboxItem item = new()
        {
            DisplayName = value
        };
        Assert.Equal(expected, item.DisplayName);
        Assert.Equal(expected, item.Properties["DisplayName"]);

        // Set same.
        item.DisplayName = value;
        Assert.Equal(expected, item.DisplayName);
        Assert.Equal(expected, item.Properties["DisplayName"]);
    }

    [Fact]
    public void ToolboxItem_DisplayName_SetWithInvalidPropertyType_ThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("DisplayName", new object()));
    }

    public static IEnumerable<object[]> Filter_Set_TestData()
    {
        yield return new object[] { null, Array.Empty<object>() };
        yield return new object[] { Array.Empty<object>(), Array.Empty<object>() };
        yield return new object[] { new object[] { null }, Array.Empty<object>() };
        yield return new object[] { new object[] { new(), new ToolboxItemFilterAttribute("filterString") }, new object[] { new ToolboxItemFilterAttribute("filterString") } };
    }

    [Theory]
    [MemberData(nameof(Filter_Set_TestData))]
    public void ToolboxItem_Filter_Set_GetReturnsExpected(ICollection value, ICollection expected)
    {
        ToolboxItem item = new()
        {
            Filter = value
        };
        Assert.Equal(expected, item.Filter);
        Assert.Equal(expected, item.Properties["Filter"]);

        // Set same.
        item.Filter = value;
        Assert.Equal(expected, item.Filter);
        Assert.Equal(expected, item.Properties["Filter"]);
    }

    [Fact]
    public void ToolboxItem_Filter_SetWithInvalidPropertyType_GetThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("Filter", new object()));
    }

    [Theory]
    [BoolData]
    public void ToolboxItem_IsTransient_Set_GetReturnsExpected(bool value)
    {
        ToolboxItem item = new()
        {
            IsTransient = value
        };
        Assert.Equal(value, item.IsTransient);
        Assert.Equal(value, item.Properties["IsTransient"]);

        // Set same.
        item.IsTransient = value;
        Assert.Equal(value, item.IsTransient);
        Assert.Equal(value, item.Properties["IsTransient"]);
    }

    [Fact]
    public void ToolboxItem_IsTransient_SetWithNullPropertyType_GetThrowsArgumentNullException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentNullException>("value", () => item.Properties.Add("IsTransient", null));
    }

    [Fact]
    public void ToolboxItem_IsTransient_SetWithInvalidPropertyType_GetThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("IsTransient", new object()));
    }

    [Theory]
    [MemberData(nameof(Bitmap_Set_TestData))]
    public void ToolboxItem_OriginalBitmap_Set_GetReturnsExpected(Bitmap value)
    {
        ToolboxItem item = new()
        {
            OriginalBitmap = value
        };
        Assert.Same(value, item.OriginalBitmap);
        Assert.Same(value, item.Properties["OriginalBitmap"]);

        // Set same.
        item.OriginalBitmap = value;
        Assert.Same(value, item.OriginalBitmap);
        Assert.Same(value, item.Properties["OriginalBitmap"]);
    }

    [Fact]
    public void ToolboxItem_OriginalBitmap_SetWithInvalidPropertyType_ThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("OriginalBitmap", new object()));
    }

    [Theory]
    [NormalizedStringData]
    public void ToolboxItem_TypeName_Set_GetReturnsExpected(string value, string expected)
    {
        ToolboxItem item = new()
        {
            TypeName = value
        };
        Assert.Equal(expected, item.TypeName);
        Assert.Equal(expected, item.Properties["TypeName"]);

        // Set same.
        item.TypeName = value;
        Assert.Equal(expected, item.TypeName);
        Assert.Equal(expected, item.Properties["TypeName"]);
    }

    [Fact]
    public void ToolboxItem_TypeName_SetWithInvalidPropertyType_ThrowsArgumentException()
    {
        ToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.Properties.Add("TypeName", new object()));
    }

    [Fact]
    public void ToolboxItem_CreateComponents_InvokeWithoutHost_ReturnsExpected()
    {
        ToolboxItem item = new()
        {
            AssemblyName = typeof(Component).Assembly.GetName(true),
            TypeName = "System.ComponentModel.Component"
        };
        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Null(e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.IsType<Component>(Assert.Single(e.Components));
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.IsType<Component>(Assert.Single(item.CreateComponents()));
        Assert.Equal(1, creatingCallCount);
        Assert.Equal(1, createdCallCount);

        Assert.IsType<Component>(Assert.Single(item.CreateComponents(null)));
        Assert.Equal(2, creatingCallCount);
        Assert.Equal(2, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.IsType<Component>(Assert.Single(item.CreateComponents(null)));
        Assert.Equal(2, creatingCallCount);
        Assert.Equal(2, createdCallCount);
    }

    public static IEnumerable<object[]> CreateComponents_InvokeWithHostWithNonIComponentInitializerDesigner_TestData()
    {
        Mock<IDesigner> mockDesigner = new(MockBehavior.Strict);
        mockDesigner.Setup(d => d.Dispose());

        yield return new object[] { new Component(), null };
        yield return new object[] { new Component(), mockDesigner.Object };

        yield return new object[] { null, null };
        yield return new object[] { null, mockDesigner.Object };
    }

    [Theory]
    [MemberData(nameof(CreateComponents_InvokeWithHostWithNonIComponentInitializerDesigner_TestData))]
    public void ToolboxItem_CreateComponents_InvokeWithHostWithNonIComponentInitializerDesigner_ReturnsExpected(Component component, IDesigner designer)
    {
        ToolboxItem item = new()
        {
            TypeName = "typeName"
        };
        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(new CustomTypeResolutionService());
        mockDesignerHost
            .Setup(h => h.CreateComponent(typeof(bool)))
            .Returns(component);
        mockDesignerHost
            .Setup(h => h.GetDesigner(component))
            .Returns(designer);

        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(mockDesignerHost.Object, e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(component, Assert.Single(e.Components));
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object)));
        Assert.Equal(1, creatingCallCount);
        Assert.Equal(1, createdCallCount);

        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object, null)));
        Assert.Equal(2, creatingCallCount);
        Assert.Equal(2, createdCallCount);

        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object, new Hashtable())));
        Assert.Equal(3, creatingCallCount);
        Assert.Equal(3, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object)));
        Assert.Equal(3, creatingCallCount);
        Assert.Equal(3, createdCallCount);
    }

    [Fact]
    public void ToolboxItem_CreateComponents_InvokeWithHostWithIComponentInitializerDesigner_ReturnsExpected()
    {
        using Component component = new();
        ToolboxItem item = new()
        {
            TypeName = "typeName"
        };
        Mock<IDesigner> mockDesigner = new(MockBehavior.Strict);
        mockDesigner.Setup(d => d.Dispose());
        Mock<IComponentInitializer> mockComponentInitializer = mockDesigner.As<IComponentInitializer>();
        mockComponentInitializer
            .Setup(i => i.InitializeNewComponent(null));
        mockComponentInitializer
            .Setup(i => i.InitializeNewComponent(new Hashtable()));

        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(new CustomTypeResolutionService());
        mockDesignerHost
            .Setup(h => h.CreateComponent(typeof(bool)))
            .Returns(component);
        mockDesignerHost
            .Setup(h => h.GetDesigner(component))
            .Returns(mockDesigner.Object);

        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(mockDesignerHost.Object, e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(component, Assert.Single(e.Components));
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object)));
        Assert.Equal(1, creatingCallCount);
        Assert.Equal(1, createdCallCount);

        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object, null)));
        Assert.Equal(2, creatingCallCount);
        Assert.Equal(2, createdCallCount);

        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object, new Hashtable())));
        Assert.Equal(3, creatingCallCount);
        Assert.Equal(3, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object)));
        Assert.Equal(3, creatingCallCount);
        Assert.Equal(3, createdCallCount);
    }

    [Fact]
    public void ToolboxItem_CreateComponents_InvokeWithHostWithThrowingIComponentInitializerDesigner_ReturnsExpected()
    {
        using Component component = new();
        ToolboxItem item = new()
        {
            TypeName = "typeName"
        };
        Mock<IDesigner> mockDesigner = new(MockBehavior.Strict);
        mockDesigner.Setup(d => d.Dispose());
        Mock<IComponentInitializer> mockComponentInitializer = mockDesigner.As<IComponentInitializer>();
        mockComponentInitializer
            .Setup(i => i.InitializeNewComponent(null))
            .Throws(new InvalidOperationException());
        mockComponentInitializer
            .Setup(i => i.InitializeNewComponent(new Hashtable()))
            .Throws(new InvalidOperationException());

        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(new CustomTypeResolutionService());
        mockDesignerHost
            .Setup(h => h.CreateComponent(typeof(bool)))
            .Returns(component);
        mockDesignerHost
            .Setup(h => h.GetDesigner(component))
            .Returns(mockDesigner.Object);
        mockDesignerHost
            .Setup(h => h.DestroyComponent(component));

        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(mockDesignerHost.Object, e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(component, Assert.Single(e.Components));
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.Throws<InvalidOperationException>(() => item.CreateComponents(mockDesignerHost.Object));
        Assert.Equal(1, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Throws<InvalidOperationException>(() => item.CreateComponents(mockDesignerHost.Object, null));
        Assert.Equal(2, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Throws<InvalidOperationException>(() => item.CreateComponents(mockDesignerHost.Object, new Hashtable()));
        Assert.Equal(3, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.Throws<InvalidOperationException>(() => item.CreateComponents(mockDesignerHost.Object));
        Assert.Equal(3, creatingCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [Fact]
    public void ToolboxItem_CreateComponents_InvokeWithNullComponentsCoreWithHost_ReturnsExpected()
    {
        NullComponentsToolboxItem item = new()
        {
            TypeName = "typeName"
        };
        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(new CustomTypeResolutionService());
        mockDesignerHost
            .Setup(h => h.CreateComponent(typeof(bool)))
            .Returns(new Component());

        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(mockDesignerHost.Object, e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Null(e.Components);
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.Null(item.CreateComponents(mockDesignerHost.Object));
        Assert.Equal(1, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Null(item.CreateComponents(mockDesignerHost.Object, null));
        Assert.Equal(2, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Null(item.CreateComponents(mockDesignerHost.Object, new Hashtable()));
        Assert.Equal(3, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.Null(item.CreateComponents(mockDesignerHost.Object));
        Assert.Equal(3, creatingCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [Theory]
    [InlineData("")]
    [InlineData("NoSuchType")]
    [InlineData("System.Int32")]
    public void ToolboxItem_CreateComponents_InvokeInvalidType_ReturnsEmpty(string typeName)
    {
        ToolboxItem item = new()
        {
            TypeName = typeName
        };
        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Null(e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Null(e.Components);
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        Assert.Empty(item.CreateComponents());
        Assert.Equal(1, creatingCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [Fact]
    public void ToolboxItem_CreateComponentsCore_InvokeWithoutHost_ReturnsExpected()
    {
        SubToolboxItem item = new()
        {
            AssemblyName = typeof(Component).Assembly.GetName(true),
            TypeName = "System.ComponentModel.Component"
        };
        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Null(e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.IsType<Component>(Assert.Single(e.Components));
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.IsType<Component>(Assert.Single(item.CreateComponentsCore(null)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.IsType<Component>(Assert.Single(item.CreateComponentsCore(null)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [Theory]
    [MemberData(nameof(CreateComponents_InvokeWithHostWithNonIComponentInitializerDesigner_TestData))]
    public void ToolboxItem_CreateComponentsCore_InvokeWithHostWithNonIComponentInitializerDesigner_ReturnsExpected(Component component, IDesigner designer)
    {
        SubToolboxItem item = new()
        {
            TypeName = "typeName"
        };
        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(new CustomTypeResolutionService());
        mockDesignerHost
            .Setup(h => h.CreateComponent(typeof(bool)))
            .Returns(component);
        mockDesignerHost
            .Setup(h => h.GetDesigner(component))
            .Returns(designer);

        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(mockDesignerHost.Object, e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(component, Assert.Single(e.Components));
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.Same(component, Assert.Single(item.CreateComponentsCore(mockDesignerHost.Object)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Same(component, Assert.Single(item.CreateComponentsCore(mockDesignerHost.Object, null)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Same(component, Assert.Single(item.CreateComponentsCore(mockDesignerHost.Object, new Hashtable())));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [Fact]
    public void ToolboxItem_CreateComponentsCore_InvokeWithHostWithIComponentInitializerDesigner_ReturnsExpected()
    {
        using Component component = new();
        SubToolboxItem item = new()
        {
            TypeName = "typeName"
        };
        Mock<IDesigner> mockDesigner = new(MockBehavior.Strict);
        mockDesigner.Setup(d => d.Dispose());
        Mock<IComponentInitializer> mockComponentInitializer = mockDesigner.As<IComponentInitializer>();
        mockComponentInitializer
            .Setup(i => i.InitializeNewComponent(null));
        mockComponentInitializer
            .Setup(i => i.InitializeNewComponent(new Hashtable()));

        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(new CustomTypeResolutionService());
        mockDesignerHost
            .Setup(h => h.CreateComponent(typeof(bool)))
            .Returns(component);
        mockDesignerHost
            .Setup(h => h.GetDesigner(component))
            .Returns(mockDesigner.Object);

        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(mockDesignerHost.Object, e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(component, Assert.Single(e.Components));
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.Same(component, Assert.Single(item.CreateComponentsCore(mockDesignerHost.Object)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Same(component, Assert.Single(item.CreateComponentsCore(mockDesignerHost.Object, null)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Same(component, Assert.Single(item.CreateComponentsCore(mockDesignerHost.Object, new Hashtable())));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.Same(component, Assert.Single(item.CreateComponents(mockDesignerHost.Object)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [Fact]
    public void ToolboxItem_CreateComponentsCore_InvokeWithHostWithThrowingIComponentInitializerDesigner_ReturnsExpected()
    {
        using Component component = new();
        SubToolboxItem item = new()
        {
            TypeName = "typeName"
        };
        Mock<IDesigner> mockDesigner = new(MockBehavior.Strict);
        mockDesigner.Setup(d => d.Dispose());
        Mock<IComponentInitializer> mockComponentInitializer = mockDesigner.As<IComponentInitializer>();
        mockComponentInitializer
            .Setup(i => i.InitializeNewComponent(null))
            .Throws(new InvalidOperationException());
        mockComponentInitializer
            .Setup(i => i.InitializeNewComponent(new Hashtable()))
            .Throws(new InvalidOperationException());

        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(new CustomTypeResolutionService());
        mockDesignerHost
            .Setup(h => h.CreateComponent(typeof(bool)))
            .Returns(component);
        mockDesignerHost
            .Setup(h => h.GetDesigner(component))
            .Returns(mockDesigner.Object);
        mockDesignerHost
            .Setup(h => h.DestroyComponent(component));

        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(mockDesignerHost.Object, e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Same(component, Assert.Single(e.Components));
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        // With handler.
        Assert.Same(component, Assert.Single(item.CreateComponentsCore(mockDesignerHost.Object)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Throws<InvalidOperationException>(() => item.CreateComponentsCore(mockDesignerHost.Object, null));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        Assert.Throws<InvalidOperationException>(() => item.CreateComponentsCore(mockDesignerHost.Object, new Hashtable()));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        item.ComponentsCreating -= creatingHandler;
        item.ComponentsCreated -= createdHandler;

        Assert.Same(component, Assert.Single(item.CreateComponentsCore(mockDesignerHost.Object)));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [Theory]
    [InlineData("")]
    [InlineData("NoSuchType")]
    [InlineData("System.Int32")]
    public void ToolboxItem_CreateComponentsCore_InvokeInvalidType_ReturnsEmpty(string typeName)
    {
        SubToolboxItem item = new()
        {
            TypeName = typeName
        };
        int creatingCallCount = 0;
        int createdCallCount = 0;
        ToolboxComponentsCreatingEventHandler creatingHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Null(e.DesignerHost);
            creatingCallCount++;
        };
        ToolboxComponentsCreatedEventHandler createdHandler = (sender, e) =>
        {
            Assert.Same(item, sender);
            Assert.Null(e.Components);
            createdCallCount++;
        };
        item.ComponentsCreating += creatingHandler;
        item.ComponentsCreated += createdHandler;

        Assert.Empty(item.CreateComponentsCore(null));
        Assert.Equal(0, creatingCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [Fact]
    public void ToolboxItem_CheckUnlocked_NotLocked_Nop()
    {
        SubToolboxItem item = new();
        item.CheckUnlocked();
        item.CheckUnlocked();
    }

    [Fact]
    public void ToolboxItem_CheckUnlocked_Locked_ThrowsInvalidOperationException()
    {
        SubToolboxItem item = new();
        item.Lock();
        Assert.Throws<InvalidOperationException>(item.CheckUnlocked);
    }

    public static IEnumerable<object[]> Equals_TestData()
    {
        ToolboxItem item = new();
        yield return new object[] { item, item, true };
        yield return new object[] { item, new ToolboxItem(), true };
        yield return new object[] { item, new SubToolboxItem(), false };

        yield return new object[]
        {
            item,
            new ToolboxItem
            {
                Company = "Company",
                DependentAssemblies = [null],
                Description = "Description",
                Filter = new ToolboxItemFilterAttribute[] { new("Filter") },
                IsTransient = true
            },
            true
        };

        yield return new object[]
        {
            new ToolboxItem { TypeName = "TypeName" },
            new ToolboxItem { TypeName = "TypeName" },
            true
        };
        yield return new object[]
        {
            new ToolboxItem { TypeName = "TypeName" },
            new ToolboxItem { TypeName = "typename" },
            false
        };
        yield return new object[]
        {
            new ToolboxItem { TypeName = "TypeName" },
            new ToolboxItem(),
            false
        };
        yield return new object[]
        {
            new ToolboxItem(),
            new ToolboxItem { TypeName = "TypeName" },
            false
        };
        yield return new object[]
        {
            new NoValidationToolboxItem { TypeName = null },
            new NoValidationToolboxItem { TypeName = null },
            true
        };
        yield return new object[]
        {
            new NoValidationToolboxItem { TypeName = null },
            new NoValidationToolboxItem { TypeName = "TypeName" },
            false
        };
        yield return new object[]
        {
            new NoValidationToolboxItem { TypeName = "TypeName" },
            new NoValidationToolboxItem { TypeName = null },
            false
        };

        yield return new object[]
        {
            new ToolboxItem { DisplayName = "DisplayName" },
            new ToolboxItem { DisplayName = "DisplayName" },
            true
        };
        yield return new object[]
        {
            new ToolboxItem { DisplayName = "DisplayName" },
            new ToolboxItem { DisplayName = "displayname" },
            false
        };
        yield return new object[]
        {
            new ToolboxItem { DisplayName = "DisplayName" },
            new ToolboxItem(),
            false
        };
        yield return new object[]
        {
            new ToolboxItem(),
            new ToolboxItem { DisplayName = "DisplayName" },
            false
        };
        yield return new object[]
        {
            new NoValidationToolboxItem { DisplayName = null },
            new NoValidationToolboxItem { DisplayName = null },
            true
        };
        yield return new object[]
        {
            new NoValidationToolboxItem { DisplayName = null },
            new NoValidationToolboxItem { DisplayName = "TypeName" },
            false
        };
        yield return new object[]
        {
            new NoValidationToolboxItem { DisplayName = "TypeName" },
            new NoValidationToolboxItem { DisplayName = null },
            false
        };

        yield return new object[]
        {
            new ToolboxItem { AssemblyName = new AssemblyName("Name") },
            new ToolboxItem { AssemblyName = new AssemblyName("Name") },
            true
        };
        yield return new object[]
        {
            new ToolboxItem { AssemblyName = new AssemblyName("Name") },
            new ToolboxItem { AssemblyName = new AssemblyName("name") },
            false
        };
        yield return new object[]
        {
            new ToolboxItem(),
            new ToolboxItem { AssemblyName = new AssemblyName("Name") },
            false
        };
        yield return new object[]
        {
            new ToolboxItem { AssemblyName = new AssemblyName("Name") },
            new ToolboxItem(),
            false
        };

        yield return new object[] { new ToolboxItem(), new(), false };
        yield return new object[] { new ToolboxItem(), null, false };
    }

    [Theory]
    [MemberData(nameof(Equals_TestData))]
    public void ToolboxItem_Equals_Invoke_ReturnsExpected(ToolboxItem item, object other, bool expected)
    {
        Assert.Equal(expected, item.Equals(other));
    }

    public static IEnumerable<object[]> FilterPropertyValue_TestData()
    {
        object o = new();
        yield return new object[] { "AssemblyName", null, null, true };
        yield return new object[] { "AssemblyName", new AssemblyName("Name"), new AssemblyName("Name"), false };
        yield return new object[] { "AssemblyName", o, o, true };
        yield return new object[] { "assemblyName", new AssemblyName("Name"), new AssemblyName("Name"), true };

        yield return new object[] { "DisplayName", null, string.Empty, false };
        yield return new object[] { "DisplayName", "value", "value", true };
        yield return new object[] { "DisplayName", o, o, true };
        yield return new object[] { "displayname", null, null, true };

        yield return new object[] { "TypeName", null, string.Empty, false };
        yield return new object[] { "TypeName", "value", "value", true };
        yield return new object[] { "TypeName", o, o, true };
        yield return new object[] { "typename", null, null, true };

        yield return new object[] { "Filter", null, Array.Empty<ToolboxItemFilterAttribute>(), false };
        yield return new object[] { "Filter", Array.Empty<ToolboxItemFilterAttribute>(), Array.Empty<ToolboxItemFilterAttribute>(), true };
        yield return new object[] { "Filter", o, o, true };
        yield return new object[] { "filter", null, null, true };

        yield return new object[] { "IsTransient", null, false, false };
        yield return new object[] { "IsTransient", true, true, true };
        yield return new object[] { "IsTransient", o, o, true };
        yield return new object[] { "istransient", null, null, true };

        yield return new object[] { "NoSuchProperty", null, null, true };
        yield return new object[] { "NoSuchProperty", "value", "value", true };
        yield return new object[] { "NoSuchProperty", o, o, true };
    }

    [Theory]
    [MemberData(nameof(FilterPropertyValue_TestData))]
    public void ToolboxItem_FilterPropertyValue_Invoke_ReturnsExpected(string propertyName, object value, object expected, bool same)
    {
        SubToolboxItem item = new();
        object actual = item.FilterPropertyValue(propertyName, value);
        if (expected is AssemblyName expectedName)
        {
            Assert.Equal(expectedName.FullName, Assert.IsType<AssemblyName>(actual).FullName);
        }
        else
        {
            Assert.Equal(expected, actual);
        }

        Assert.Equal(same, ReferenceEquals(value, actual));
    }

    public static IEnumerable<object[]> GetHashCode_TestData()
    {
        yield return new object[] { new ToolboxItem() };
        yield return new object[] { new ToolboxItem { TypeName = "TypeName", DisplayName = "DisplayName" } };
        yield return new object[] { new NoValidationToolboxItem { TypeName = null, DisplayName = null } };
    }

    [Theory]
    [MemberData(nameof(GetHashCode_TestData))]
    public void ToolboxItem_GetHashCode_Invoke_ReturnsExpected(ToolboxItem item)
    {
        Assert.Equal(item.GetHashCode(), item.GetHashCode());
    }

    public static IEnumerable<object[]> GetType_TestData()
    {
        Mock<IDesignerHost> nullServiceDesignerHost = new(MockBehavior.Strict);
        nullServiceDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(null);
        foreach (object host in new object[] { null, nullServiceDesignerHost.Object })
        {
            yield return new object[] { null, null, "System.Int32", false, typeof(int) };
            yield return new object[] { null, new AssemblyName("NoSuchAssembly"), "System.Int32", false, typeof(int) };
            yield return new object[] { null, new AssemblyName(typeof(int).Assembly.FullName), "System.Int32", false, typeof(int) };
            yield return new object[] { null, new AssemblyName(typeof(ToolboxItem).Assembly.FullName), "System.Int32", false, typeof(int) };
            yield return new object[] { null, new AssemblyName(typeof(int).Assembly.FullName), "System.Drawing.Design.Tests.ToolboxItemTests", false, null };
            yield return new object[] { null, new AssemblyName(typeof(ToolboxItemTests).Assembly.FullName), "System.Drawing.Design.Tests.ToolboxItemTests", false, typeof(ToolboxItemTests) };
            yield return new object[] { null, new AssemblyName(typeof(ToolboxItemTests).Assembly.FullName), "System.Drawing.Design.Tests.toolboxitemtests", false, null };
            yield return new object[] { null, new AssemblyName(typeof(ToolboxItemTests).Assembly.FullName), "NoSuchType", false, null };
            yield return new object[] { null, null, string.Empty, false, null };

#pragma warning disable SYSLIB0044 // Type or member is obsolete
            AssemblyName validNameWithCodeBase = new(typeof(int).Assembly.FullName)
            {
                CodeBase = "System.Windows.Forms.Design.Tests.dll"
            };
            yield return new object[] { null, validNameWithCodeBase, "System.Drawing.Design.Tests.ToolboxItemTests", false, null };

            AssemblyName invalidNameWithCodeBase = new("NoSuchAssembly")
            {
                CodeBase = "System.Windows.Forms.Design.Tests.dll"
            };
            yield return new object[] { null, invalidNameWithCodeBase, "System.Drawing.Design.Tests.ToolboxItemTests", false, typeof(ToolboxItemTests) };

            AssemblyName invalidNameWithInvalidCodeBase = new("NoSuchAssembly")
            {
                CodeBase = "AlsoNoSuchAssembly"
            };
            yield return new object[] { null, invalidNameWithInvalidCodeBase, "System.Drawing.Design.Tests.ToolboxItemTests", false, null };

            AssemblyLoadContext.Default.Resolving += (context, name) =>
            {
                if (name.Name == "ThrowBadImageFormatException")
                {
                    throw new BadImageFormatException();
                }
                else if (name.Name == "ThrowIOException")
                {
                    throw new IOException();
                }

                return null;
            };
            yield return new object[] { null, new AssemblyName("ThrowBadImageFormatException"), "System.Int32", false, typeof(int) };
            yield return new object[] { null, new AssemblyName("ThrowIOException"), "System.Int32", false, typeof(int) };

            AssemblyName badImageFormatExceptionCodeBase = new("NoSuchAssembly")
            {
                CodeBase = "ThrowBadImageFormatException"
            };
            yield return new object[] { null, badImageFormatExceptionCodeBase, "System.Int32", false, typeof(int) };

            AssemblyName ioFormatExceptionCodeBase = new("NoSuchAssembly")
            {
                CodeBase = "ThrowIOException"
            };
#pragma warning restore SYSLIB0044
            yield return new object[] { null, ioFormatExceptionCodeBase, "System.Int32", false, typeof(int) };
        }

        Mock<IDesignerHost> invalidServiceDesignerHost = new(MockBehavior.Strict);
        invalidServiceDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(new object());
        yield return new object[] { invalidServiceDesignerHost.Object, new AssemblyName(typeof(int).Assembly.FullName), "System.Int32", false, typeof(int) };

        foreach (bool reference in new bool[] { true, false })
        {
            Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
            mockDesignerHost
                .Setup(h => h.GetService(typeof(ITypeResolutionService)))
                .Returns(new CustomTypeResolutionService());
            yield return new object[] { mockDesignerHost.Object, null, "typeName", reference, typeof(bool) };
            yield return new object[] { mockDesignerHost.Object, null, string.Empty, false, null };
            yield return new object[] { mockDesignerHost.Object, new AssemblyName(), "typeName", reference, typeof(bool) };
            yield return new object[] { mockDesignerHost.Object, new AssemblyName(typeof(int).Assembly.FullName), "System.Int32", reference, typeof(int) };
            yield return new object[] { mockDesignerHost.Object, new AssemblyName(typeof(int).Assembly.FullName), "System.Drawing.Design.Tests.ToolboxItemTests", reference, typeof(ToolboxItemTests) };
            yield return new object[] { mockDesignerHost.Object, new AssemblyName(typeof(int).Assembly.FullName), "System.Drawing.Design.Tests.toolboxitemtests", reference, null };
            yield return new object[] { mockDesignerHost.Object, new AssemblyName(typeof(int).Assembly.FullName), "NoSuchType", reference, null };
        }
    }

    [Theory]
    [MemberData(nameof(GetType_TestData))]
    public void ToolboxItem_GetType_InvokeWithoutTypeNameAssemblyName_ReturnsExpected(IDesignerHost host, AssemblyName assemblyName, string typeName, bool reference, Type expected)
    {
        if (reference)
        {
            return;
        }

        ToolboxItem item = new()
        {
            AssemblyName = assemblyName,
            TypeName = typeName
        };
        Assert.Equal(expected, item.GetType(host));
    }

    [Theory]
    [MemberData(nameof(GetType_TestData))]
    public void ToolboxItem_GetType_InvokeWithTypeNameAssemblyName_ReturnsExpected(IDesignerHost host, AssemblyName assemblyName, string typeName, bool reference, Type expected)
    {
        SubToolboxItem item = new();
        Assert.Equal(expected, item.GetType(host, assemblyName, typeName, reference));
    }

    [Fact]
    public void ToolboxItem_GetType_NullTypeName_ThrowsArgumentNullException()
    {
        SubToolboxItem item = new();
        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(null);
        Assert.Throws<ArgumentNullException>("typeName", () => item.GetType(mockDesignerHost.Object, null, null, false));
        Assert.Throws<ArgumentNullException>("typeName", () => item.GetType(null, null, null, false));
    }

    [Fact]
    public void ToolboxItem_GetType_EmptyAssemblyName_ThrowsArgumentException()
    {
        SubToolboxItem item = new();
        Mock<IDesignerHost> mockDesignerHost = new(MockBehavior.Strict);
        mockDesignerHost
            .Setup(h => h.GetService(typeof(ITypeResolutionService)))
            .Returns(null);
        Assert.Throws<ArgumentException>(() => item.GetType(mockDesignerHost.Object, new AssemblyName(), "typeName", false));
        Assert.Throws<ArgumentException>(() => item.GetType(null, new AssemblyName(), "typeName", false));
    }

    public static IEnumerable<object[]> Initialize_TypeWithAttributes_TestData()
    {
        yield return new object[] { typeof(ClassWithValidAttributes), new Size(16, 16) };
        yield return new object[] { typeof(ClassWithStretchedWidthImage), new Size(24, 16) };
        yield return new object[] { typeof(ClassWithStretchedHeightImage), new Size(16, 24) };
        yield return new object[] { typeof(ClassWithInvalidImage), new Size(16, 16) };
    }

    [Theory]
    [MemberData(nameof(Initialize_TypeWithAttributes_TestData))]
    public void ToolboxItem_Initialize_TypeWithAttributes_Success(Type type, Size expectedOriginalBitmapSize)
    {
        using Bitmap bitmap = new(10, 10);
        using Bitmap originalBitmap = new(10, 10);

        var filter = new ToolboxItemFilterAttribute[] { new("Filter") };
        ToolboxItem item = new()
        {
            AssemblyName = new AssemblyName("AssemblyName"),
            Bitmap = bitmap,
            Company = "Company",
            Description = "Description",
            DependentAssemblies = new AssemblyName[2],
            DisplayName = "DisplayName",
            Filter = filter,
            OriginalBitmap = originalBitmap
        };
        item.Initialize(type);
        if (expectedOriginalBitmapSize == new Size(10, 10))
        {
            Assert.NotEqual(bitmap, item.Bitmap);
            Assert.Same(item.Bitmap, item.Bitmap);
        }
        else
        {
            Assert.Equal(new Size(16, 16), item.Bitmap.Size);
        }

        Assert.Equal("Microsoft Corporation", item.Company);
        Assert.Equal("Description", item.Description);
        Assert.Equal(type.Assembly.FullName, item.AssemblyName.FullName);
        Assert.Equal(new string[] { type.Assembly.FullName }, item.DependentAssemblies.Select(a => a.FullName));
        Assert.Equal(type.Name, item.DisplayName);
        Assert.Equal(new string[] { type.Name, "Filter", "System.Drawing.Design.Tests.ToolboxItemTests+" + type.Name }, item.Filter.Cast<ToolboxItemFilterAttribute>().Select(a => a.FilterString).OrderBy(f => f));
        Assert.Equal(expectedOriginalBitmapSize, item.OriginalBitmap.Size);

        Assert.Equal("Microsoft Corporation", item.Company);
        Assert.Equal("Description", item.Description);
        Assert.Equal(type.Assembly.FullName, item.AssemblyName.FullName);
        Assert.Equal(new string[] { type.Assembly.FullName }, item.DependentAssemblies.Select(a => a.FullName));
        Assert.Equal(type.Name, item.DisplayName);
        Assert.Equal(new string[] { type.Name, "Filter", "System.Drawing.Design.Tests.ToolboxItemTests+" + type.Name }, item.Filter.Cast<ToolboxItemFilterAttribute>().Select(a => a.FilterString).OrderBy(f => f));
        Assert.Equal(expectedOriginalBitmapSize, item.OriginalBitmap.Size);
    }

    [Fact]
    public void ToolboxItem_Initialize_ObjectType_Success()
    {
        using Bitmap bitmap = new(10, 10);
        using Bitmap originalBitmap = new(10, 10);
        var filter = new ToolboxItemFilterAttribute[] { new("Filter") };
        ToolboxItem item = new()
        {
            AssemblyName = new AssemblyName("AssemblyName"),
            Bitmap = bitmap,
            Company = "Company",
            Description = "Description",
            DependentAssemblies = new AssemblyName[2],
            DisplayName = "DisplayName",
            Filter = filter,
            OriginalBitmap = originalBitmap
        };
        item.Initialize(typeof(object));
        Assert.NotEqual(bitmap, item.Bitmap);
        Assert.Same(item.Bitmap, item.Bitmap);
        Assert.Equal("Microsoft Corporation", item.Company);
        Assert.Empty(item.Description);
        Assert.Equal(typeof(object).Assembly.FullName, item.AssemblyName.FullName);
        Assert.Equal(new string[] { typeof(object).Assembly.FullName }, item.DependentAssemblies.Select(a => a.FullName));
        Assert.Equal("Object", item.DisplayName);
        Assert.Equal(new string[] { "System.Object" }, item.Filter.Cast<ToolboxItemFilterAttribute>().Select(a => a.FilterString));
        Assert.Same(item.OriginalBitmap, item.OriginalBitmap);
    }

    [Fact]
    public void ToolboxItem_Initialize_NullType_Nop()
    {
        using Bitmap bitmap = new(10, 10);
        using Bitmap originalBitmap = new(10, 10);
        var filter = new ToolboxItemFilterAttribute[] { new("Filter") };
        ToolboxItem item = new()
        {
            AssemblyName = new AssemblyName("AssemblyName"),
            Bitmap = bitmap,
            Company = "Company",
            Description = "Description",
            DependentAssemblies = new AssemblyName[2],
            DisplayName = "DisplayName",
            Filter = filter,
            OriginalBitmap = originalBitmap
        };
        item.Initialize(null);
        Assert.Equal("AssemblyName", item.AssemblyName.FullName);
        Assert.Same(bitmap, item.Bitmap);
        Assert.Equal("Company", item.Company);
        Assert.Equal("Description", item.Description);
        Assert.Equal(new AssemblyName[2], item.DependentAssemblies);
        Assert.Equal("DisplayName", item.DisplayName);
        Assert.Equal(filter, item.Filter);
        Assert.Same(originalBitmap, item.OriginalBitmap);
    }

    [Theory]
    [InlineData(null)]
    [InlineData(typeof(int))]
    public void ToolboxItem_Initialize_Locked_ThrowsInvalidOperationException(Type type)
    {
        ToolboxItem item = new();
        item.Lock();
        Assert.Throws<InvalidOperationException>(() => item.Initialize(type));
    }

    [Fact]
    public void ToolboxItem_Lock_Invoke_Success()
    {
        ToolboxItem item = new();
        item.Lock();
        Assert.True(item.Locked);
        Assert.True(item.Properties.IsFixedSize);
        Assert.True(item.Properties.IsReadOnly);

        // Lock again.
        item.Lock();
        Assert.True(item.Locked);
        Assert.True(item.Properties.IsFixedSize);
        Assert.True(item.Properties.IsReadOnly);
    }

    [Fact]
    public void ToolboxItem_OnComponentsCreated_Invoke_Success()
    {
        SubToolboxItem item = new();

        // No handler.
        item.OnComponentsCreated(null);

        // Handler.
        int callCount = 0;
        ToolboxComponentsCreatedEventHandler handler = (sender, e) =>
        {
            Assert.Equal(item, sender);
            Assert.Null(e);
            callCount++;
        };

        item.ComponentsCreated += handler;
        item.OnComponentsCreated(null);
        Assert.Equal(1, callCount);

        // Should not call if the handler is removed.
        item.ComponentsCreated -= handler;
        item.OnComponentsCreated(null);
        Assert.Equal(1, callCount);
    }

    [Fact]
    public void ToolboxItem_OnComponentsCreating_Invoke_Success()
    {
        SubToolboxItem item = new();

        // No handler.
        item.OnComponentsCreating(null);

        // Handler.
        int callCount = 0;
        ToolboxComponentsCreatingEventHandler handler = (sender, e) =>
        {
            Assert.Equal(item, sender);
            Assert.Null(e);
            callCount++;
        };

        item.ComponentsCreating += handler;
        item.OnComponentsCreating(null);
        Assert.Equal(1, callCount);

        // Should not call if the handler is removed.
        item.ComponentsCreating -= handler;
        item.OnComponentsCreating(null);
        Assert.Equal(1, callCount);
    }

    public static IEnumerable<object[]> ToString_TestData()
    {
        yield return new object[] { new ToolboxItem(), string.Empty };
        yield return new object[] { new ToolboxItem { DisplayName = "DisplayName" }, "DisplayName" };
        yield return new object[] { new NoValidationToolboxItem { DisplayName = null }, string.Empty };
    }

    [Theory]
    [MemberData(nameof(ToString_TestData))]
    public void ToolboxItem_ToString_Invoke_ReturnsExpected(ToolboxItem item, string expected)
    {
        Assert.Equal(expected, item.ToString());
    }

    public static IEnumerable<object[]> ValidatePropertyValue_TestData()
    {
        AssemblyName name = new();
        yield return new object[] { "AssemblyName", null, null };
        yield return new object[] { "AssemblyName", name, name };

        Bitmap bitmap = new(10, 10);
        yield return new object[] { "Bitmap", null, null };
        yield return new object[] { "Bitmap", bitmap, bitmap };

        Bitmap originalBitmap = new(10, 10);
        yield return new object[] { "OriginalBitmap", null, null };
        yield return new object[] { "OriginalBitmap", originalBitmap, originalBitmap };

        yield return new object[] { "Company", null, string.Empty };
        yield return new object[] { "Company", "value", "value" };

        yield return new object[] { "Description", null, string.Empty };
        yield return new object[] { "Description", "value", "value" };

        yield return new object[] { "DisplayName", null, string.Empty };
        yield return new object[] { "DisplayName", "value", "value" };

        yield return new object[] { "TypeName", null, string.Empty };
        yield return new object[] { "TypeName", "value", "value" };

        ToolboxItemFilterAttribute filter = new("filter");
        yield return new object[] { "Filter", null, Array.Empty<ToolboxItemFilterAttribute>() };
        yield return new object[] { "Filter", Array.Empty<ToolboxItemFilterAttribute>(), Array.Empty<ToolboxItemFilterAttribute>() };
        yield return new object[] { "Filter", new object[] { null, "value", filter, filter }, new ToolboxItemFilterAttribute[] { filter, filter } };

        yield return new object[] { "NoSuchProperty", null, null };
        yield return new object[] { "NoSuchProperty", 1, 1 };

        yield return new object[] { "istransient", null, null };
        yield return new object[] { "istransient", 1, 1 };
    }

    [Theory]
    [MemberData(nameof(ValidatePropertyValue_TestData))]
    public void ToolboxItem_ValidatePropertyValue_ValueAllowed_ReturnsExpected(string propertyName, object value, object expected)
    {
        SubToolboxItem item = new();
        Assert.Equal(expected, item.ValidatePropertyValue(propertyName, value));
    }

    [Theory]
    [InlineData("IsTransient")]
    public void ToolboxItem_ValidatePropertyValue_NullValueDisallowed_ThrowsArgumentNullException(string propertyName)
    {
        SubToolboxItem item = new();
        Assert.Throws<ArgumentNullException>("value", () => item.ValidatePropertyValue(propertyName, null));
    }

    [Theory]
    [InlineData("AssemblyName")]
    [InlineData("Bitmap")]
    [InlineData("OriginalBitmap")]
    [InlineData("Company")]
    [InlineData("Description")]
    [InlineData("DisplayName")]
    [InlineData("TypeName")]
    [InlineData("Filter")]
    [InlineData("DependentAssemblies")]
    public void ToolboxItem_ValidatePropertyValue_InvalidValue_ThrowsArgumentException(string propertyName)
    {
        SubToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.ValidatePropertyValue(propertyName, new object()));
    }

    [Theory]
    [InlineData(null, null)]
    [InlineData("propertyName", typeof(int))]
    public void ToolboxItem_ValidatePropertyType_NullDisallowed_ThrowsArgumentNullException(string propertyName, Type expectedType)
    {
        SubToolboxItem item = new();
        Assert.Throws<ArgumentNullException>("value", () => item.ValidatePropertyType(propertyName, null, expectedType, allowNull: false));
    }

    [Theory]
    [InlineData(null, null)]
    [InlineData("propertyName", typeof(int))]
    public void ToolboxItem_ValidatePropertyType_NullAllowed_Nop(string propertyName, Type expectedType)
    {
        SubToolboxItem item = new();
        item.ValidatePropertyType(propertyName, null, expectedType, allowNull: true);
    }

    [Theory]
    [InlineData(null, false)]
    [InlineData("propertyName", true)]
    public void ToolboxItem_ValidatePropertyType_ValidType_Nop(string propertyName, bool allowNull)
    {
        SubToolboxItem item = new();
        item.ValidatePropertyType(propertyName, 1, typeof(int), allowNull);
    }

    [Theory]
    [InlineData(null, false)]
    [InlineData("propertyName", true)]
    public void ToolboxItem_ValidatePropertyType_InvalidType_ThrowsArgumentException(string propertyName, bool allowNull)
    {
        SubToolboxItem item = new();
        Assert.Throws<ArgumentException>("value", () => item.ValidatePropertyType(propertyName, new object(), typeof(int), allowNull));
    }

    private class SubToolboxItem : ToolboxItem
    {
        public new void CheckUnlocked() => base.CheckUnlocked();

        public new IComponent[] CreateComponentsCore(IDesignerHost host)
        {
            return base.CreateComponentsCore(host);
        }

        public new IComponent[] CreateComponentsCore(IDesignerHost host, IDictionary defaultValues)
        {
            return base.CreateComponentsCore(host, defaultValues);
        }

        public new object FilterPropertyValue(string propertyName, object value)
        {
            return base.FilterPropertyValue(propertyName, value);
        }

        public new Type GetType(IDesignerHost host, AssemblyName assemblyName, string typeName, bool reference)
        {
            return base.GetType(host, assemblyName, typeName, reference);
        }

        public new void OnComponentsCreated(ToolboxComponentsCreatedEventArgs args)
        {
            base.OnComponentsCreated(args);
        }

        public new void OnComponentsCreating(ToolboxComponentsCreatingEventArgs args)
        {
            base.OnComponentsCreating(args);
        }

        public new object ValidatePropertyValue(string propertyName, object value)
        {
            return base.ValidatePropertyValue(propertyName, value);
        }

        public new void ValidatePropertyType(string propertyName, object value, Type expectedType, bool allowNull)
        {
            base.ValidatePropertyType(propertyName, value, expectedType, allowNull);
        }
    }

    private class CustomTypeResolutionService : ITypeResolutionService
    {
        public Assembly GetAssemblyResult { get; set; }

        public Assembly GetAssembly(AssemblyName name) => GetAssemblyResult;

        public Assembly GetAssembly(AssemblyName name, bool throwOnError)
        {
            throw new NotImplementedException();
        }

        public string GetPathOfAssembly(AssemblyName name)
        {
            throw new NotImplementedException();
        }

        public Type GetType(string name)
        {
            if (name == "typeName")
            {
                return typeof(bool);
            }

            return Type.GetType(name);
        }

        public Type GetType(string name, bool throwOnError)
        {
            throw new NotImplementedException();
        }

        public Type GetType(string name, bool throwOnError, bool ignoreCase)
        {
            throw new NotImplementedException();
        }

        public List<AssemblyName> ReferenceAssemblies { get; } = [];

        public void ReferenceAssembly(AssemblyName name)
        {
            ReferenceAssemblies.Add(name);
        }
    }

    private class NoValidationToolboxItem : ToolboxItem
    {
        protected override object FilterPropertyValue(string propertyName, object value)
        {
            // Don't normalize.
            return value;
        }

        protected override object ValidatePropertyValue(string propertyName, object value)
        {
            // Don't normalize.
            return value;
        }
    }

    private class NullComponentsToolboxItem : ToolboxItem
    {
        protected override IComponent[] CreateComponentsCore(IDesignerHost host)
        {
            return null;
        }
    }

    [Description("Description")]
    [ToolboxBitmap(typeof(ToolboxItemTests), "16x16.bmp")]
    [ToolboxItemFilter("System.Drawing.Design.Tests.ToolboxItemTests+ClassWithValidAttributes")]
    [ToolboxItemFilter("ClassWithValidAttributes")]
    [ToolboxItemFilter("Filter")]
    private class ClassWithValidAttributes
    {
    }

    [Description("Description")]
    [ToolboxBitmap(typeof(ToolboxItemTests), "24x16.bmp")]
    [ToolboxItemFilter("System.Drawing.Design.Tests.ToolboxItemTests+ClassWithStretchedWidthImage")]
    [ToolboxItemFilter("ClassWithStretchedWidthImage")]
    [ToolboxItemFilter("Filter")]
    private class ClassWithStretchedWidthImage
    {
    }

    [Description("Description")]
    [ToolboxBitmap(typeof(ToolboxItemTests), "16x24.bmp")]
    [ToolboxItemFilter("System.Drawing.Design.Tests.ToolboxItemTests+ClassWithStretchedHeightImage")]
    [ToolboxItemFilter("ClassWithStretchedHeightImage")]
    [ToolboxItemFilter("Filter")]
    private class ClassWithStretchedHeightImage
    {
    }

    [Description("Description")]
    [ToolboxBitmap("NoSuchImage")]
    [ToolboxItemFilter("System.Drawing.Design.Tests.ToolboxItemTests+ClassWithInvalidImage")]
    [ToolboxItemFilter("ClassWithInvalidImage")]
    [ToolboxItemFilter("Filter")]
    private class ClassWithInvalidImage
    {
    }
}
