// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing.Design;
using System.Reflection;
using System.Runtime.InteropServices;
using Xunit;
using static Interop.Ole32;

namespace System.Windows.Forms.Tests
{
    [Collection("Sequential")] // workaround for WebBrowser control corrupting memory when run on multiple UI threads (instantiated via GUID)
    public class AxHostPropertyDescriptorTests : IClassFixture<ThreadExceptionFixture>
    {
        private const string EmptyClsidString = "00000000-0000-0000-0000-000000000000";
        private const string WebBrowserClsidString = "8856f961-340a-11d0-a96b-00c04fd705a2";

        [WinFormsFact]
        public void AxPropertyDescriptor_Attributes_Get_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.NotNull(property.Attributes[typeof(CustomAttribute)]);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.False(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Null(property.Attributes[typeof(DispIdAttribute)]);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Attributes_GetWithDispIdAttribute_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdProperty)];
            Assert.Null(property.Attributes[typeof(CustomAttribute)]);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.False(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Equal((int)DispatchID.TEXT, Assert.IsType<DispIdAttribute>(property.Attributes[typeof(DispIdAttribute)]).Value);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Attributes_GetWithDispIdAttributeNotBrowsable_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdNotBrowsableProperty)];
            Assert.Null(property.Attributes[typeof(CustomAttribute)]);
            Assert.False(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.False(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Equal((int)DispatchID.TEXT, Assert.IsType<DispIdAttribute>(property.Attributes[typeof(DispIdAttribute)]).Value);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Attributes_GetWithDispIdAttributeReadOnly_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdReadOnlyProperty)];
            Assert.Null(property.Attributes[typeof(CustomAttribute)]);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.True(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Equal((int)DispatchID.TEXT, Assert.IsType<DispIdAttribute>(property.Attributes[typeof(DispIdAttribute)]).Value);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Attributes_GetWithDispIdAttributeNotBrowsableReadOnly_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdNotBrowsableReadOnlyProperty)];
            Assert.Null(property.Attributes[typeof(CustomAttribute)]);
            Assert.False(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.True(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Equal((int)DispatchID.TEXT, Assert.IsType<DispIdAttribute>(property.Attributes[typeof(DispIdAttribute)]).Value);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Category_Get_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.Equal("Misc", property.Category);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Category_GetWithCategoryAttribute_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CategoryProperty)];
            Assert.Equal("Category", property.Category);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_ComponentType_Get_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.Equal(typeof(CustomAxHost), property.ComponentType);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Converter_Get_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.IsType<StringConverter>(property.Converter);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Converter_GetDispId_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdProperty)];
            Assert.IsType<StringConverter>(property.Converter);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Converter_GetDispIdWithOcx_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdProperty)];
            Assert.IsType<StringConverter>(property.Converter);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);

            // Call again to test caching behaviour.
            Assert.IsType<StringConverter>(property.Converter);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Converter_GetCustomConverter_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomConverterProperty)];
            Assert.IsType<CustomTypeConverter>(property.Converter);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Converter_GetDispIdCustomConverter_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdCustomConverterProperty)];
            Assert.IsType<CustomTypeConverter>(property.Converter);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Description_Get_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.Empty(property.Description);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_Description_GetWithDescriptionAttribute_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DescriptionProperty)];
            Assert.Equal("Description", property.Description);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_IsReadOnly_Get_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.False(property.IsReadOnly);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_IsReadOnly_GetWithoutSetter_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.GetOnlyProperty)];
            Assert.True(property.IsReadOnly);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_IsBrowsable_Get_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.True(property.IsBrowsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_IsBrowsable_GetBrowsableAttribute_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.NotBrowsableProperty)];
            Assert.False(property.IsBrowsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_IsReadOnly_GetReadOnlyAttribute_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.ReadOnlyProperty)];
            Assert.True(property.IsReadOnly);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_PropertyType_Get_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.Equal(typeof(string), property.PropertyType);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_CanResetValue_Invoke_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomValue"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.True(property.CanResetValue(control));
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_CanResetValue_InvokeReadOnly_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.ReadOnlyProperty)];
            Assert.False(property.CanResetValue(control));
            Assert.False(control.IsHandleCreated);
        }

