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

using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms.Layout;
using System.Windows.Forms.TestUtilities;
using Moq;
using Windows.Win32.System.Variant;
using Windows.Win32.UI.Accessibility;
using Point = System.Drawing.Point;
using Size = System.Drawing.Size;

namespace System.Windows.Forms.Tests;

public class ButtonTests : AbstractButtonBaseTests
{
    [WinFormsFact]
    public void Button_Ctor_Default()
    {
        using SubButton control = new();
        Assert.Null(control.AccessibleDefaultActionDescription);
        Assert.Null(control.AccessibleDescription);
        Assert.Null(control.AccessibleName);
        Assert.Equal(AccessibleRole.Default, control.AccessibleRole);
        Assert.False(control.AllowDrop);
        Assert.Equal(AnchorStyles.Top | AnchorStyles.Left, control.Anchor);
        Assert.False(control.AutoEllipsis);
        Assert.False(control.AutoSize);
        Assert.Equal(AutoSizeMode.GrowOnly, control.AutoSizeMode);
        Assert.Equal(Control.DefaultBackColor, control.BackColor);
        Assert.Null(control.BackgroundImage);
        Assert.Equal(ImageLayout.Tile, control.BackgroundImageLayout);
        Assert.Null(control.BindingContext);
        Assert.Equal(23, control.Bottom);
        Assert.Equal(new Rectangle(0, 0, 75, 23), control.Bounds);
        Assert.False(control.CanEnableIme);
        Assert.False(control.CanFocus);
        Assert.True(control.CanRaiseEvents);
        Assert.True(control.CanSelect);
        Assert.False(control.Capture);
        Assert.True(control.CausesValidation);
        Assert.Equal(new Size(75, 23), control.ClientSize);
        Assert.Equal(new Rectangle(0, 0, 75, 23), control.ClientRectangle);
        Assert.Null(control.Container);
        Assert.False(control.ContainsFocus);
        Assert.Null(control.ContextMenuStrip);
        Assert.Empty(control.Controls);
        Assert.Same(control.Controls, control.Controls);
        Assert.False(control.Created);
        Assert.Same(Cursors.Default, control.Cursor);
        Assert.Same(Cursors.Default, control.DefaultCursor);
        Assert.Equal(ImeMode.Disable, control.DefaultImeMode);
        Assert.Equal(new Padding(3), control.DefaultMargin);
        Assert.Equal(Size.Empty, control.DefaultMaximumSize);
        Assert.Equal(Size.Empty, control.DefaultMinimumSize);
        Assert.Equal(Padding.Empty, control.DefaultPadding);
        Assert.Equal(new Size(75, 23), control.DefaultSize);
        Assert.False(control.DesignMode);
        Assert.Equal(DialogResult.None, control.DialogResult);
        Assert.Equal(new Rectangle(0, 0, 75, 23), control.DisplayRectangle);
        Assert.Equal(DockStyle.None, control.Dock);
        Assert.True(control.DoubleBuffered);
        Assert.True(control.Enabled);
        Assert.NotNull(control.Events);
        Assert.Same(control.Events, control.Events);
        Assert.NotNull(control.FlatAppearance);
        Assert.Same(control.FlatAppearance, control.FlatAppearance);
        Assert.Equal(FlatStyle.Standard, control.FlatStyle);
        Assert.False(control.Focused);
        Assert.Equal(Control.DefaultFont, control.Font);
        Assert.Equal(control.Font.Height, control.FontHeight);
        Assert.Equal(Control.DefaultForeColor, control.ForeColor);
        Assert.False(control.HasChildren);
        Assert.Equal(23, control.Height);
        Assert.Null(control.Image);
        Assert.Equal(ContentAlignment.MiddleCenter, control.ImageAlign);
        Assert.Equal(-1, control.ImageIndex);
        Assert.Empty(control.ImageKey);
        Assert.Null(control.ImageList);
        Assert.Equal(ImeMode.Disable, control.ImeMode);
        Assert.Equal(ImeMode.Disable, control.ImeModeBase);
        Assert.False(control.IsAccessible);
        Assert.False(control.IsDefault);
        Assert.False(control.IsMirrored);
        Assert.NotNull(control.LayoutEngine);
        Assert.Same(control.LayoutEngine, control.LayoutEngine);
        Assert.Equal(0, control.Left);
        Assert.Equal(Point.Empty, control.Location);
        Assert.Equal(new Padding(3), control.Margin);
        Assert.Equal(Size.Empty, control.MaximumSize);
        Assert.Equal(Size.Empty, control.MinimumSize);
        Assert.Equal(Padding.Empty, control.Padding);
        Assert.Null(control.Parent);
        Assert.Equal("Microsoft\u00AE .NET", control.ProductName);
        Assert.Equal(new Size(75, 23), control.PreferredSize);
        Assert.False(control.RecreatingHandle);
        Assert.Null(control.Region);
        Assert.True(control.ResizeRedraw);
        Assert.Equal(75, control.Right);
        Assert.Equal(RightToLeft.No, control.RightToLeft);
        Assert.True(control.ShowFocusCues);
        Assert.True(control.ShowKeyboardCues);
        Assert.Null(control.Site);
        Assert.Equal(new Size(75, 23), control.Size);
        Assert.Equal(0, control.TabIndex);
        Assert.True(control.TabStop);
        Assert.Empty(control.Text);
        Assert.Equal(ContentAlignment.MiddleCenter, control.TextAlign);
        Assert.Equal(TextImageRelation.Overlay, control.TextImageRelation);
        Assert.Equal(0, control.Top);
        Assert.Null(control.TopLevelControl);
        Assert.True(control.UseCompatibleTextRendering);
        Assert.True(control.UseMnemonic);
        Assert.True(control.UseVisualStyleBackColor);
        Assert.False(control.UseWaitCursor);
        Assert.True(control.Visible);
        Assert.Equal(75, control.Width);

        Assert.False(control.IsHandleCreated);
    }

    [WinFormsFact]
    public void Button_CreateParams_GetDefault_ReturnsExpected()
    {
        using SubButton control = new();
        CreateParams createParams = control.CreateParams;
        Assert.Null(createParams.Caption);
        Assert.Equal("Button", createParams.ClassName);
        Assert.Equal(0x8, createParams.ClassStyle);
        Assert.Equal(0, createParams.ExStyle);
        Assert.Equal(23, createParams.Height);
        Assert.Equal(IntPtr.Zero, createParams.Parent);
        Assert.Null(createParams.Param);
        Assert.Equal(0x5601000B, createParams.Style);
        Assert.Equal(75, createParams.Width);
        Assert.Equal(0, createParams.X);
        Assert.Equal(0, createParams.Y);
        Assert.Same(createParams, control.CreateParams);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(true, 0x5601000B)]
    [InlineData(false, 0x56010000)]
    public void Button_CreateParams_GetUserPaint_ReturnsExpected(bool userPaint, int expectedStyle)
    {
        using SubButton control = new();
        control.SetStyle(ControlStyles.UserPaint, userPaint);

        CreateParams createParams = control.CreateParams;
        Assert.Null(createParams.Caption);
        Assert.Equal("Button", createParams.ClassName);
        Assert.Equal(0x8, createParams.ClassStyle);
        Assert.Equal(0, createParams.ExStyle);
        Assert.Equal(23, createParams.Height);
        Assert.Equal(IntPtr.Zero, createParams.Parent);
        Assert.Null(createParams.Param);
        Assert.Equal(expectedStyle, createParams.Style);
        Assert.Equal(75, createParams.Width);
        Assert.Equal(0, createParams.X);
        Assert.Equal(0, createParams.Y);
        Assert.Same(createParams, control.CreateParams);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(FlatStyle.Flat, true, 0x5601000B)]
    [InlineData(FlatStyle.Flat, false, 0x5601000B)]
    [InlineData(FlatStyle.Popup, true, 0x5601000B)]
    [InlineData(FlatStyle.Popup, false, 0x5601000B)]
    [InlineData(FlatStyle.Standard, true, 0x5601000B)]
    [InlineData(FlatStyle.Standard, false, 0x5601000B)]
    [InlineData(FlatStyle.System, true, 0x56012F01)]
    [InlineData(FlatStyle.System, false, 0x56012F00)]
    public void Button_CreateParams_GetIsDefault_ReturnsExpected(FlatStyle flatStyle, bool isDefault, int expectedStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle,
            IsDefault = isDefault
        };