#if false
        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_Invoke_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomValue"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.Null(property.GetEditor(typeof(UITypeEditor)));
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_InvokeWithEditor_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.EditorProperty)];
            Assert.IsType<CustomEditor>(property.GetEditor(typeof(object)));
            Assert.Null(property.GetEditor(typeof(CustomEditor)));
            Assert.Null(property.GetEditor(typeof(UITypeEditor)));
            Assert.Null(property.GetEditor(typeof(int)));
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_InvokeWithUITypeEditor_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.UITypeEditorProperty)];
            Assert.Null(property.GetEditor(typeof(object));
            Assert.Null(property.GetEditor(typeof(CustomUITypeEditor)));
            Assert.IsType<CustomUITypeEditor>(property.GetEditor(typeof(UITypeEditor)));
            Assert.Null(property.GetEditor(typeof(int)));
            Assert.False(control.IsHandleCreated);
        }
#endif

        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_InvokeDispId_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdProperty)];
            Assert.Null(property.GetEditor(typeof(UITypeEditor)));
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_InvokeDispIdWithEditor_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdEditorProperty)];
            Assert.IsType<CustomEditor>(property.GetEditor(typeof(object)));
            Assert.Null(property.GetEditor(typeof(CustomEditor)));
            Assert.Null(property.GetEditor(typeof(UITypeEditor)));
            Assert.Null(property.GetEditor(typeof(int)));
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_InvokeDispIdWithUITypeEditor_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdUITypeEditorProperty)];
            Assert.Null(property.GetEditor(typeof(object)));
            Assert.Null(property.GetEditor(typeof(CustomUITypeEditor)));
            Assert.IsType<CustomUITypeEditor>(property.GetEditor(typeof(UITypeEditor)));
            Assert.Null(property.GetEditor(typeof(int)));
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_InvokeDispIdWithOcx_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdProperty)];
            Assert.Null(property.GetEditor(typeof(UITypeEditor)));
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_InvokeDispIdWithOcxWithEditor_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdEditorProperty)];
            Assert.IsType<CustomEditor>(property.GetEditor(typeof(object)));
            Assert.Null(property.GetEditor(typeof(CustomEditor)));
            Assert.Null(property.GetEditor(typeof(UITypeEditor)));
            Assert.Null(property.GetEditor(typeof(int)));
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_InvokeDispIdWithOcxWithUITypeEditor_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdUITypeEditorProperty)];
            Assert.Null(property.GetEditor(typeof(object)));
            Assert.Null(property.GetEditor(typeof(CustomUITypeEditor)));
            Assert.IsType<CustomUITypeEditor>(property.GetEditor(typeof(UITypeEditor)));
            Assert.Null(property.GetEditor(typeof(int)));
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

#if false
        [WinFormsFact]
        public void AxPropertyDescriptor_GetEditor_NullEditorBaseType_ThrowsArgumentNullException()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomValue"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdProperty)];
            Assert.Throws<ArgumentNullException>("editorBaseType", () => property.GetEditor(null));
        }
#endif

        [WinFormsFact]
        public void AxPropertyDescriptor_GetValue_Invoke_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];

            Assert.Null(property.GetValue(control));
            Assert.NotNull(property.Attributes[typeof(CustomAttribute)]);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.False(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Null(property.Attributes[typeof(DispIdAttribute)]);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetValue_InvokeDataSource_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                DataSourceProperty = new DataSource
                {
                    Value = "CustomProperty"
                }
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DataSourceProperty)];

            Assert.Null(property.GetValue(control));
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetValue_InvokeDispatchIdWithDataSource_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                DispIdDataSourceProperty = new DataSource
                {
                    Value = "CustomProperty"
                }
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdDataSourceProperty)];

            Assert.Equal("CustomProperty", Assert.IsType<DataSource>(property.GetValue(control)).Value);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetValue_InvokeWithOcx_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];

            Assert.Equal("CustomProperty", property.GetValue(control));
            Assert.NotNull(property.Attributes[typeof(CustomAttribute)]);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.False(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Null(property.Attributes[typeof(DispIdAttribute)]);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetValue_InvokeDataSourceWithOcx_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                DataSourceProperty = new DataSource
                {
                    Value = "CustomProperty"
                }
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DataSourceProperty)];

            Assert.Equal("CustomProperty", Assert.IsType<DataSource>(property.GetValue(control)).Value);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_GetValue_InvokeDispatchIdWithDataSourceWithOcx_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                DispIdDataSourceProperty = new DataSource
                {
                    Value = "CustomProperty"
                }
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdDataSourceProperty)];

            Assert.Equal("CustomProperty", Assert.IsType<DataSource>(property.GetValue(control)).Value);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_GetValue_NullComponent_ReturnsExpected(string propertyName)
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            Assert.Null(property.GetValue(null));
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_GetValue_NullComponentWithOcx_ThrowsArgumentNullException(string propertyName)
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            Assert.Null(property.GetValue(null));
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_GetValueInvalidComponent_ReturnsExpected(string propertyName)
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            Assert.Null(property.GetValue(new object()));
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_GetValueInvalidComponentWithOcx_ThrowsTargetInvocationException(string propertyName)
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            Assert.Throws<TargetInvocationException>(() => property.GetValue(new object()));
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);

            // Call again.
            Assert.Null(property.GetValue(new object()));
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_ResetValue_Invoke_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomValue"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            property.ResetValue(control);
            Assert.Null(control.CustomProperty);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_ResetValue_InvokeReadOnly_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.ReadOnlyProperty)];
            property.ResetValue(control);
            Assert.Null(control.CustomProperty);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValue_Invoke_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];

            property.SetValue(control, "CustomProperty");
            Assert.Equal("CustomProperty", control.CustomProperty);
            Assert.NotNull(property.Attributes[typeof(CustomAttribute)]);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.False(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Null(property.Attributes[typeof(DispIdAttribute)]);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValue_InvokeDataSource_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DataSourceProperty)];

            property.SetValue(control, new DataSource
            {
                Value = "CustomProperty"
            });
            Assert.Null(control.DataSourceProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValue_InvokeDispatchIdWithDataSource_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdDataSourceProperty)];

            property.SetValue(control, new DataSource
            {
                Value = "CustomProperty"
            });
            Assert.Null(control.DataSourceProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValue_InvokeWithOcx_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];

            property.SetValue(control, "CustomProperty");
            Assert.Equal("CustomProperty", control.CustomProperty);
            Assert.NotNull(property.Attributes[typeof(CustomAttribute)]);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.Equal("Misc", Assert.IsType<CategoryAttribute>(property.Attributes[typeof(CategoryAttribute)]).Category);
            Assert.Empty(Assert.IsType<DescriptionAttribute>(property.Attributes[typeof(DescriptionAttribute)]).Description);
            Assert.False(Assert.IsType<ReadOnlyAttribute>(property.Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly);
            Assert.Null(property.Attributes[typeof(DispIdAttribute)]);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValue_InvokeWithOcxDataSource_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DataSourceProperty)];

            property.SetValue(control, new DataSource
            {
                Value = "CustomProperty"
            });
            Assert.Equal("CustomProperty", Assert.IsType<DataSource>(control.DataSourceProperty).Value);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValue_InvokeWithOcxDispatchIdWithDataSource_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.DispIdDataSourceProperty)];

            property.SetValue(control, new DataSource
            {
                Value = "CustomProperty"
            });
            Assert.Null(control.DataSourceProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValue_InvokeEnumWithOcx_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.EnumProperty)];

            property.SetValue(control, ConsoleColor.Blue);
            Assert.Equal(ConsoleColor.Blue, control.EnumProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValue_InvokeEnumNotEnumTypeWithOcx_ReturnsExpected()
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                CustomProperty = "CustomProperty"
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.EnumProperty)];

            property.SetValue(control, (int)ConsoleColor.Blue);
            Assert.Equal(ConsoleColor.Blue, control.EnumProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_SetValue_NullComponent_ReturnsExpected(string propertyName)
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            property.SetValue(null, "CustomProperty");
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_SetValue_NullComponentWithOcx_ThrowsArgumentNullException(string propertyName)
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            property.SetValue(null, "Value");
            Assert.Null(control.CustomProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_SetValueInvalidComponent_ReturnsExpected(string propertyName)
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            property.SetValue(new object(), "Value");
            Assert.Null(control.CustomProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_SetValueInvalidComponentWithOcx_ThrowsTargetException(string propertyName)
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            Assert.Throws<TargetException>(() => property.SetValue(new object(), "Value"));
            Assert.Null(control.CustomProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);

            // Call again.
            Assert.Throws<TargetException>(() => property.SetValue(new object(), "Value"));
            Assert.Null(control.CustomProperty);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_SetValueInvalidValue_ReturnsExpected(string propertyName)
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            property.SetValue(control, new object());
            Assert.Null(control.CustomProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsTheory]
        [InlineData(nameof(CustomAxHost.CustomProperty))]
        [InlineData(nameof(CustomAxHost.DispIdProperty))]
        public void AxPropertyDescriptor_SetValueInvalidValueWithOcx_ThrowsArgumentException(string propertyName)
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[propertyName];

            Assert.Throws<ArgumentException>(null, () => property.SetValue(control, new object()));
            Assert.Null(control.CustomProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);

            // Call again.
            Assert.Throws<ArgumentException>(null, () => property.SetValue(control, new object()));
            Assert.Null(control.CustomProperty);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValueInvalidValueInt_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.IntProperty)];

            property.SetValue(control, new object());
            Assert.Equal(0, control.IntProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValueInvalidValueIntWithOcx_ThrowsArgumentException()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.IntProperty)];

            Assert.Throws<ArgumentException>(null, () => property.SetValue(control, new object()));
            Assert.Equal(0, control.IntProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);

            // Call again.
            Assert.Throws<ArgumentException>(null, () => property.SetValue(control, new object()));
            Assert.Equal(0, control.IntProperty);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValueInvalidValueEnum_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.EnumProperty)];

            property.SetValue(control, new object());
            Assert.Equal((ConsoleColor)0, control.EnumProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValueInvalidValueEnumWithOcx_ThrowsArgumentException()
        {
            using var control = new CustomAxHost(WebBrowserClsidString);
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.EnumProperty)];

            Assert.Throws<ArgumentException>("value", () => property.SetValue(control, new object()));
            Assert.Equal((ConsoleColor)0, control.EnumProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);

            // Call again.
            Assert.Throws<ArgumentException>("value", () => property.SetValue(control, new object()));
            Assert.Equal((ConsoleColor)0, control.EnumProperty);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValueNullValueInt_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                IntProperty = 1
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.IntProperty)];

            property.SetValue(control, null);
            Assert.Equal(1, control.IntProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValueNullValueIntWithOcx_ThrowsArgumentException()
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                IntProperty = 1
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.IntProperty)];

            property.SetValue(control, null);
            Assert.Equal(0, control.IntProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);

            // Call again.
            property.SetValue(control, null);
            Assert.Equal(0, control.IntProperty);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_SetValueNullValueEnum_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                EnumProperty = ConsoleColor.Blue
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.EnumProperty)];

            property.SetValue(control, null);
            Assert.Equal(ConsoleColor.Blue, control.EnumProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.False(control.IsHandleCreated);
        }