        CreateParams createParams = control.CreateParams;
        Assert.Null(createParams.Caption);
        Assert.Equal("Button", createParams.ClassName);
        Assert.Equal(0x8, createParams.ClassStyle);
        Assert.Equal(0, createParams.ExStyle);
        Assert.Equal(23, createParams.Height);
        Assert.Equal(IntPtr.Zero, createParams.Parent);
        Assert.Null(createParams.Param);
        Assert.Equal(expectedStyle, createParams.Style);
        Assert.Equal(75, createParams.Width);
        Assert.Equal(0, createParams.X);
        Assert.Equal(0, createParams.Y);
        Assert.Same(createParams, control.CreateParams);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> CreateParams_GetIsDefault_TestData()
    {
        foreach (FlatStyle flatStyle in new FlatStyle[] { FlatStyle.Flat, FlatStyle.Popup, FlatStyle.Standard })
        {
            foreach (ContentAlignment textAlign in Enum.GetValues(typeof(ContentAlignment)))
            {
                yield return new object[] { flatStyle, RightToLeft.Yes, textAlign, 0x5601000B, 0x7000 };
                yield return new object[] { flatStyle, RightToLeft.No, textAlign, 0x5601000B, 0 };
                yield return new object[] { flatStyle, RightToLeft.Inherit, textAlign, 0x5601000B, 0 };
            }
        }

        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.BottomLeft, 0x56012A00, 0x6000 };
        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.BottomCenter, 0x56012B00, 0x6000 };
        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.BottomRight, 0x56012900, 0x6000 };
        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.MiddleLeft, 0x56012E00, 0x6000 };
        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.MiddleCenter, 0x56012F00, 0x6000 };
        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.MiddleRight, 0x56012D00, 0x6000 };
        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.TopLeft, 0x56012600, 0x6000 };
        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.TopCenter, 0x56012700, 0x6000 };
        yield return new object[] { FlatStyle.System, RightToLeft.Yes, ContentAlignment.TopRight, 0x56012500, 0x6000 };

        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.BottomLeft, 0x56012900, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.BottomCenter, 0x56012B00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.BottomRight, 0x56012A00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.MiddleLeft, 0x56012D00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.MiddleCenter, 0x56012F00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.MiddleRight, 0x56012E00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.TopLeft, 0x56012500, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.TopCenter, 0x56012700, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.No, ContentAlignment.TopRight, 0x56012600, 0 };

        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.BottomLeft, 0x56012900, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.BottomCenter, 0x56012B00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.BottomRight, 0x56012A00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.MiddleLeft, 0x56012D00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.MiddleCenter, 0x56012F00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.MiddleRight, 0x56012E00, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.TopLeft, 0x56012500, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.TopCenter, 0x56012700, 0 };
        yield return new object[] { FlatStyle.System, RightToLeft.Inherit, ContentAlignment.TopRight, 0x56012600, 0 };
    }

    [WinFormsTheory]
    [MemberData(nameof(CreateParams_GetIsDefault_TestData))]
    public void Button_CreateParams_GetTextAlign_ReturnsExpected(FlatStyle flatStyle, RightToLeft rightToLeft, ContentAlignment textAlign, int expectedStyle, int expectedExStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle,
            RightToLeft = rightToLeft,
            TextAlign = textAlign
        };

        CreateParams createParams = control.CreateParams;
        Assert.Null(createParams.Caption);
        Assert.Equal("Button", createParams.ClassName);
        Assert.Equal(0x8, createParams.ClassStyle);
        Assert.Equal(expectedExStyle, createParams.ExStyle);
        Assert.Equal(23, createParams.Height);
        Assert.Equal(IntPtr.Zero, createParams.Parent);
        Assert.Null(createParams.Param);
        Assert.Equal(expectedStyle, createParams.Style);
        Assert.Equal(75, createParams.Width);
        Assert.Equal(0, createParams.X);
        Assert.Equal(0, createParams.Y);
        Assert.Same(createParams, control.CreateParams);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [EnumData<AutoSizeMode>]
    public void Button_AutoSizeMode_Set_GetReturnsExpected(AutoSizeMode value)
    {
        using SubButton control = new();
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;

        control.AutoSizeMode = value;
        Assert.Equal(value, control.AutoSizeMode);
        Assert.Equal(value, control.GetAutoSizeMode());
        Assert.False(control.IsHandleCreated);
        Assert.Equal(0, layoutCallCount);

        // Set same.
        control.AutoSizeMode = value;
        Assert.Equal(value, control.AutoSizeMode);
        Assert.Equal(value, control.GetAutoSizeMode());
        Assert.False(control.IsHandleCreated);
        Assert.Equal(0, layoutCallCount);
    }

    [WinFormsTheory]
    [InlineData(AutoSizeMode.GrowAndShrink, 1)]
    [InlineData(AutoSizeMode.GrowOnly, 0)]
    public void Button_AutoSizeMode_SetWithParent_GetReturnsExpected(AutoSizeMode value, int expectedLayoutCallCount)
    {
        using Control parent = new();
        using SubButton control = new()
        {
            Parent = parent
        };
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(parent, sender);
            Assert.Same(control, e.AffectedControl);
            Assert.Equal("AutoSize", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        parent.Layout += parentHandler;

        try
        {
            control.AutoSizeMode = value;
            Assert.Equal(value, control.AutoSizeMode);
            Assert.Equal(value, control.GetAutoSizeMode());
            Assert.False(control.IsHandleCreated);
            Assert.Equal(0, layoutCallCount);
            Assert.False(parent.IsHandleCreated);
            Assert.Equal(expectedLayoutCallCount, parentLayoutCallCount);

            // Set same.
            control.AutoSizeMode = value;
            Assert.Equal(value, control.AutoSizeMode);
            Assert.Equal(value, control.GetAutoSizeMode());
            Assert.False(control.IsHandleCreated);
            Assert.Equal(0, layoutCallCount);
            Assert.False(parent.IsHandleCreated);
            Assert.Equal(expectedLayoutCallCount, parentLayoutCallCount);
        }
        finally
        {
            parent.Layout -= parentHandler;
        }
    }

    [WinFormsTheory]
    [InlineData(AutoSizeMode.GrowAndShrink, 1)]
    [InlineData(AutoSizeMode.GrowOnly, 0)]
    public void Button_AutoSizeMode_SetWithCustomLayoutEngineParent_GetReturnsExpected(AutoSizeMode value, int expectedLayoutCallCount)
    {
        using CustomLayoutEngineControl parent = new();
        using SubButton control = new()
        {
            Parent = parent
        };
        parent.SetLayoutEngine(new SubLayoutEngine());
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(parent, sender);
            Assert.Same(control, e.AffectedControl);
            Assert.Equal("AutoSize", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        parent.Layout += parentHandler;
        try
        {
            control.AutoSizeMode = value;
            Assert.Equal(value, control.AutoSizeMode);
            Assert.Equal(value, control.GetAutoSizeMode());
            Assert.False(control.IsHandleCreated);
            Assert.Equal(0, layoutCallCount);
            Assert.False(parent.IsHandleCreated);
            Assert.Equal(expectedLayoutCallCount, parentLayoutCallCount);

            // Set same.
            control.AutoSizeMode = value;
            Assert.Equal(value, control.AutoSizeMode);
            Assert.Equal(value, control.GetAutoSizeMode());
            Assert.False(control.IsHandleCreated);
            Assert.Equal(0, layoutCallCount);
            Assert.False(parent.IsHandleCreated);
            Assert.Equal(expectedLayoutCallCount, parentLayoutCallCount);
        }
        finally
        {
            parent.Layout -= parentHandler;
        }
    }

    private class CustomLayoutEngineControl : Control
    {
        private LayoutEngine _layoutEngine;

        public CustomLayoutEngineControl()
        {
            _layoutEngine = base.LayoutEngine;
        }

        public void SetLayoutEngine(LayoutEngine layoutEngine)
        {
            _layoutEngine = layoutEngine;
        }

        public override LayoutEngine LayoutEngine => _layoutEngine;
    }

    private class SubLayoutEngine : LayoutEngine
    {
    }

    [WinFormsTheory]
    [EnumData<AutoSizeMode>]
    public void Button_AutoSizeMode_SetWithHandle_GetReturnsExpected(AutoSizeMode value)
    {
        using SubButton control = new();
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        control.AutoSizeMode = value;
        Assert.Equal(value, control.AutoSizeMode);
        Assert.Equal(value, control.GetAutoSizeMode());
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Set same.
        control.AutoSizeMode = value;
        Assert.Equal(value, control.AutoSizeMode);
        Assert.Equal(value, control.GetAutoSizeMode());
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsTheory]
    [InlineData(AutoSizeMode.GrowAndShrink, 1)]
    [InlineData(AutoSizeMode.GrowOnly, 0)]
    public void Button_AutoSizeMode_SetWithParentWithHandle_GetReturnsExpected(AutoSizeMode value, int expectedLayoutCallCount)
    {
        using Control parent = new();
        using SubButton control = new()
        {
            Parent = parent
        };
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(parent, sender);
            Assert.Same(control, e.AffectedControl);
            Assert.Equal("AutoSize", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        parent.Layout += parentHandler;
        Assert.NotEqual(IntPtr.Zero, parent.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int parentInvalidatedCallCount = 0;
        parent.Invalidated += (sender, e) => parentInvalidatedCallCount++;
        int parentStyleChangedCallCount = 0;
        parent.StyleChanged += (sender, e) => parentStyleChangedCallCount++;
        int parentCreatedCallCount = 0;
        parent.HandleCreated += (sender, e) => parentCreatedCallCount++;

        try
        {
            control.AutoSizeMode = value;
            Assert.Equal(value, control.AutoSizeMode);
            Assert.Equal(value, control.GetAutoSizeMode());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(expectedLayoutCallCount, parentLayoutCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(parent.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);

            // Set same.
            control.AutoSizeMode = value;
            Assert.Equal(value, control.AutoSizeMode);
            Assert.Equal(value, control.GetAutoSizeMode());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(expectedLayoutCallCount, parentLayoutCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(parent.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
        }
        finally
        {
            parent.Layout -= parentHandler;
        }
    }

    [WinFormsTheory]
    [InlineData(AutoSizeMode.GrowAndShrink, 1)]
    [InlineData(AutoSizeMode.GrowOnly, 0)]
    public void Button_AutoSizeMode_SetWithCustomLayoutEngineParentWithHandle_GetReturnsExpected(AutoSizeMode value, int expectedLayoutCallCount)
    {
        using CustomLayoutEngineControl parent = new();
        using SubButton control = new()
        {
            Parent = parent
        };
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(parent, sender);
            Assert.Same(control, e.AffectedControl);
            Assert.Equal("AutoSize", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        parent.Layout += parentHandler;
        Assert.NotEqual(IntPtr.Zero, parent.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int parentInvalidatedCallCount = 0;
        parent.Invalidated += (sender, e) => parentInvalidatedCallCount++;
        int parentStyleChangedCallCount = 0;
        parent.StyleChanged += (sender, e) => parentStyleChangedCallCount++;
        int parentCreatedCallCount = 0;
        parent.HandleCreated += (sender, e) => parentCreatedCallCount++;

        try
        {
            control.AutoSizeMode = value;
            Assert.Equal(value, control.AutoSizeMode);
            Assert.Equal(value, control.GetAutoSizeMode());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(expectedLayoutCallCount, parentLayoutCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(parent.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);

            // Set same.
            control.AutoSizeMode = value;
            Assert.Equal(value, control.AutoSizeMode);
            Assert.Equal(value, control.GetAutoSizeMode());
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(expectedLayoutCallCount, parentLayoutCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(parent.IsHandleCreated);
            Assert.Equal(0, parentInvalidatedCallCount);
            Assert.Equal(0, parentStyleChangedCallCount);
            Assert.Equal(0, parentCreatedCallCount);
        }
        finally
        {
            parent.Layout -= parentHandler;
        }
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsFact(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    public void Button_AutoSize_SetCachedPreferredSize_DoesNotInvalidate()
    {
        using Button control = new()
        {
            FlatStyle = FlatStyle.System,
            AutoSizeMode = AutoSizeMode.GrowOnly,
            Text = "text"
        };
        Assert.Equal(new Size(75, 24), control.GetPreferredSize(Size.Empty));

        // Set font.
        Size newSize = TextRenderer.MeasureText(control.Text, control.Font);
        control.AutoSizeMode = AutoSizeMode.GrowAndShrink;
        Assert.Equal(new Size(75, 24), control.GetPreferredSize(Size.Empty));
    }

    [WinFormsTheory]
    [InvalidEnumData<AutoSizeMode>]
    public void Button_AutoSizeMode_SetInvalid_ThrowsInvalidEnumArgumentException(AutoSizeMode value)
    {
        using Button control = new();
        Assert.Throws<InvalidEnumArgumentException>("value", () => control.AutoSizeMode = value);
    }

    [WinFormsTheory]
    [EnumData<DialogResult>]
    public void Button_DialogResult_Set_GetReturnsExpected(DialogResult value)
    {
        using Button control = new()
        {
            DialogResult = value
        };
        Assert.Equal(value, control.DialogResult);
        Assert.False(control.IsHandleCreated);

        // Set same.
        control.DialogResult = value;
        Assert.Equal(value, control.DialogResult);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [EnumData<DialogResult>]
    public void Button_DialogResult_SetWithHandle_GetReturnsExpected(DialogResult value)
    {
        using Button control = new();
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        control.DialogResult = value;
        Assert.Equal(value, control.DialogResult);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Set same.
        control.DialogResult = value;
        Assert.Equal(value, control.DialogResult);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsTheory]
    [InvalidEnumData<DialogResult>]
    public void Button_DialogResult_SetInvalidValue_ThrowsInvalidEnumArgumentException(DialogResult value)
    {
        using Button control = new();
        Assert.Throws<InvalidEnumArgumentException>("value", () => control.DialogResult = value);
    }

    [WinFormsTheory]
    [CommonMemberData(typeof(CommonTestHelperEx), nameof(CommonTestHelperEx.GetFontTheoryData))]
    public void Button_Font_Set_GetReturnsExpected(Font value)
    {
        using SubButton control = new()
        {
            Font = value
        };
        Assert.Equal(value ?? Control.DefaultFont, control.Font);
        Assert.Equal(control.Font.Height, control.FontHeight);
        Assert.False(control.IsHandleCreated);

        // Set same.
        control.Font = value;
        Assert.Equal(value ?? Control.DefaultFont, control.Font);
        Assert.Equal(control.Font.Height, control.FontHeight);
        Assert.False(control.IsHandleCreated);
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsFact(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    public void Button_Font_SetCachedPreferredSize_Invalidates()
    {
        using Font font = new("Arial", 100f);
        using Button control = new()
        {
            AutoSizeMode = AutoSizeMode.GrowAndShrink,
            FlatStyle = FlatStyle.System,
            Text = "text"
        };
        Size oldSize = TextRenderer.MeasureText(control.Text, control.Font);
        Assert.Equal(oldSize + new Size(14, 9), control.GetPreferredSize(Size.Empty));

        // Set font.
        Size newSize = TextRenderer.MeasureText(control.Text, font);
        control.Font = font;
        Assert.Equal(newSize + new Size(14, 9), control.GetPreferredSize(Size.Empty));
    }

    [WinFormsFact]
    public void Button_Font_SetWithHandler_CallsFontChanged()
    {
        using Button control = new();
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };
        control.FontChanged += handler;

        // Set different.
        using Font font1 = new("Arial", 8.25f);
        control.Font = font1;
        Assert.Same(font1, control.Font);
        Assert.Equal(1, callCount);

        // Set same.
        control.Font = font1;
        Assert.Same(font1, control.Font);
        Assert.Equal(1, callCount);

        // Set different.
        using var font2 = SystemFonts.DialogFont;
        control.Font = font2;
        Assert.Same(font2, control.Font);
        Assert.Equal(2, callCount);

        // Set null.
        control.Font = null;
        Assert.Equal(Control.DefaultFont, control.Font);
        Assert.Equal(3, callCount);

        // Remove handler.
        control.FontChanged -= handler;
        control.Font = font1;
        Assert.Same(font1, control.Font);
        Assert.Equal(3, callCount);
    }

    public static IEnumerable<object[]> Text_Set_TestData()
    {
        foreach (bool autoSize in new bool[] { true, false })
        {
            yield return new object[] { autoSize, null, string.Empty };
            yield return new object[] { autoSize, string.Empty, string.Empty };
            yield return new object[] { autoSize, "text", "text" };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(Text_Set_TestData))]
    public void Button_Text_Set_GetReturnsExpected(bool autoSize, string value, string expected)
    {
        using SubButton control = new()
        {
            AutoSize = autoSize
        };
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;

        control.Text = value;
        Assert.Equal(expected, control.Text);
        Assert.Equal(0, layoutCallCount);
        Assert.False(control.IsHandleCreated);

        // Set same.
        control.Text = value;
        Assert.Equal(expected, control.Text);
        Assert.Equal(0, layoutCallCount);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> Text_SetWithParent_TestData()
    {
        yield return new object[] { true, null, string.Empty, 0 };
        yield return new object[] { true, string.Empty, string.Empty, 0 };
        yield return new object[] { true, "text", "text", 1 };
    }

    [WinFormsTheory]
    [MemberData(nameof(Text_SetWithParent_TestData))]
    public void Button_Text_SetWithParent_GetReturnsExpected(bool autoSize, string value, string expected, int expectedParentLayoutCallCount)
    {
        using Control parent = new();
        using Button control = new()
        {
            AutoSize = autoSize,
            Parent = parent
        };
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(parent, sender);
            Assert.Same(control, e.AffectedControl);
            Assert.Equal("Text", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        parent.Layout += parentHandler;

        try
        {
            control.Text = value;
            Assert.Equal(expected, control.Text);
            Assert.Equal(expectedParentLayoutCallCount, layoutCallCount);
            Assert.Equal(expectedParentLayoutCallCount, parentLayoutCallCount);
            Assert.False(control.IsHandleCreated);
            Assert.False(parent.IsHandleCreated);

            // Set same.
            control.Text = value;
            Assert.Equal(expected, control.Text);
            Assert.Equal(expectedParentLayoutCallCount, layoutCallCount);
            Assert.Equal(expectedParentLayoutCallCount, parentLayoutCallCount);
            Assert.False(control.IsHandleCreated);
            Assert.False(parent.IsHandleCreated);
        }
        finally
        {
            parent.Layout -= parentHandler;
        }
    }

    public static IEnumerable<object[]> Text_SetWithHandle_TestData()
    {
        foreach (bool autoSize in new bool[] { true, false })
        {
            yield return new object[] { autoSize, null, string.Empty, 0 };
            yield return new object[] { autoSize, string.Empty, string.Empty, 0 };
            yield return new object[] { autoSize, "text", "text", 1 };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(Text_SetWithHandle_TestData))]
    public void Button_Text_SetWithHandle_GetReturnsExpected(bool autoSize, string value, string expected, int expectedInvalidatedCallCount)
    {
        using SubButton control = new()
        {
            AutoSize = autoSize
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        control.Text = value;
        Assert.Equal(expected, control.Text);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Set same.
        control.Text = value;
        Assert.Equal(expected, control.Text);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    public static IEnumerable<object[]> Text_SetWithParentWithHandle_TestData()
    {
        yield return new object[] { true, null, string.Empty, 0 };
        yield return new object[] { true, string.Empty, string.Empty, 0 };
        yield return new object[] { true, "text", "text", 1 };
    }

    [WinFormsTheory]
    [MemberData(nameof(Text_SetWithParentWithHandle_TestData))]
    public void Button_Text_SetWithParentWithHandle_GetReturnsExpected(bool autoSize, string value, string expected, int expectedParentLayoutCallCount)
    {
        using Control parent = new();
        using Button control = new()
        {
            AutoSize = autoSize,
            Parent = parent
        };
        Assert.NotEqual(IntPtr.Zero, parent.Handle);
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(parent, sender);
            Assert.Same(control, e.AffectedControl);
            Assert.Equal("Text", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        parent.Layout += parentHandler;

        try
        {
            control.Text = value;
            Assert.Equal(expected, control.Text);
            Assert.Equal(expectedParentLayoutCallCount, layoutCallCount);
            Assert.Equal(expectedParentLayoutCallCount, parentLayoutCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(expectedParentLayoutCallCount * 2, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(parent.IsHandleCreated);

            // Set same.
            control.Text = value;
            Assert.Equal(expected, control.Text);
            Assert.Equal(expectedParentLayoutCallCount, layoutCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(expectedParentLayoutCallCount * 2, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(parent.IsHandleCreated);
        }
        finally
        {
            parent.Layout -= parentHandler;
        }
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsFact(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    public void Button_Text_SetCachedPreferredSize_Invalidates()
    {
        using Button control = new()
        {
            AutoSizeMode = AutoSizeMode.GrowAndShrink,
            FlatStyle = FlatStyle.System,
            Text = "text"
        };
        Size oldSize = TextRenderer.MeasureText(control.Text, control.Font);
        Assert.Equal(oldSize + new Size(14, 9), control.GetPreferredSize(Size.Empty));

        // Set font.
        Size newSize = TextRenderer.MeasureText("NewText", control.Font);
        control.Text = "NextText";
        Assert.Equal(newSize + new Size(14, 9), control.GetPreferredSize(Size.Empty));
    }

    [WinFormsFact]
    public void Button_Text_SetWithHandler_CallsTextChanged()
    {
        using SubButton control = new();
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Equal(EventArgs.Empty, e);
            callCount++;
        };
        control.TextChanged += handler;

        // Set different.
        control.Text = "text";
        Assert.Equal("text", control.Text);
        Assert.Equal(1, callCount);

        // Set same.
        control.Text = "text";
        Assert.Equal("text", control.Text);
        Assert.Equal(1, callCount);

        // Set different.
        control.Text = null;
        Assert.Empty(control.Text);
        Assert.Equal(2, callCount);

        // Remove handler.
        control.TextChanged -= handler;
        control.Text = "text";
        Assert.Equal("text", control.Text);
        Assert.Equal(2, callCount);
    }

    [WinFormsTheory]
    [EnumData<FlatStyle>]
    public void Button_CreateAccessibilityInstance_Invoke_ReturnsExpected_IfHandleIsNotCreated(FlatStyle flatStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };

        Assert.False(control.IsHandleCreated);
        Button.ButtonBaseAccessibleObject instance = Assert.IsAssignableFrom<Button.ButtonBaseAccessibleObject>(control.CreateAccessibilityInstance());
        Assert.NotNull(instance);
        Assert.Same(control, instance.Owner);
        Assert.Equal(AccessibleStates.None, instance.State);
        Assert.Equal(AccessibleRole.None, instance.Role);
        Assert.NotSame(control.CreateAccessibilityInstance(), instance);
        Assert.NotSame(control.AccessibilityObject, instance);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [EnumData<FlatStyle>]
    public void Button_CreateAccessibilityInstance_Invoke_ReturnsExpected_IfHandleIsCreated(FlatStyle flatStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };

        control.CreateControl();
        Assert.True(control.IsHandleCreated);
        Button.ButtonBaseAccessibleObject instance = Assert.IsAssignableFrom<Button.ButtonBaseAccessibleObject>(control.CreateAccessibilityInstance());
        Assert.NotNull(instance);
        Assert.Same(control, instance.Owner);
        Assert.Equal(AccessibleStates.Focusable, instance.State);
        Assert.Equal(AccessibleRole.PushButton, instance.Role);
        Assert.NotSame(control.CreateAccessibilityInstance(), instance);
        Assert.NotSame(control.AccessibilityObject, instance);
        Assert.True(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(true, FlatStyle.Flat, AccessibleStates.Pressed | AccessibleStates.Focusable, AccessibleRole.PushButton)]
    [InlineData(true, FlatStyle.Popup, AccessibleStates.Pressed | AccessibleStates.Focusable, AccessibleRole.PushButton)]
    [InlineData(true, FlatStyle.Standard, AccessibleStates.Pressed | AccessibleStates.Focusable, AccessibleRole.PushButton)]
    [InlineData(true, FlatStyle.System, AccessibleStates.Focusable, AccessibleRole.PushButton)]
    [InlineData(false, FlatStyle.Flat, AccessibleStates.None, AccessibleRole.None)]
    [InlineData(false, FlatStyle.Popup, AccessibleStates.None, AccessibleRole.None)]
    [InlineData(false, FlatStyle.Standard, AccessibleStates.None, AccessibleRole.None)]
    [InlineData(false, FlatStyle.System, AccessibleStates.None, AccessibleRole.None)]
    public void Button_CreateAccessibilityInstance_InvokeMouseDown_ReturnsExpected(bool createControl, FlatStyle flatStyle, AccessibleStates expectedState, AccessibleRole expectedRole)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };

        if (createControl)
        {
            control.CreateControl();
        }

        Assert.Equal(createControl, control.IsHandleCreated);

        control.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
        Button.ButtonBaseAccessibleObject instance = Assert.IsAssignableFrom<Button.ButtonBaseAccessibleObject>(control.CreateAccessibilityInstance());
        Assert.NotNull(instance);
        Assert.Same(control, instance.Owner);
        Assert.Equal(expectedState, instance.State);
        Assert.Equal(expectedRole, instance.Role);
        Assert.NotSame(control.CreateAccessibilityInstance(), instance);
        Assert.NotSame(control.AccessibilityObject, instance);
        Assert.Equal(createControl, control.IsHandleCreated);
    }

    [WinFormsTheory]
    [EnumData<FlatStyle>]
    public void Button_CreateAccessibilityInstance_InvokeWithCustomRole_ReturnsExpected_IfHandleIsCreated(FlatStyle flatStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle,
            AccessibleRole = AccessibleRole.HelpBalloon
        };

        control.CreateControl();
        Assert.True(control.IsHandleCreated);
        Button.ButtonBaseAccessibleObject instance = Assert.IsAssignableFrom<Button.ButtonBaseAccessibleObject>(control.CreateAccessibilityInstance());
        Assert.NotNull(instance);
        Assert.Same(control, instance.Owner);
        Assert.Equal(AccessibleStates.Focusable, instance.State);
        Assert.Equal(AccessibleRole.HelpBalloon, instance.Role);
        Assert.NotSame(control.CreateAccessibilityInstance(), instance);
        Assert.NotSame(control.AccessibilityObject, instance);
        Assert.True(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [EnumData<FlatStyle>]
    public void Button_CreateAccessibilityInstance_InvokeWithCustomRole_ReturnsExpected_IfHandleIsNotCreated(FlatStyle flatStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle,
            AccessibleRole = AccessibleRole.HelpBalloon
        };

        Assert.False(control.IsHandleCreated);
        Button.ButtonBaseAccessibleObject instance = Assert.IsAssignableFrom<Button.ButtonBaseAccessibleObject>(control.CreateAccessibilityInstance());
        Assert.NotNull(instance);
        Assert.Same(control, instance.Owner);
        Assert.Equal(AccessibleStates.None, instance.State);
        Assert.Equal(AccessibleRole.HelpBalloon, instance.Role);
        Assert.NotSame(control.CreateAccessibilityInstance(), instance);
        Assert.NotSame(control.AccessibilityObject, instance);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [EnumData<FlatStyle>]
    public void Button_CreateAccessibilityInstance_InvokeDoDefaultAction_CallsOnClick_IfHandleIsCreated(FlatStyle flatStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };

        control.CreateControl();
        int callCount = 0;
        control.Click += (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };
        Button.ButtonBaseAccessibleObject instance = Assert.IsAssignableFrom<Button.ButtonBaseAccessibleObject>(control.CreateAccessibilityInstance());
        instance.DoDefaultAction();
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [EnumData<FlatStyle>]
    public void Button_CreateAccessibilityInstance_InvokeDoDefaultAction_CallsOnClick_IfHandleIsNotCreated(FlatStyle flatStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };

        int callCount = 0;
        control.Click += (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };
        Button.ButtonBaseAccessibleObject instance = Assert.IsAssignableFrom<Button.ButtonBaseAccessibleObject>(control.CreateAccessibilityInstance());
        instance.DoDefaultAction();
        Assert.Equal(0, callCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsFact]
    public void Button_GetAutoSizeMode_Invoke_ReturnsExpected()
    {
        using SubButton control = new();
        Assert.Equal(AutoSizeMode.GrowOnly, control.GetAutoSizeMode());
    }

    public static IEnumerable<object[]> GetPreferredSize_System_TestData()
    {
        yield return new object[] { AutoSizeMode.GrowAndShrink, Size.Empty, new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(14, 9) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(14, 9) };

        yield return new object[] { AutoSizeMode.GrowOnly, Size.Empty, new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-1, -2), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-2, -1), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-1, -1), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(1, 2), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(2, 1), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(1, 1), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(10, 20), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(30, 40), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(30, int.MaxValue), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(int.MaxValue, 40), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(int.MaxValue, int.MaxValue), new Size(75, 23) };
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_System_TestData))]
    public void Button_GetPreferredSize_InvokeSystem_ReturnsExpected(AutoSizeMode autoSizeMode, Size proposedSize, Size expected)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.System,
            AutoSizeMode = autoSizeMode
        };
        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> GetPreferredSize_SystemWithPadding_TestData()
    {
        yield return new object[] { AutoSizeMode.GrowAndShrink, Size.Empty, new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(18, 15) };

        yield return new object[] { AutoSizeMode.GrowOnly, Size.Empty, new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-1, -2), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-2, -1), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-1, -1), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(1, 2), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(2, 1), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(1, 1), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(10, 20), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(30, 40), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(30, int.MaxValue), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(int.MaxValue, 40), new Size(75, 23) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(int.MaxValue, int.MaxValue), new Size(75, 23) };
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_SystemWithPadding_TestData))]
    public void Button_GetPreferredSize_InvokeSystemWithPadding_ReturnsExpected(AutoSizeMode autoSizeMode, Size proposedSize, Size expected)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.System,
            AutoSizeMode = autoSizeMode,
            Padding = new Padding(1, 2, 3, 4)
        };
        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> GetPreferredSize_SystemWithPaddingSmallSize_TestData()
    {
        yield return new object[] { AutoSizeMode.GrowAndShrink, Size.Empty, new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(18, 15) };

        yield return new object[] { AutoSizeMode.GrowOnly, Size.Empty, new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-1, -2), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-2, -1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(-1, -1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(1, 2), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(2, 1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(1, 1), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(10, 20), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(30, 40), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(30, int.MaxValue), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(int.MaxValue, 40), new Size(18, 15) };
        yield return new object[] { AutoSizeMode.GrowOnly, new Size(int.MaxValue, int.MaxValue), new Size(18, 15) };
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_SystemWithPaddingSmallSize_TestData))]
    public void Button_GetPreferredSize_InvokeSystemWithPaddingSmallSize_ReturnsExpected(AutoSizeMode autoSizeMode, Size proposedSize, Size expected)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.System,
            AutoSizeMode = autoSizeMode,
            Size = new Size(14, 9),
            Padding = new Padding(1, 2, 3, 4)
        };
        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> GetPreferredSize_SystemWithText_TestData()
    {
        yield return new object[] { Size.Empty };
        yield return new object[] { new Size(-1, -2) };
        yield return new object[] { new Size(-2, -1) };
        yield return new object[] { new Size(-1, -1) };
        yield return new object[] { new Size(1, 2) };
        yield return new object[] { new Size(2, 1) };
        yield return new object[] { new Size(1, 1) };
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_SystemWithText_TestData))]
    public void Button_GetPreferredSize_InvokeSystemGrowOnlyWithText_ReturnsExpected(Size proposedSize)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.System,
            AutoSizeMode = AutoSizeMode.GrowOnly,
            Padding = new Padding(1, 2, 3, 4),
            Text = "Text"
        };
        Size minSize = TextRenderer.MeasureText(control.Text, control.Font);

        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(new Size(75, minSize.Height + 6 + 9), size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(new Size(75, minSize.Height + 6 + 9), size);
        Assert.False(control.IsHandleCreated);
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_SystemWithText_TestData))]
    public void Button_GetPreferredSize_InvokeSystemGrowAndShrinkWithText_ReturnsExpected(Size proposedSize)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.System,
            AutoSizeMode = AutoSizeMode.GrowAndShrink,
            Padding = new Padding(1, 2, 3, 4),
            Text = "Text"
        };
        Size minSize = TextRenderer.MeasureText(control.Text, control.Font);

        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(minSize + new Size(14, 9) + new Size(4, 6), size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(minSize + new Size(14, 9) + new Size(4, 6), size);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> GetPreferredSize_Flat_TestData()
    {
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, Size.Empty, new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(8, 8) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(8, 8) };

        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, Size.Empty, new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(6, 6) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(6, 6) };

        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, Size.Empty, new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(6, 6) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(6, 6) };

        foreach (FlatStyle flatStyle in new FlatStyle[] { FlatStyle.Flat, FlatStyle.Popup, FlatStyle.Standard })
        {
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, Size.Empty, new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-1, -2), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-2, -1), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-1, -1), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(1, 2), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(2, 1), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(1, 1), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(10, 20), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(30, 40), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(30, int.MaxValue), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(int.MaxValue, 40), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(int.MaxValue, int.MaxValue), new Size(75, 23) };
        }
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_Flat_TestData))]
    public void Button_GetPreferredSize_InvokeFlat_ReturnsExpected(FlatStyle flatStyle, AutoSizeMode autoSizeMode, Size proposedSize, Size expected)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle,
            AutoSizeMode = autoSizeMode
        };
        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> GetPreferredSize_FlatWithPadding_TestData()
    {
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, Size.Empty, new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(12, 14) };

        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, Size.Empty, new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(10, 12) };
        yield return new object[] { FlatStyle.Popup, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(10, 12) };

        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, Size.Empty, new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(10, 12) };
        yield return new object[] { FlatStyle.Standard, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(10, 12) };

        foreach (FlatStyle flatStyle in new FlatStyle[] { FlatStyle.Flat, FlatStyle.Popup, FlatStyle.Standard })
        {
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, Size.Empty, new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-1, -2), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-2, -1), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-1, -1), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(1, 2), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(2, 1), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(1, 1), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(10, 20), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(30, 40), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(30, int.MaxValue), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(int.MaxValue, 40), new Size(75, 23) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(int.MaxValue, int.MaxValue), new Size(75, 23) };
        }
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_FlatWithPadding_TestData))]
    public void Button_GetPreferredSize_InvokeFlatWithPadding_ReturnsExpected(FlatStyle flatStyle, AutoSizeMode autoSizeMode, Size proposedSize, Size expected)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle,
            AutoSizeMode = autoSizeMode,
            Padding = new Padding(1, 2, 3, 4)
        };
        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> GetPreferredSize_FlatWithPaddingSmallSize_TestData()
    {
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, Size.Empty, new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(12, 14) };

        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, Size.Empty, new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(-1, -2), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(-2, -1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(-1, -1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(1, 2), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(2, 1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(1, 1), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(10, 20), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(30, 40), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(30, int.MaxValue), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(int.MaxValue, 40), new Size(12, 14) };
        yield return new object[] { FlatStyle.Flat, AutoSizeMode.GrowOnly, new Size(int.MaxValue, int.MaxValue), new Size(12, 14) };

        foreach (FlatStyle flatStyle in new FlatStyle[] { FlatStyle.Popup, FlatStyle.Standard })
        {
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, Size.Empty, new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(-1, -2), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(-2, -1), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(-1, -1), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(1, 2), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(2, 1), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(1, 1), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(10, 20), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(30, 40), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(30, int.MaxValue), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, 40), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowAndShrink, new Size(int.MaxValue, int.MaxValue), new Size(10, 12) };

            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, Size.Empty, new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-1, -2), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-2, -1), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(-1, -1), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(1, 2), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(2, 1), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(1, 1), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(10, 20), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(30, 40), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(30, int.MaxValue), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(int.MaxValue, 40), new Size(10, 12) };
            yield return new object[] { flatStyle, AutoSizeMode.GrowOnly, new Size(int.MaxValue, int.MaxValue), new Size(10, 12) };
        }
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_FlatWithPaddingSmallSize_TestData))]
    public void Button_GetPreferredSize_InvokeFlatWithPaddingSmallSize_ReturnsExpected(FlatStyle flatStyle, AutoSizeMode autoSizeMode, Size proposedSize, Size expected)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle,
            AutoSizeMode = autoSizeMode,
            Size = new Size(6, 6),
            Padding = new Padding(1, 2, 3, 4)
        };
        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(expected, size);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> GetPreferredSize_FlatWithText_TestData()
    {
        yield return new object[] { Size.Empty };
        yield return new object[] { new Size(-1, -2) };
        yield return new object[] { new Size(-2, -1) };
        yield return new object[] { new Size(-1, -1) };
        yield return new object[] { new Size(1, 2) };
        yield return new object[] { new Size(2, 1) };
        yield return new object[] { new Size(1, 1) };
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_FlatWithText_TestData))]
    public void Button_GetPreferredSize_InvokeFlatGrowOnlyWithText_ReturnsExpected(Size proposedSize)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.Flat,
            AutoSizeMode = AutoSizeMode.GrowOnly,
            Padding = new Padding(1, 2, 3, 4),
            Text = "Text"
        };
        Size minSize = TextRenderer.MeasureText(control.Text, control.Font);

        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(new Size(75, minSize.Height + 13 + 8), size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(new Size(75, minSize.Height + 13 + 8), size);
        Assert.False(control.IsHandleCreated);
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_FlatWithText_TestData))]
    public void Button_GetPreferredSize_InvokeFlatGrowAndShrinkWithText_ReturnsExpected(Size proposedSize)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.Flat,
            AutoSizeMode = AutoSizeMode.GrowAndShrink,
            Padding = new Padding(1, 2, 3, 4),
            Text = "Text"
        };
        Size minSize = TextRenderer.MeasureText(control.Text, control.Font);

        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(minSize + new Size(11, 15) + new Size(4, 6), size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(minSize + new Size(11, 15) + new Size(4, 6), size);
        Assert.False(control.IsHandleCreated);
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_FlatWithText_TestData))]
    public void Button_GetPreferredSize_InvokePopupGrowOnlyWithText_ReturnsExpected(Size proposedSize)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.Popup,
            AutoSizeMode = AutoSizeMode.GrowOnly,
            Padding = new Padding(1, 2, 3, 4),
            Text = "Text"
        };
        Size minSize = TextRenderer.MeasureText(control.Text, control.Font);

        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(new Size(75, minSize.Height + 13 + 6), size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(new Size(75, minSize.Height + 13 + 6), size);
        Assert.False(control.IsHandleCreated);
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_FlatWithText_TestData))]
    public void Button_GetPreferredSize_InvokePopupGrowAndShrinkWithText_ReturnsExpected(Size proposedSize)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.Popup,
            AutoSizeMode = AutoSizeMode.GrowAndShrink,
            Padding = new Padding(1, 2, 3, 4),
            Text = "Text"
        };
        Size minSize = TextRenderer.MeasureText(control.Text, control.Font);

        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(minSize + new Size(9, 13) + new Size(4, 6), size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(minSize + new Size(9, 13) + new Size(4, 6), size);
        Assert.False(control.IsHandleCreated);
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_FlatWithText_TestData))]
    public void Button_GetPreferredSize_InvokeStandardGrowOnlyWithText_ReturnsExpected(Size proposedSize)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.Standard,
            AutoSizeMode = AutoSizeMode.GrowOnly,
            Padding = new Padding(1, 2, 3, 4),
            Text = "Text"
        };
        Size minSize = TextRenderer.MeasureText(control.Text, control.Font);

        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(new Size(75, minSize.Height + 13 + 6), size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(new Size(75, minSize.Height + 13 + 6), size);
        Assert.False(control.IsHandleCreated);
    }

    [ActiveIssue("https://github.com/dotnet/winforms/issues/3647")]
    [WinFormsTheory(Skip = "Flaky tests, see: https://github.com/dotnet/winforms/issues/3647")]
    [MemberData(nameof(GetPreferredSize_FlatWithText_TestData))]
    public void Button_GetPreferredSize_InvokeStandardGrowAndShrinkWithText_ReturnsExpected(Size proposedSize)
    {
        using SubButton control = new()
        {
            FlatStyle = FlatStyle.Standard,
            AutoSizeMode = AutoSizeMode.GrowAndShrink,
            Padding = new Padding(1, 2, 3, 4),
            Text = "Text"
        };
        Size minSize = TextRenderer.MeasureText(control.Text, control.Font);

        Size size = control.GetPreferredSize(proposedSize);
        Assert.Equal(minSize + new Size(9, 13) + new Size(4, 6), size);
        Assert.False(control.IsHandleCreated);

        // Call again.
        size = control.GetPreferredSize(proposedSize);
        Assert.Equal(minSize + new Size(9, 13) + new Size(4, 6), size);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(ControlStyles.ContainerControl, false)]
    [InlineData(ControlStyles.UserPaint, true)]
    [InlineData(ControlStyles.Opaque, true)]
    [InlineData(ControlStyles.ResizeRedraw, true)]
    [InlineData(ControlStyles.FixedWidth, false)]
    [InlineData(ControlStyles.FixedHeight, false)]
    [InlineData(ControlStyles.StandardClick, false)]
    [InlineData(ControlStyles.Selectable, true)]
    [InlineData(ControlStyles.UserMouse, true)]
    [InlineData(ControlStyles.SupportsTransparentBackColor, true)]
    [InlineData(ControlStyles.StandardDoubleClick, false)]
    [InlineData(ControlStyles.AllPaintingInWmPaint, true)]
    [InlineData(ControlStyles.CacheText, true)]
    [InlineData(ControlStyles.EnableNotifyMessage, false)]
    [InlineData(ControlStyles.DoubleBuffer, false)]
    [InlineData(ControlStyles.OptimizedDoubleBuffer, true)]
    [InlineData(ControlStyles.UseTextForAccessibility, true)]
    [InlineData((ControlStyles)0, true)]
    [InlineData((ControlStyles)int.MaxValue, false)]
    [InlineData((ControlStyles)(-1), false)]
    public void Button_GetStyle_Invoke_ReturnsExpected(ControlStyles flag, bool expected)
    {
        using SubButton control = new();
        Assert.Equal(expected, control.GetStyle(flag));

        // Call again to test caching.
        Assert.Equal(expected, control.GetStyle(flag));
    }

    [WinFormsFact]
    public void Button_GetTopLevel_Invoke_ReturnsExpected()
    {
        using SubButton control = new();
        Assert.False(control.GetTopLevel());
    }

    public static IEnumerable<object[]> NotifyDefault_TestData()
    {
        foreach (FlatStyle flatStyle in Enum.GetValues(typeof(FlatStyle)))
        {
            yield return new object[] { flatStyle, true };
            yield return new object[] { flatStyle, false };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(NotifyDefault_TestData))]
    public void Button_NotifyDefault_Invoke_GetReturnsExpected(FlatStyle flatStyle, bool value)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };

        control.NotifyDefault(value);
        Assert.Equal(value, control.IsDefault);
        Assert.False(control.IsHandleCreated);

        // Set same.
        control.NotifyDefault(value);
        Assert.Equal(value, control.IsDefault);
        Assert.False(control.IsHandleCreated);

        // Set different.
        control.NotifyDefault(!value);
        Assert.Equal(!value, control.IsDefault);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> NotifyDefault_WithHandle_TestData()
    {
        yield return new object[] { FlatStyle.Flat, true, 1, 0, 2, 0 };
        yield return new object[] { FlatStyle.Popup, true, 1, 0, 2, 0 };
        yield return new object[] { FlatStyle.Standard, true, 1, 0, 2, 0 };
        yield return new object[] { FlatStyle.System, true, 1, 1, 2, 2 };

        yield return new object[] { FlatStyle.Flat, false, 0, 0, 1, 0 };
        yield return new object[] { FlatStyle.Popup, false, 0, 0, 1, 0 };
        yield return new object[] { FlatStyle.Standard, false, 0, 0, 1, 0 };
        yield return new object[] { FlatStyle.System, false, 0, 0, 1, 1 };
    }

    [WinFormsTheory]
    [MemberData(nameof(NotifyDefault_WithHandle_TestData))]
    public void Button_NotifyDefault_InvokeWithHandle_GetReturnsExpected(FlatStyle flatStyle, bool value, int expectedInvalidatedCallCount1, int expectedStyleChangeCallCount1, int expectedInvalidatedCallCount2, int expectedStyleChangeCallCount2)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        control.NotifyDefault(value);
        Assert.Equal(value, control.IsDefault);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount1, invalidatedCallCount);
        Assert.Equal(expectedStyleChangeCallCount1, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Set same.
        control.NotifyDefault(value);
        Assert.Equal(value, control.IsDefault);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount1, invalidatedCallCount);
        Assert.Equal(expectedStyleChangeCallCount1, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Set different.
        control.NotifyDefault(!value);
        Assert.Equal(!value, control.IsDefault);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount2, invalidatedCallCount);
        Assert.Equal(expectedStyleChangeCallCount2, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsTheory]
    [InlineData(1, 2)]
    [InlineData(0, 0)]
    [InlineData(-1, -2)]
    public void Button_RescaleConstantsForDpi_Invoke_Nop(int deviceDpiOld, int deviceDpiNew)
    {
        using SubButton control = new();
        control.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew);
        Assert.False(control.IsHandleCreated);

        // Call again.
        control.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnClick_Invoke_CallsClick(EventArgs eventArgs)
    {
        using SubButton control = new();
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.Click += handler;
        control.OnClick(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.Click -= handler;
        control.OnClick(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnClick_InvokeWithForm_CallsClick(EventArgs eventArgs)
    {
        using Form form = new();
        using Control parent = new()
        {
            Parent = form
        };
        using SubButton control = new()
        {
            Parent = parent,
            DialogResult = DialogResult.Yes
        };
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.Click += handler;
        control.OnClick(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(DialogResult.Yes, form.DialogResult);

        Assert.False(control.IsHandleCreated);
        Assert.False(parent.IsHandleCreated);
        Assert.False(form.IsHandleCreated);

        // Remove handler.
        control.Click -= handler;
        control.OnClick(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(DialogResult.Yes, form.DialogResult);

        Assert.False(control.IsHandleCreated);
        Assert.False(parent.IsHandleCreated);
        Assert.False(form.IsHandleCreated);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnClick_InvokeWithHandle_CallsClick(EventArgs eventArgs)
    {
        using SubButton control = new();
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.Click += handler;
        control.OnClick(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.Click -= handler;
        control.OnClick(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnClick_InvokeWithFormWithHandle_CallsClick(EventArgs eventArgs)
    {
        using Form form = new();
        using Control parent = new()
        {
            Parent = form
        };
        using SubButton control = new()
        {
            Parent = parent,
            DialogResult = DialogResult.Yes
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.Click += handler;
        control.OnClick(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(DialogResult.Yes, form.DialogResult);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
        Assert.False(parent.IsHandleCreated);
        Assert.False(form.IsHandleCreated);

        // Remove handler.
        control.Click -= handler;
        control.OnClick(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(DialogResult.Yes, form.DialogResult);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
        Assert.False(parent.IsHandleCreated);
        Assert.False(form.IsHandleCreated);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnDoubleClick_Invoke_CallsDoubleClick(EventArgs eventArgs)
    {
        using SubButton control = new();
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.DoubleClick += handler;
        control.OnDoubleClick(eventArgs);
        Assert.Equal(1, callCount);

        // Remove handler.
        control.DoubleClick -= handler;
        control.OnDoubleClick(eventArgs);
        Assert.Equal(1, callCount);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnFontChanged_Invoke_CallsFontChanged(EventArgs eventArgs)
    {
        using SubButton control = new();
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.FontChanged += handler;
        control.OnFontChanged(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.FontChanged -= handler;
        control.OnFontChanged(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnHandleCreated_Invoke_CallsHandleCreated(EventArgs eventArgs)
    {
        using SubButton control = new();
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.HandleCreated += handler;
        control.OnHandleCreated(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.HandleCreated -= handler;
        control.OnHandleCreated(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnHandleCreated_InvokeWithHandle_CallsHandleCreated(EventArgs eventArgs)
    {
        using SubButton control = new();
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.HandleCreated += handler;
        control.OnHandleCreated(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);

        // Remove handler.
        control.HandleCreated -= handler;
        control.OnHandleCreated(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnHandleDestroyed_Invoke_CallsHandleDestroyed(EventArgs eventArgs)
    {
        using SubButton control = new();
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.HandleDestroyed += handler;
        control.OnHandleDestroyed(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.HandleDestroyed -= handler;
        control.OnHandleDestroyed(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [NewAndDefaultData<EventArgs>]
    public void Button_OnHandleDestroyed_InvokeWithHandle_CallsHandleDestroyed(EventArgs eventArgs)
    {
        using SubButton control = new();
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.HandleDestroyed += handler;
        control.OnHandleDestroyed(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);

        // Remove handler.
        control.HandleDestroyed -= handler;
        control.OnHandleDestroyed(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [CommonMemberData(typeof(CommonTestHelperEx), nameof(CommonTestHelperEx.GetMouseEventArgsTheoryData))]
    public void Button_OnMouseDoubleClick_Invoke_CallsMouseDoubleClick(MouseEventArgs eventArgs)
    {
        using SubButton control = new();
        int callCount = 0;
        MouseEventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseDoubleClick += handler;
        control.OnMouseDoubleClick(eventArgs);
        Assert.Equal(1, callCount);

        // Remove handler.
        control.MouseDoubleClick -= handler;
        control.OnMouseDoubleClick(eventArgs);
        Assert.Equal(1, callCount);
    }

    public static IEnumerable<object[]> OnMouseDown_TestData()
    {
        foreach (bool enabled in new bool[] { true, false })
        {
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.None, 0, 0, 0, 0) };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0) };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Right, 0, 0, 0, 0) };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0) };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Right, 1, 0, 0, 0) };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Left, 2, 0, 0, 0) };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Right, 2, 0, 0, 0) };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Left, 3, 0, 0, 0) };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Right, 3, 0, 0, 0) };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseDown_TestData))]
    public void Button_OnMouseDown_Invoke_CallsMouseDown(bool enabled, MouseEventArgs eventArgs)
    {
        using SubButton control = new()
        {
            Enabled = enabled
        };
        int callCount = 0;
        MouseEventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseDown += handler;
        control.OnMouseDown(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.MouseDown -= handler;
        control.OnMouseDown(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> OnMouseDown_WithHandle_TestData()
    {
        foreach (bool enabled in new bool[] { true, false })
        {
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.None, 0, 0, 0, 0), 0 };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0), 1 };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Right, 0, 0, 0, 0), 0 };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0), 1 };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Right, 1, 0, 0, 0), 0 };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Left, 2, 0, 0, 0), 1 };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Right, 2, 0, 0, 0), 0 };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Left, 3, 0, 0, 0), 1 };
            yield return new object[] { enabled, new MouseEventArgs(MouseButtons.Right, 3, 0, 0, 0), 0 };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseDown_WithHandle_TestData))]
    public void Button_OnMouseDown_InvokeWithHandle_CallsMouseDown(bool enabled, MouseEventArgs eventArgs, int expectedInvalidatedCallCount)
    {
        using SubButton control = new()
        {
            Enabled = enabled
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        MouseEventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseDown += handler;
        control.OnMouseDown(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.MouseDown -= handler;
        control.OnMouseDown(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount * 2, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsFact]
    public void Button_OnMouseDown_NullE_ReturnsExpected()
    {
        using SubButton control = new();
        Assert.Throws<NullReferenceException>(() => control.OnMouseDown(null));
    }

    public static IEnumerable<object[]> OnMouseEnter_TestData()
    {
        foreach (bool enabled in new bool[] { true, false })
        {
            foreach (bool autoEllipsis in new bool[] { true, false })
            {
                foreach (string text in new string[] { null, string.Empty, "text" })
                {
                    yield return new object[] { enabled, autoEllipsis, text, null };
                    yield return new object[] { enabled, autoEllipsis, text, new EventArgs() };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseEnter_TestData))]
    public void Button_OnMouseEnter_Invoke_CallsMouseEnter(bool enabled, bool autoEllipsis, string text, EventArgs eventArgs)
    {
        using SubButton control = new()
        {
            Enabled = enabled,
            AutoEllipsis = autoEllipsis,
            Text = text
        };
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseEnter += handler;
        control.OnMouseEnter(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.MouseEnter -= handler;
        control.OnMouseEnter(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseEnter_TestData))]
    public void Button_OnMouseEnter_InvokeDesignMode_CallsMouseEnter(bool enabled, bool autoEllipsis, string text, EventArgs eventArgs)
    {
        Mock<ISite> mockSite = new(MockBehavior.Strict);
        mockSite
            .Setup(s => s.GetService(typeof(AmbientProperties)))
            .Returns(null);
        mockSite
            .Setup(s => s.DesignMode)
            .Returns(true);
        mockSite
            .Setup(s => s.Name)
            .Returns((string)null);
        mockSite
            .Setup(s => s.Container)
            .Returns((IContainer)null);
        using SubButton control = new()
        {
            Enabled = enabled,
            AutoEllipsis = autoEllipsis,
            Text = text,
            Site = mockSite.Object
        };
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseEnter += handler;
        control.OnMouseEnter(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.MouseEnter -= handler;
        control.OnMouseEnter(eventArgs);
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseEnter_TestData))]
    public void Button_OnMouseEnter_InvokeWithHandle_CallsMouseEnter(bool enabled, bool autoEllipsis, string text, EventArgs eventArgs)
    {
        using SubButton control = new()
        {
            Enabled = enabled,
            AutoEllipsis = autoEllipsis,
            Text = text
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseEnter += handler;
        control.OnMouseEnter(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(1, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.MouseEnter -= handler;
        control.OnMouseEnter(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(2, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseEnter_TestData))]
    public void Button_OnMouseEnter_InvokeDesignModeWithHandle_CallsMouseEnter(bool enabled, bool autoEllipsis, string text, EventArgs eventArgs)
    {
        Mock<ISite> mockSite = new(MockBehavior.Strict);
        mockSite
            .Setup(s => s.GetService(typeof(AmbientProperties)))
            .Returns(null);
        mockSite
            .Setup(s => s.DesignMode)
            .Returns(true);
        mockSite
            .Setup(s => s.Name)
            .Returns((string)null);
        mockSite
            .Setup(s => s.Container)
            .Returns((IContainer)null);
        using SubButton control = new()
        {
            Enabled = enabled,
            AutoEllipsis = autoEllipsis,
            Text = text,
            Site = mockSite.Object
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseEnter += handler;
        control.OnMouseEnter(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(1, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.MouseEnter -= handler;
        control.OnMouseEnter(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(2, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    public static IEnumerable<object[]> OnMouseLeave_TestData()
    {
        foreach (bool enabled in new bool[] { true, false })
        {
            foreach (bool autoEllipsis in new bool[] { true, false })
            {
                foreach (string text in new string[] { null, string.Empty, "text" })
                {
                    yield return new object[] { enabled, autoEllipsis, text, null };
                    yield return new object[] { enabled, autoEllipsis, text, new EventArgs() };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseLeave_TestData))]
    public void Button_OnMouseLeave_Invoke_CallsMouseLeave(bool enabled, bool autoEllipsis, string text, EventArgs eventArgs)
    {
        using SubButton control = new()
        {
            Enabled = enabled,
            AutoEllipsis = autoEllipsis,
            Text = text
        };
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseLeave += handler;
        control.OnMouseLeave(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(autoEllipsis, control.IsHandleCreated);

        // Remove handler.
        control.MouseLeave -= handler;
        control.OnMouseLeave(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(autoEllipsis, control.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseLeave_TestData))]
    public void Button_OnMouseLeave_InvokeWithHandle_CallsMouseLeave(bool enabled, bool autoEllipsis, string text, EventArgs eventArgs)
    {
        using SubButton control = new()
        {
            Enabled = enabled,
            AutoEllipsis = autoEllipsis,
            Text = text
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.MouseLeave += handler;
        control.OnMouseLeave(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(1, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.MouseLeave -= handler;
        control.OnMouseLeave(eventArgs);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(2, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    public static IEnumerable<object[]> OnMouseUp_TestData()
    {
        foreach (FlatStyle flatStyle in Enum.GetValues(typeof(FlatStyle)))
        {
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.None, 1, 2, 3, 4) };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Left, 1, 2, 3, 4) };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Middle, 1, 2, 3, 4) };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Right, 1, 2, 3, 4) };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.None, 1, 2, 3, 4) };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Left, 1, 2, 3, 4) };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Middle, 1, 2, 3, 4) };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Right, 1, 2, 3, 4) };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseUp_TestData))]
    public void Button_OnMouseUp_Invoke_CallsMouseUp(FlatStyle flatStyle, MouseEventArgs eventArgs)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };
        int callCount = 0;
        MouseEventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };
        int clickCallCount = 0;
        control.Click += (sender, e) => clickCallCount++;
        int mouseClickCallCount = 0;
        control.MouseClick += (sender, e) => mouseClickCallCount++;

        // Call with handler.
        control.MouseUp += handler;
        control.OnMouseUp(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, clickCallCount);
        Assert.Equal(0, mouseClickCallCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.MouseUp -= handler;
        control.OnMouseUp(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, clickCallCount);
        Assert.Equal(0, mouseClickCallCount);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> OnMouseUp_MouseDown_TestData()
    {
        foreach (FlatStyle flatStyle in Enum.GetValues(typeof(FlatStyle)))
        {
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.None, 1, 2, 3, 4), false };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Left, 1, 2, 3, 4), true };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Middle, 1, 2, 3, 4), false };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Right, 1, 2, 3, 4), false };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.None, 1, 2, 3, 4), false };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Left, 1, 2, 3, 4), true };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Middle, 1, 2, 3, 4), false };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Right, 1, 2, 3, 4), false };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseUp_MouseDown_TestData))]
    public void Button_OnMouseUp_InvokeMouseDown_CallsMouseUp(FlatStyle flatStyle, MouseEventArgs eventArgs, bool expectedIsHandleCreated)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };
        control.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
        int callCount = 0;
        MouseEventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };
        int clickCallCount = 0;
        control.Click += (sender, e) => clickCallCount++;
        int mouseClickCallCount = 0;
        control.MouseClick += (sender, e) => mouseClickCallCount++;

        // Call with handler.
        control.MouseUp += handler;
        control.OnMouseUp(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, clickCallCount);
        Assert.Equal(0, mouseClickCallCount);
        Assert.Equal(expectedIsHandleCreated, control.IsHandleCreated);

        // Remove handler.
        control.MouseUp -= handler;
        control.OnMouseUp(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, clickCallCount);
        Assert.Equal(0, mouseClickCallCount);
        Assert.Equal(expectedIsHandleCreated, control.IsHandleCreated);
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseUp_TestData))]
    public void Button_OnMouseUp_InvokeWithHandle_CallsMouseUp(FlatStyle flatStyle, MouseEventArgs eventArgs)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        MouseEventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };
        int clickCallCount = 0;
        control.Click += (sender, e) => clickCallCount++;
        int mouseClickCallCount = 0;
        control.MouseClick += (sender, e) => mouseClickCallCount++;

        // Call with handler.
        control.MouseUp += handler;
        control.OnMouseUp(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, clickCallCount);
        Assert.Equal(0, mouseClickCallCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.MouseUp -= handler;
        control.OnMouseUp(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, clickCallCount);
        Assert.Equal(0, mouseClickCallCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    public static IEnumerable<object[]> OnMouseUp_WithHandle_TestData()
    {
        foreach (FlatStyle flatStyle in Enum.GetValues(typeof(FlatStyle)))
        {
            int expectedCallCount = flatStyle != FlatStyle.System ? 1 : 0;
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.None, 1, 2, 3, 4), 0 };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Left, 1, 2, 3, 4), expectedCallCount };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Middle, 1, 2, 3, 4), 0 };
            yield return new object[] { flatStyle, new MouseEventArgs(MouseButtons.Right, 1, 2, 3, 4), 0 };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.None, 1, 2, 3, 4), 0 };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Left, 1, 2, 3, 4), expectedCallCount };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Middle, 1, 2, 3, 4), 0 };
            yield return new object[] { flatStyle, new HandledMouseEventArgs(MouseButtons.Right, 1, 2, 3, 4), 0 };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnMouseUp_WithHandle_TestData))]
    public void Button_OnMouseUp_InvokeMouseDownWithHandle_CallsMouseUp(FlatStyle flatStyle, MouseEventArgs eventArgs, int expectedInvalidatedCallCount)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };
        control.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        MouseEventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };
        int clickCallCount = 0;
        control.Click += (sender, e) => clickCallCount++;
        int mouseClickCallCount = 0;
        control.MouseClick += (sender, e) => mouseClickCallCount++;

        // Call with handler.
        control.MouseUp += handler;
        control.OnMouseUp(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, clickCallCount);
        Assert.Equal(0, mouseClickCallCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.MouseUp -= handler;
        control.OnMouseUp(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, clickCallCount);
        Assert.Equal(0, mouseClickCallCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(expectedInvalidatedCallCount, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsFact]
    public void Button_OnMouseUp_NullE_ThrowsNullReferenceException()
    {
        using SubButton control = new();
        Assert.Throws<NullReferenceException>(() => control.OnMouseUp(null));
    }

    public static IEnumerable<object[]> OnTextChanged_TestData()
    {
        foreach (bool autoSize in new bool[] { true, false })
        {
            yield return new object[] { autoSize, null };
            yield return new object[] { autoSize, new EventArgs() };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnTextChanged_TestData))]
    public void Button_OnTextChanged_Invoke_CallsTextChanged(bool autoSize, EventArgs eventArgs)
    {
        using SubButton control = new()
        {
            AutoSize = autoSize
        };
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.TextChanged += handler;
        control.OnTextChanged(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, layoutCallCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.TextChanged -= handler;
        control.OnTextChanged(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, layoutCallCount);
        Assert.False(control.IsHandleCreated);
    }

    public static IEnumerable<object[]> OnTextChanged_WithParent_TestData()
    {
        yield return new object[] { true, null, 1 };
        yield return new object[] { true, new EventArgs(), 1 };
        yield return new object[] { false, null, 0 };
        yield return new object[] { false, new EventArgs(), 0 };
    }

    [WinFormsTheory]
    [MemberData(nameof(OnTextChanged_WithParent_TestData))]
    public void Button_OnTextChanged_InvokeWithParent_CallsTextChanged(bool autoSize, EventArgs eventArgs, int expectedParentLayoutCallCount)
    {
        using Control parent = new();
        using SubButton control = new()
        {
            AutoSize = autoSize,
            Parent = parent
        };
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(parent, sender);
            Assert.Same(control, e.AffectedControl);
            Assert.Equal("Text", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        parent.Layout += parentHandler;

        try
        {
            int callCount = 0;
            EventHandler handler = (sender, e) =>
            {
                Assert.Same(control, sender);
                Assert.Same(eventArgs, e);
                callCount++;
            };

            // Call with handler.
            control.TextChanged += handler;
            control.OnTextChanged(eventArgs);
            Assert.Equal(1, callCount);
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(expectedParentLayoutCallCount, parentLayoutCallCount);
            Assert.False(control.IsHandleCreated);
            Assert.False(parent.IsHandleCreated);

            // Remove handler.
            control.TextChanged -= handler;
            control.OnTextChanged(eventArgs);
            Assert.Equal(1, callCount);
            Assert.Equal(expectedParentLayoutCallCount * 2, parentLayoutCallCount);
            Assert.False(control.IsHandleCreated);
            Assert.False(parent.IsHandleCreated);
        }
        finally
        {
            parent.Layout -= parentHandler;
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(OnTextChanged_TestData))]
    public void Button_OnTextChanged_InvokeWithHandle_CallsTextChanged(bool autoSize, EventArgs eventArgs)
    {
        using SubButton control = new()
        {
            AutoSize = autoSize
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(eventArgs, e);
            callCount++;
        };

        // Call with handler.
        control.TextChanged += handler;
        control.OnTextChanged(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, layoutCallCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(1, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.TextChanged -= handler;
        control.OnTextChanged(eventArgs);
        Assert.Equal(1, callCount);
        Assert.Equal(0, layoutCallCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(2, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsTheory]
    [MemberData(nameof(OnTextChanged_WithParent_TestData))]
    public void Button_OnTextChanged_InvokeWithParentWithHandle_CallsTextChanged(bool autoSize, EventArgs eventArgs, int expectedParentLayoutCallCount)
    {
        using Control parent = new();
        using SubButton control = new()
        {
            AutoSize = autoSize,
            Parent = parent
        };
        Assert.NotEqual(IntPtr.Zero, parent.Handle);
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int layoutCallCount = 0;
        control.Layout += (sender, e) => layoutCallCount++;
        int parentLayoutCallCount = 0;
        void parentHandler(object sender, LayoutEventArgs e)
        {
            Assert.Same(parent, sender);
            Assert.Same(control, e.AffectedControl);
            Assert.Equal("Text", e.AffectedProperty);
            parentLayoutCallCount++;
        }

        parent.Layout += parentHandler;

        try
        {
            int callCount = 0;
            EventHandler handler = (sender, e) =>
            {
                Assert.Same(control, sender);
                Assert.Same(eventArgs, e);
                callCount++;
            };

            // Call with handler.
            control.TextChanged += handler;
            control.OnTextChanged(eventArgs);
            Assert.Equal(1, callCount);
            Assert.Equal(0, layoutCallCount);
            Assert.Equal(expectedParentLayoutCallCount, parentLayoutCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(1, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(parent.IsHandleCreated);

            // Remove handler.
            control.TextChanged -= handler;
            control.OnTextChanged(eventArgs);
            Assert.Equal(1, callCount);
            Assert.Equal(expectedParentLayoutCallCount * 2, parentLayoutCallCount);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(2, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.True(parent.IsHandleCreated);
        }
        finally
        {
            parent.Layout -= parentHandler;
        }
    }

    [WinFormsFact]
    public void Button_PerformClick_Invoke_CallsClick()
    {
        using SubButton control = new();
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };

        // Call with handler.
        control.Click += handler;
        control.PerformClick();
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);

        // Remove handler.
        control.Click -= handler;
        control.PerformClick();
        Assert.Equal(1, callCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsFact]
    public void Button_PerformClick_InvokeWithForm_CallsClick()
    {
        using Form form = new();
        using Control parent = new()
        {
            Parent = form
        };
        using SubButton control = new()
        {
            Parent = parent,
            DialogResult = DialogResult.Yes
        };
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };

        // Call with handler.
        control.Click += handler;
        control.PerformClick();
        Assert.Equal(0, callCount);
        Assert.Equal(DialogResult.None, form.DialogResult);
        Assert.False(control.IsHandleCreated);
        Assert.False(parent.IsHandleCreated);
        Assert.False(form.IsHandleCreated);

        // Remove handler.
        control.Click -= handler;
        control.PerformClick();
        Assert.Equal(0, callCount);
        Assert.Equal(DialogResult.None, form.DialogResult);
        Assert.False(control.IsHandleCreated);
        Assert.False(parent.IsHandleCreated);
        Assert.False(form.IsHandleCreated);
    }

    [WinFormsFact]
    public void Button_PerformClick_InvokeWithHandle_CallsClick()
    {
        using SubButton control = new();
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };

        // Call with handler.
        control.Click += handler;
        control.PerformClick();
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(1, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);

        // Remove handler.
        control.Click -= handler;
        control.PerformClick();
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(2, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsFact]
    public void Button_PerformClick_InvokeWithFormWithHandle_CallsClick()
    {
        using Form form = new();
        using Control parent = new()
        {
            Parent = form
        };
        using SubButton control = new()
        {
            Parent = parent,
            DialogResult = DialogResult.Yes
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        EventHandler handler = (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };

        // Call with handler.
        control.Click += handler;
        control.PerformClick();
        Assert.Equal(0, callCount);
        Assert.Equal(DialogResult.None, form.DialogResult);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
        Assert.False(parent.IsHandleCreated);
        Assert.False(form.IsHandleCreated);

        // Remove handler.
        control.Click -= handler;
        control.PerformClick();
        Assert.Equal(0, callCount);
        Assert.Equal(DialogResult.None, form.DialogResult);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
        Assert.False(parent.IsHandleCreated);
        Assert.False(form.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(true, "", 'a', 0)]
    [InlineData(true, "", char.MinValue, 0)]
    [InlineData(true, "&a", 'a', 1)]
    [InlineData(true, "&a", 'b', 0)]
    [InlineData(true, "&&a", 'a', 0)]
    [InlineData(true, "&", 'a', 0)]
    [InlineData(true, "text", 'a', 0)]
    [InlineData(false, "", 'a', 0)]
    [InlineData(false, "", char.MinValue, 0)]
    [InlineData(false, "&a", 'a', 0)]
    [InlineData(false, "&a", 'b', 0)]
    [InlineData(false, "&&a", 'a', 0)]
    [InlineData(false, "&", 'a', 0)]
    [InlineData(false, "text", 'a', 0)]
    public void Button_ProcessMnemonic_Invoke_ReturnsExpected(bool useMnemonic, string text, char charCode, int expectedClickCallCount)
    {
        using SubButton control = new()
        {
            UseMnemonic = useMnemonic,
            Text = text
        };
        int clickCallCount = 0;
        control.Click += (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            clickCallCount++;
        };
        Assert.Equal(expectedClickCallCount != 0, control.ProcessMnemonic(charCode));
        Assert.Equal(expectedClickCallCount, clickCallCount);

        Assert.False(control.IsHandleCreated);
    }

    [WinFormsTheory]
    [InlineData(true, "", 'a')]
    [InlineData(true, "", char.MinValue)]
    [InlineData(true, "&a", 'a')]
    [InlineData(true, "&a", 'b')]
    [InlineData(true, "&&a", 'a')]
    [InlineData(true, "&", 'a')]
    [InlineData(true, "text", 'a')]
    [InlineData(false, "", 'a')]
    [InlineData(false, "", char.MinValue)]
    [InlineData(false, "&a", 'a')]
    [InlineData(false, "&a", 'b')]
    [InlineData(false, "&&a", 'a')]
    [InlineData(false, "&", 'a')]
    [InlineData(false, "text", 'a')]
    public void Button_ProcessMnemonic_InvokeCantProcessMnemonic_ReturnsFalse(bool useMnemonic, string text, char charCode)
    {
        using SubButton control = new()
        {
            UseMnemonic = useMnemonic,
            Text = text,
            Enabled = false
        };
        int clickCallCount = 0;
        control.Click += (sender, e) => clickCallCount++;
        Assert.False(control.ProcessMnemonic(charCode));
        Assert.Equal(0, clickCallCount);
        Assert.False(control.IsHandleCreated);
    }

    [WinFormsFact]
    public void Button_ToString_Invoke_ReturnsExpected()
    {
        using Button control = new();
        Assert.Equal("System.Windows.Forms.Button, Text: ", control.ToString());
    }

    [WinFormsFact]
    public void Button_RaiseAutomationEvent_Invoke_Success_IfControlIsCreated()
    {
        using TestButton button = new();
        Assert.False(button.IsHandleCreated);
        button.CreateControl();
        var accessibleObject = (SubButtonAccessibleObject)button.AccessibilityObject;
        Assert.Equal(0, accessibleObject.RaiseAutomationEventCallsCount);
        Assert.Equal(0, accessibleObject.RaiseAutomationPropertyChangedEventCallsCount);

        button.PerformClick();

        Assert.Equal(1, accessibleObject.RaiseAutomationEventCallsCount);
        Assert.Equal(1, accessibleObject.RaiseAutomationPropertyChangedEventCallsCount);
        Assert.True(button.IsHandleCreated);
    }

    [WinFormsFact]
    public void Button_RaiseAutomationEvent_IsNotInvoked_IfControlIsNotCreated()
    {
        using TestButton button = new();
        var accessibleObject = (SubButtonAccessibleObject)button.AccessibilityObject;
        Assert.Equal(0, accessibleObject.RaiseAutomationEventCallsCount);
        Assert.Equal(0, accessibleObject.RaiseAutomationPropertyChangedEventCallsCount);

        button.PerformClick();

        Assert.Equal(0, accessibleObject.RaiseAutomationEventCallsCount);
        Assert.Equal(0, accessibleObject.RaiseAutomationPropertyChangedEventCallsCount);
        Assert.False(button.IsHandleCreated);
    }

    [WinFormsFact]
    public void Button_ToString_InvokeShortText_ReturnsExpected()
    {
        using Button control = new()
        {
            Text = "Text"
        };
        Assert.Equal("System.Windows.Forms.Button, Text: Text", control.ToString());
    }

    public static IEnumerable<object[]> WndProc_EraseBkgndWithoutHandleWithoutWParam_TestData()
    {
        foreach (bool opaque in new bool[] { true, false })
        {
            yield return new object[] { true, true, opaque, IntPtr.Zero };
            yield return new object[] { true, false, opaque, IntPtr.Zero };
            yield return new object[] { false, true, opaque, IntPtr.Zero };
            yield return new object[] { false, false, opaque, IntPtr.Zero };
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(WndProc_EraseBkgndWithoutHandleWithoutWParam_TestData))]
    public void Button_WndProc_InvokeEraseBkgndWithoutHandleWithoutWParam_Success(bool userPaint, bool allPaintingInWmPaint, bool opaque, IntPtr expectedResult)
    {
        using (new NoAssertContext())
        {
            using SubButton control = new();
            control.SetStyle(ControlStyles.UserPaint, userPaint);
            control.SetStyle(ControlStyles.AllPaintingInWmPaint, allPaintingInWmPaint);
            control.SetStyle(ControlStyles.Opaque, opaque);
            int paintCallCount = 0;
            control.Paint += (sender, e) => paintCallCount++;

            Message m = new()
            {
                Msg = (int)PInvokeCore.WM_ERASEBKGND,
                Result = 250
            };
            control.WndProc(ref m);
            Assert.Equal(expectedResult, m.Result);
            Assert.False(control.IsHandleCreated);
            Assert.Equal(0, paintCallCount);
        }
    }

    public static IEnumerable<object[]> WndProc_EraseBkgnd_TestData()
    {
        foreach (bool userPaint in new bool[] { true, false })
        {
            foreach (bool allPaintingInWmPaint in new bool[] { true, false })
            {
                foreach (bool opaque in new bool[] { true, false })
                {
                    yield return new object[] { userPaint, allPaintingInWmPaint, opaque };
                }
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(WndProc_EraseBkgnd_TestData))]
    public void Button_WndProc_InvokeEraseBkgndWithoutHandleWithWParam_Success(bool userPaint, bool allPaintingInWmPaint, bool opaque)
    {
        using (new NoAssertContext())
        {
            using SubButton control = new();
            control.SetStyle(ControlStyles.UserPaint, userPaint);
            control.SetStyle(ControlStyles.AllPaintingInWmPaint, allPaintingInWmPaint);
            control.SetStyle(ControlStyles.Opaque, opaque);
            int paintCallCount = 0;
            control.Paint += (sender, e) => paintCallCount++;

            using Bitmap image = new(10, 10);
            using Graphics graphics = Graphics.FromImage(image);
            IntPtr hdc = graphics.GetHdc();
            try
            {
                Message m = new()
                {
                    Msg = (int)PInvokeCore.WM_ERASEBKGND,
                    WParam = hdc,
                    Result = 250
                };
                control.WndProc(ref m);
                Assert.Equal(IntPtr.Zero, m.Result);
                Assert.False(control.IsHandleCreated);
                Assert.Equal(0, paintCallCount);
            }
            finally
            {
                graphics.ReleaseHdc();
            }
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(WndProc_EraseBkgnd_TestData))]
    public void Button_WndProc_InvokeEraseBkgndWithHandleWithoutWParam_Success(bool userPaint, bool allPaintingInWmPaint, bool opaque)
    {
        using SubButton control = new();
        control.SetStyle(ControlStyles.UserPaint, userPaint);
        control.SetStyle(ControlStyles.AllPaintingInWmPaint, allPaintingInWmPaint);
        control.SetStyle(ControlStyles.Opaque, opaque);
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int paintCallCount = 0;
        control.Paint += (sender, e) => paintCallCount++;

        Message m = new()
        {
            Msg = (int)PInvokeCore.WM_ERASEBKGND,
            Result = 250
        };
        control.WndProc(ref m);
        Assert.Equal(IntPtr.Zero, m.Result);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
        Assert.Equal(0, paintCallCount);
    }

    [WinFormsTheory]
    [MemberData(nameof(WndProc_EraseBkgnd_TestData))]
    public void Button_WndProc_InvokeEraseBkgndWithHandleWithWParam_Success(bool userPaint, bool allPaintingInWmPaint, bool opaque)
    {
        using SubButton control = new();
        control.SetStyle(ControlStyles.UserPaint, userPaint);
        control.SetStyle(ControlStyles.AllPaintingInWmPaint, allPaintingInWmPaint);
        control.SetStyle(ControlStyles.Opaque, opaque);
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int paintCallCount = 0;
        control.Paint += (sender, e) => paintCallCount++;

        using Bitmap image = new(10, 10);
        using Graphics graphics = Graphics.FromImage(image);
        IntPtr hdc = graphics.GetHdc();
        try
        {
            Message m = new()
            {
                Msg = (int)PInvokeCore.WM_ERASEBKGND,
                WParam = hdc,
                Result = 250
            };
            control.WndProc(ref m);
            Assert.Equal(IntPtr.Zero, m.Result);
            Assert.True(control.IsHandleCreated);
            Assert.Equal(0, invalidatedCallCount);
            Assert.Equal(0, styleChangedCallCount);
            Assert.Equal(0, createdCallCount);
            Assert.Equal(0, paintCallCount);
        }
        finally
        {
            graphics.ReleaseHdc();
        }
    }

    [WinFormsTheory]
    [EnumData<FlatStyle>]
    public void Button_WndProc_InvokeMouseHoverWithHandle_Success(FlatStyle flatStyle)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;

        int callCount = 0;
        control.MouseHover += (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };
        Message m = new()
        {
            Msg = (int)PInvokeCore.WM_MOUSEHOVER,
            Result = 250
        };
        control.WndProc(ref m);
        Assert.Equal(IntPtr.Zero, m.Result);
        Assert.Equal(1, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    public static IEnumerable<object[]> WndProc_ReflectCommandWithoutHandle_TestData()
    {
        yield return new object[] { FlatStyle.Flat, IntPtr.Zero, (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Flat, PARAM.FromLowHigh(0, (int)PInvoke.BN_CLICKED), (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Flat, PARAM.FromLowHigh(123, (int)PInvoke.BN_CLICKED), (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Flat, PARAM.FromLowHigh(123, 456), (IntPtr)250, 0 };

        yield return new object[] { FlatStyle.Popup, IntPtr.Zero, (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Popup, PARAM.FromLowHigh(0, (int)PInvoke.BN_CLICKED), (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Popup, PARAM.FromLowHigh(123, (int)PInvoke.BN_CLICKED), (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Popup, PARAM.FromLowHigh(123, 456), (IntPtr)250, 0 };

        yield return new object[] { FlatStyle.Standard, IntPtr.Zero, (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Standard, PARAM.FromLowHigh(0, (int)PInvoke.BN_CLICKED), (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Standard, PARAM.FromLowHigh(123, (int)PInvoke.BN_CLICKED), (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.Standard, PARAM.FromLowHigh(123, 456), (IntPtr)250, 0 };

        yield return new object[] { FlatStyle.System, IntPtr.Zero, (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.System, PARAM.FromLowHigh(0, (int)PInvoke.BN_CLICKED), (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.System, PARAM.FromLowHigh(123, (int)PInvoke.BN_CLICKED), (IntPtr)250, 1 };
        yield return new object[] { FlatStyle.System, PARAM.FromLowHigh(123, 456), (IntPtr)250, 0 };
    }

    [WinFormsFact]
    public void ButtonBase_Click_RaisesEvent()
    {
        using var button = (SubButton)CreateButton();
        bool clickEventRaised = false;
        button.Click += (sender, e) => clickEventRaised = true;
        button.PerformClick();

        clickEventRaised.Should().BeTrue();
    }

    [WinFormsTheory]
    [MemberData(nameof(WndProc_ReflectCommandWithoutHandle_TestData))]
    public void Button_WndProc_InvokeReflectCommandWithoutHandle_Success(FlatStyle flatStyle, IntPtr wParam, IntPtr expectedResult, int expectedCallCount)
    {
        using (new NoAssertContext())
        {
            using SubButton control = new()
            {
                FlatStyle = flatStyle
            };

            int callCount = 0;
            control.Click += (sender, e) =>
            {
                Assert.Same(control, sender);
                Assert.Same(EventArgs.Empty, e);
                callCount++;
            };

            Message m = new()
            {
                Msg = (int)(MessageId.WM_REFLECT_COMMAND),
                WParam = wParam,
                Result = 250
            };

            control.WndProc(ref m);
            Assert.Equal(expectedResult, m.Result);
            Assert.Equal(expectedCallCount, callCount);
            Assert.False(control.IsHandleCreated);
        }
    }

    [WinFormsTheory]
    [MemberData(nameof(WndProc_ReflectCommandWithoutHandle_TestData))]
    public void Button_WndProc_InvokeReflectCommandWithHandle_Success(FlatStyle flatStyle, IntPtr wParam, IntPtr expectedResult, int expectedCallCount)
    {
        using SubButton control = new()
        {
            FlatStyle = flatStyle
        };
        Assert.NotEqual(IntPtr.Zero, control.Handle);
        int invalidatedCallCount = 0;
        control.Invalidated += (sender, e) => invalidatedCallCount++;
        int styleChangedCallCount = 0;
        control.StyleChanged += (sender, e) => styleChangedCallCount++;
        int createdCallCount = 0;
        control.HandleCreated += (sender, e) => createdCallCount++;
        int callCount = 0;
        control.Click += (sender, e) =>
        {
            Assert.Same(control, sender);
            Assert.Same(EventArgs.Empty, e);
            callCount++;
        };

        Message m = new()
        {
            Msg = (int)(MessageId.WM_REFLECT_COMMAND),
            WParam = wParam,
            Result = 250
        };
        control.WndProc(ref m);
        Assert.Equal(expectedResult, m.Result);
        Assert.Equal(expectedCallCount, callCount);
        Assert.True(control.IsHandleCreated);
        Assert.Equal(0, invalidatedCallCount);
        Assert.Equal(0, styleChangedCallCount);
        Assert.Equal(0, createdCallCount);
    }

    [WinFormsTheory]
    [InlineData(-1)]
    [InlineData(0)]
    [InlineData(1)]
    public void Button_Flat_ValidBorder(int borderSize) => base.ButtonBase_FlatStyle_ValidFlatButtonBorder(borderSize);

    [WinFormsTheory]
    [InlineData(255, 0, 0)]
    [InlineData(0, 255, 0)]
    [InlineData(0, 0, 255)]
    public void Button_Flat_ProperColor(int red, int green, int blue) => base.ButtonBase_FlatStyle_ProperFlatButtonColor(red, green, blue);

    protected override ButtonBase CreateButton() => new SubButton();

    private class SubButton : Button
    {
        public new bool CanEnableIme => base.CanEnableIme;

        public new bool CanRaiseEvents => base.CanRaiseEvents;

        public new CreateParams CreateParams => base.CreateParams;

        public new Cursor DefaultCursor => base.DefaultCursor;

        public new ImeMode DefaultImeMode => base.DefaultImeMode;

        public new Padding DefaultMargin => base.DefaultMargin;

        public new Size DefaultMaximumSize => base.DefaultMaximumSize;

        public new Size DefaultMinimumSize => base.DefaultMinimumSize;

        public new Padding DefaultPadding => base.DefaultPadding;

        public new Size DefaultSize => base.DefaultSize;

        public new bool DesignMode => base.DesignMode;

        public new bool DoubleBuffered
        {
            get => base.DoubleBuffered;
            set => base.DoubleBuffered = value;
        }

        public new EventHandlerList Events => base.Events;

        public new int FontHeight
        {
            get => base.FontHeight;
            set => base.FontHeight = value;
        }

        public new ImeMode ImeModeBase
        {
            get => base.ImeModeBase;
            set => base.ImeModeBase = value;
        }

        public new bool IsDefault
        {
            get => base.IsDefault;
            set => base.IsDefault = value;
        }

        public new bool ResizeRedraw
        {
            get => base.ResizeRedraw;
            set => base.ResizeRedraw = value;
        }

        public new bool ShowFocusCues => base.ShowFocusCues;

        public new bool ShowKeyboardCues => base.ShowKeyboardCues;

        public new AccessibleObject CreateAccessibilityInstance() => base.CreateAccessibilityInstance();

        public new AutoSizeMode GetAutoSizeMode() => base.GetAutoSizeMode();

        public new bool GetStyle(ControlStyles flag) => base.GetStyle(flag);

        public new bool GetTopLevel() => base.GetTopLevel();

        public new void OnClick(EventArgs e) => base.OnClick(e);

        public new void OnDoubleClick(EventArgs e) => base.OnDoubleClick(e);

        public new void OnFontChanged(EventArgs e) => base.OnFontChanged(e);

        public new void OnHandleCreated(EventArgs e) => base.OnHandleCreated(e);

        public new void OnHandleDestroyed(EventArgs e) => base.OnHandleDestroyed(e);

        public new void OnMouseDoubleClick(MouseEventArgs e) => base.OnMouseDoubleClick(e);

        public new void OnMouseDown(MouseEventArgs e) => base.OnMouseDown(e);

        public new void OnMouseEnter(EventArgs eventargs) => base.OnMouseEnter(eventargs);

        public new void OnMouseLeave(EventArgs eventargs) => base.OnMouseLeave(eventargs);

        public new void OnMouseUp(MouseEventArgs eventargs) => base.OnMouseUp(eventargs);

        public new void OnTextChanged(EventArgs e) => base.OnTextChanged(e);

        public new bool ProcessMnemonic(char charCode) => base.ProcessMnemonic(charCode);

        public new void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew) => base.RescaleConstantsForDpi(deviceDpiOld, deviceDpiNew);

        public new void SetStyle(ControlStyles flag, bool value) => base.SetStyle(flag, value);

        public new void WndProc(ref Message m) => base.WndProc(ref m);
    }

    private class TestButton : Button
    {
        protected override AccessibleObject CreateAccessibilityInstance()
        {
            return new SubButtonAccessibleObject(this);
        }
    }

    private class SubButtonAccessibleObject : Button.ButtonAccessibleObject
    {
        public SubButtonAccessibleObject(Button owner) : base(owner)
        {
            RaiseAutomationEventCallsCount = 0;
            RaiseAutomationPropertyChangedEventCallsCount = 0;
        }

        public int RaiseAutomationEventCallsCount { get; private set; }

        public int RaiseAutomationPropertyChangedEventCallsCount { get; private set; }

        internal override bool RaiseAutomationEvent(UIA_EVENT_ID eventId)
        {
            if (Owner.IsHandleCreated)
            {
                RaiseAutomationEventCallsCount++;
            }

            return base.RaiseAutomationEvent(eventId);
        }

        internal override bool RaiseAutomationPropertyChangedEvent(UIA_PROPERTY_ID propertyId, VARIANT oldValue, VARIANT newValue)
        {
            if (Owner.IsHandleCreated)
            {
                RaiseAutomationPropertyChangedEventCallsCount++;
            }

            return base.RaiseAutomationPropertyChangedEvent(propertyId, oldValue, newValue);
        }
    }
}