#if false
        [WinFormsFact]
        public void AxPropertyDescriptor_SetValueNullValueEnumWithOcx_ThrowsArgumentException()
        {
            using var control = new CustomAxHost(WebBrowserClsidString)
            {
                EnumProperty = ConsoleColor.Blue
            };
            Assert.NotEqual(IntPtr.Zero, control.Handle);
            int invalidatedCallCount = 0;
            control.Invalidated += (sender, e) => invalidatedCallCount++;
            int styleChangedCallCount = 0;
            ((Control)control).StyleChanged += (sender, e) => styleChangedCallCount++;
            int createdCallCount = 0;
            control.HandleCreated += (sender, e) => createdCallCount++;
            Assert.NotNull(control.GetOcx());

            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.EnumProperty)];

            property.SetValue(control, null);
            Assert.Equal(ConsoleColor.Blue, control.EnumProperty);
            Assert.True(Assert.IsType<BrowsableAttribute>(property.Attributes[typeof(BrowsableAttribute)]).Browsable);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);

            // Call again.
            property.SetValue(control, new object());
            Assert.Equal(ConsoleColor.Blue, control.EnumProperty);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
        }
#endif

        [WinFormsFact]
        public void AxPropertyDescriptor_ShouldSerializeValue_Invoke_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString)
            {
                CustomProperty = "CustomValue"
            };
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.CustomProperty)];
            Assert.True(property.ShouldSerializeValue(control));
            Assert.False(control.IsHandleCreated);
        }

        [WinFormsFact]
        public void AxPropertyDescriptor_ShouldSerializeValue_InvokeReadOnly_ReturnsExpected()
        {
            using var control = new CustomAxHost(EmptyClsidString);
            ICustomTypeDescriptor customTypeDescriptor = control;
            PropertyDescriptorCollection events = customTypeDescriptor.GetProperties();
            PropertyDescriptor property = events[nameof(CustomAxHost.ReadOnlyProperty)];
            Assert.False(property.ShouldSerializeValue(control));
            Assert.False(control.IsHandleCreated);
        }

        private class SubComponentEditor : ComponentEditor
        {
            public override bool EditComponent(ITypeDescriptorContext context, object component)
            {
                throw new NotImplementedException();
            }
        }

        [AttributeUsage(AttributeTargets.All)]
        private class CustomAttribute : Attribute
        {
        }

        private class CustomTypeConverter : TypeConverter
        {
        }

        private class CustomEditor
        {
        }

        private class CustomUITypeEditor : UITypeEditor
        {
        }

        [Guid("7C0FFAB3-CD84-11D0-949A-00A0C91110ED")]
        private class DataSource
        {
            public string Value { get; set; }
        }

        [CustomAttribute]
        private class CustomAxHost : AxHost
        {
            public CustomAxHost(string clsid) : base(clsid)
            {
            }

            [CustomAttribute]
            [DefaultValue(null)]
            public string CustomProperty { get; set; }

            public DataSource DataSourceProperty { get; set; }

            public ConsoleColor EnumProperty { get; set; }

            public int IntProperty { get; set; }

            [TypeConverter(typeof(CustomTypeConverter))]
            public string CustomConverterProperty { get; set; }

            [TypeConverter(typeof(CustomTypeConverter))]
            public string DispIdCustomConverterProperty { get; set; }

            [Editor(typeof(CustomEditor), typeof(object))]
            public string EditorProperty { get; set; }

            [Editor(typeof(CustomUITypeEditor), typeof(UITypeEditor))]
            public string UITypeEditorProperty { get; set; }

            public string GetOnlyProperty { get; }

            [Category("Category")]
            public string CategoryProperty { get; set; }

            [DispId((int)DispatchID.TEXT)]
            [Description("Description")]
            public string DescriptionProperty { get; set; }

            [Browsable(false)]
            public string NotBrowsableProperty { get; set; }

            [ReadOnly(true)]
            public string ReadOnlyProperty { get; set; }

            [DispId((int)DispatchID.TEXT)]
            public string DispIdProperty { get; set; }

            [DispId((int)DispatchID.TEXT)]
            public DataSource DispIdDataSourceProperty { get; set; }

            [DispId((int)DispatchID.TEXT)]
            [Browsable(false)]
            public string DispIdNotBrowsableProperty { get; set; }

            [DispId((int)DispatchID.TEXT)]
            [ReadOnly(true)]
            public string DispIdReadOnlyProperty { get; set; }

            [DispId((int)DispatchID.TEXT)]
            [ReadOnly(true)]
            [Browsable(false)]
            public string DispIdNotBrowsableReadOnlyProperty { get; set; }

            [DispId((int)DispatchID.TEXT)]
            [Editor(typeof(CustomEditor), typeof(object))]
            public string DispIdEditorProperty { get; set; }

            [DispId((int)DispatchID.TEXT)]
            [Editor(typeof(CustomUITypeEditor), typeof(UITypeEditor))]
            public string DispIdUITypeEditorProperty { get; set; }
        }
    }
}
