Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit tests for DesignerFrame #12712

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,377 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using Moq;

namespace System.Windows.Forms.Design.Tests;

public class DesignerFrameTests
{
[Fact]
public void DesignerFrame_Constructor_SetsProperties()
{
Mock<ISite> mockSite = new();
Mock<IUIService> mockUIService = new();
mockUIService.Setup(ui => ui.Styles["ArtboardBackground"]).Returns(Color.Red);
mockSite.Setup(site => site.GetService(typeof(IUIService))).Returns(mockUIService.Object);

DesignerFrame designerFrame = new(mockSite.Object);

designerFrame.Text.Should().Be("DesignerFrame");
designerFrame.BackColor.Should().Be(Color.Red);
designerFrame.Controls.Cast<Control>().Should().HaveCount(1);

Type? overlayControlType = typeof(DesignerFrame).GetNestedType("OverlayControl", BindingFlags.NonPublic);
overlayControlType.Should().NotBeNull();
designerFrame.Controls[0].Should().BeOfType(overlayControlType);
}

[Fact]
public void DesignerFrame_Initialize_SetsDesignerProperties()
{
Mock<ISite> mockSite = new();
Control control = new();
DesignerFrame designerFrame = new(mockSite.Object);

designerFrame.Initialize(control);

FieldInfo? designerField = typeof(DesignerFrame).GetField("_designer", BindingFlags.NonPublic | BindingFlags.Instance);

object designer = designerField?.GetValue(designerFrame) ?? throw new InvalidOperationException("Field '_designer' not found in DesignerFrame.");

designer.Should().Be(control);
control.Visible.Should().BeTrue();
control.Enabled.Should().BeTrue();

designerFrame.Controls.Cast<Control>().ToList().ForEach(c =>
Console.WriteLine($"Control Type: {c.GetType()}, Control Name: {c.Name}"));

designerFrame.Controls.Cast<Control>().Should().NotContain(control);
}

[Fact]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR provides thorough testing for the OverlayControl class. However, since OverlayControl is a private class fully encapsulated within the DesignerFrame class (the actual target of this PR), it cannot be accessed directly outside of DesignerFrame.

Testing OverlayControl here relies heavily on reflection, which we typically avoid in unit tests.

An alternative approach could involve recreating OverlayControl within the test class, but given its size and complexity, this may introduce significant overhead for testing an inner private class.

At this stage, it might be more practical to focus on testing DesignerFrame's more exposed properties and methods, deferring OverlayControl-specific tests for a future refactor.

What do you think @lonitra @Tanya-Solyanik ?

Example of how tests could go:

[Fact]
public void Designer_ProcessDialogKey()
{
    Mock<ISite> mockSite = new(); // Consider making this a field if reused
    using DesignerFrame designerFrame = new(mockSite.Object); // Same for this if used across methods
    bool result = designerFrame.TestAccessor().Dynamic.ProcessDialogKey(Keys.Enter);
    result.Should().BeFalse();
}

public void OverlayControl_Constructor_SetsProperties()
{
Mock<IServiceProvider> mockProvider = new();

Type? overlayControlType = typeof(DesignerFrame).GetNestedType("OverlayControl", BindingFlags.NonPublic);
overlayControlType.Should().NotBeNull();
ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);
constructor.Should().NotBeNull();
object? overlayControl = constructor?.Invoke([mockProvider.Object]);
overlayControl.Should().NotBeNull();

FieldInfo? providerField = overlayControlType?.GetField("_provider", BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo? overlayListField = overlayControlType?.GetField("_overlayList", BindingFlags.NonPublic | BindingFlags.Instance);

providerField?.GetValue(overlayControl).Should().Be(mockProvider.Object);
overlayListField?.GetValue(overlayControl).Should().NotBeNull();

((bool)overlayControlType?.GetProperty("AutoScroll")?.GetValue(overlayControl)!).Should().BeTrue();
overlayControlType?.GetProperty("Text")?.GetValue(overlayControl).Should().Be("OverlayControl");
}

[Fact]
public void OverlayControl_PushOverlay_AddsControl()
{
Mock<IServiceProvider> mockProvider = new();

Type? overlayControlType = typeof(DesignerFrame).GetNestedType("OverlayControl", BindingFlags.NonPublic);
overlayControlType.Should().NotBeNull();

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);
constructor.Should().NotBeNull();

object? overlayControl = constructor?.Invoke([mockProvider.Object]);
overlayControl.Should().NotBeNull();

Control control = new();

MethodInfo? pushOverlayMethod = overlayControlType?.GetMethod("PushOverlay");
pushOverlayMethod.Should().NotBeNull();

object? index = pushOverlayMethod?.Invoke(overlayControl, [control]);

FieldInfo? overlayListField = overlayControlType?.GetField("_overlayList", BindingFlags.NonPublic | BindingFlags.Instance);
List<Control>? overlayList = overlayListField?.GetValue(overlayControl) as List<Control>;

overlayList.Should().NotBeNull();
overlayList.Should().Contain(control);
index.Should().Be(0);

if ((bool)overlayControlType?.GetProperty("IsHandleCreated")?.GetValue(overlayControl)!)
{
FieldInfo? parentField = typeof(Control).GetField("_parent", BindingFlags.NonPublic | BindingFlags.Instance);
parentField?.GetValue(control).Should().Be(overlayControl);

PropertyInfo? displayRectangleProperty = overlayControlType?.GetProperty("DisplayRectangle");
displayRectangleProperty?.GetValue(overlayControl).Should().Be(control.Bounds);
}
}

[Fact]
public void OverlayControl_RemoveOverlay_RemovesControl()
{
Mock<IServiceProvider> mockProvider = new();

Type? overlayControlType = typeof(DesignerFrame).GetNestedType("OverlayControl", BindingFlags.NonPublic);
overlayControlType.Should().NotBeNull();

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);
constructor.Should().NotBeNull();

object? overlayControl = constructor?.Invoke([mockProvider.Object]);
overlayControl.Should().NotBeNull();

Control control = new();

MethodInfo? pushOverlayMethod = overlayControlType?.GetMethod("PushOverlay");
pushOverlayMethod.Should().NotBeNull();
pushOverlayMethod?.Invoke(overlayControl, [control]);

MethodInfo? removeOverlayMethod = overlayControlType?.GetMethod("RemoveOverlay");
removeOverlayMethod.Should().NotBeNull();
removeOverlayMethod?.Invoke(overlayControl, [control]);

FieldInfo? overlayListField = overlayControlType?.GetField("_overlayList", BindingFlags.NonPublic | BindingFlags.Instance);
List<Control>? overlayList = overlayListField?.GetValue(overlayControl) as List<Control>;

overlayList.Should().NotBeNull();
overlayList.Should().NotContain(control);
control.Visible.Should().BeFalse();
control.Parent.Should().BeNull();
}

[Fact]
public void OverlayControl_InsertOverlay_InsertsControlAtIndex()
{
Mock<IServiceProvider> mockProvider = new();

Type? overlayControlType = typeof(DesignerFrame).GetNestedType("OverlayControl", BindingFlags.NonPublic);
overlayControlType.Should().NotBeNull();

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);
constructor.Should().NotBeNull();

object? overlayControl = constructor?.Invoke([mockProvider.Object]);
overlayControl.Should().NotBeNull();

Control control1 = new();
Control control2 = new();

MethodInfo? pushOverlayMethod = overlayControlType?.GetMethod("PushOverlay");
pushOverlayMethod.Should().NotBeNull();
pushOverlayMethod?.Invoke(overlayControl, [control1]);

MethodInfo? insertOverlayMethod = overlayControlType?.GetMethod("InsertOverlay");
insertOverlayMethod.Should().NotBeNull();
insertOverlayMethod?.Invoke(overlayControl, [control2, 0]);

FieldInfo? overlayListField = overlayControlType?.GetField("_overlayList", BindingFlags.NonPublic | BindingFlags.Instance);
List<Control>? overlayList = overlayListField?.GetValue(overlayControl) as List<Control>;

overlayList.Should().NotBeNull();
overlayList?[0].Should().Be(control2);
overlayList?[1].Should().Be(control1);

if ((bool)overlayControlType?.GetProperty("IsHandleCreated")?.GetValue(overlayControl)!)
{
FieldInfo? parentField = typeof(Control).GetField("_parent", BindingFlags.NonPublic | BindingFlags.Instance);
parentField?.GetValue(control2).Should().Be(overlayControl);

PropertyInfo? displayRectangleProperty = overlayControlType?.GetProperty("DisplayRectangle");
displayRectangleProperty?.GetValue(overlayControl).Should().Be(control2.Bounds);
}
}

[Fact]
public void OverlayControl_InvalidateOverlays_InvokesInvalidateOnIntersectingControls()
{
Mock<IServiceProvider> mockProvider = new();

Type? overlayControlType = typeof(DesignerFrame).GetNestedType("OverlayControl", BindingFlags.NonPublic);
overlayControlType.Should().NotBeNull();

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);
constructor.Should().NotBeNull();

object? overlayControl = constructor?.Invoke([mockProvider.Object]);
overlayControl.Should().NotBeNull();

Control control1 = new() { Bounds = new Rectangle(0, 0, 100, 100) };
Control control2 = new() { Bounds = new Rectangle(50, 50, 100, 100) };

MethodInfo? pushOverlayMethod = overlayControlType?.GetMethod("PushOverlay");
pushOverlayMethod.Should().NotBeNull();
pushOverlayMethod?.Invoke(overlayControl, [control1]);
pushOverlayMethod?.Invoke(overlayControl, [control2]);

MethodInfo? invalidateOverlaysMethod = overlayControlType?.GetMethod("InvalidateOverlays", [typeof(Rectangle)]);
invalidateOverlaysMethod.Should().NotBeNull();
invalidateOverlaysMethod?.Invoke(overlayControl, [new Rectangle(25, 25, 50, 50)]);

FieldInfo? overlayListField = overlayControlType?.GetField("_overlayList", BindingFlags.NonPublic | BindingFlags.Instance);
List<Control>? overlayList = overlayListField?.GetValue(overlayControl) as List<Control>;

overlayList.Should().NotBeNull();
overlayList.Should().Contain(control1);
overlayList.Should().Contain(control2);

MethodInfo? invalidateMethod = typeof(Control).GetMethod("Invalidate", [typeof(Rectangle)]);
invalidateMethod.Should().NotBeNull();

invalidateMethod?.Invoke(control1, [new Rectangle(25, 25, 50, 50)]);
invalidateMethod?.Invoke(control2, [new Rectangle(25, 25, 50, 50)]);
}

[Fact]
public void OverlayControl_InvalidateOverlays_InvokesInvalidateOnIntersectingControlsWithRegion()
{
Mock<IServiceProvider> mockProvider = new();

Type? overlayControlType = typeof(DesignerFrame).GetNestedType("OverlayControl", BindingFlags.NonPublic);
overlayControlType.Should().NotBeNull();

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);
constructor.Should().NotBeNull();

object? overlayControl = constructor?.Invoke([mockProvider.Object]);
overlayControl.Should().NotBeNull();

Control control1 = new() { Bounds = new Rectangle(0, 0, 100, 100) };
Control control2 = new() { Bounds = new Rectangle(50, 50, 100, 100) };

MethodInfo? pushOverlayMethod = overlayControlType?.GetMethod("PushOverlay");
pushOverlayMethod.Should().NotBeNull();
pushOverlayMethod?.Invoke(overlayControl, [control1]);
pushOverlayMethod?.Invoke(overlayControl, [control2]);

MethodInfo? invalidateOverlaysMethod = overlayControlType?.GetMethod("InvalidateOverlays", [typeof(Region)]);
invalidateOverlaysMethod.Should().NotBeNull();

using (var region = new Region(new Rectangle(25, 25, 50, 50)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please declare variables explicitly with a type. This happens again a few lines below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please declare variables explicitly with a type. This happens again a few lines below.

{
invalidateOverlaysMethod?.Invoke(overlayControl, [region]);
}

FieldInfo? overlayListField = overlayControlType?.GetField("_overlayList", BindingFlags.NonPublic | BindingFlags.Instance);
List<Control>? overlayList = overlayListField?.GetValue(overlayControl) as List<Control>;

overlayList.Should().NotBeNull();
overlayList.Should().Contain(control1);
overlayList.Should().Contain(control2);

MethodInfo? invalidateMethod = typeof(Control).GetMethod("Invalidate", [typeof(Region)]);
invalidateMethod.Should().NotBeNull();

using (var region = new Region(new Rectangle(25, 25, 50, 50)))
{
invalidateMethod?.Invoke(control1, [region]);
invalidateMethod?.Invoke(control2, [region]);
}
}

[Fact]
public void CanReflectOverlayControlType()
{
Type designerFrameType = typeof(DesignerFrame);

Type? overlayControlType = designerFrameType.GetNestedType("OverlayControl", BindingFlags.NonPublic);

overlayControlType.Should().NotBeNull();
if (overlayControlType is not null)
{
Console.WriteLine($"OverlayControl type found: {overlayControlType.FullName}");
}
}

[Fact]
public void CanReflectOverlayControlConstructor()
{
Type designerFrameType = typeof(DesignerFrame);
Type? overlayControlType = designerFrameType.GetNestedType("OverlayControl", BindingFlags.NonPublic);

overlayControlType.Should().NotBeNull();

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);

constructor.Should().NotBeNull();
}

[Fact]
public void CanCreateOverlayControlInstance()
{
Mock<IServiceProvider> mockProvider = new();

Type designerFrameType = typeof(DesignerFrame);

Type? overlayControlType = designerFrameType.GetNestedType("OverlayControl", BindingFlags.NonPublic);

overlayControlType.Should().NotBeNull();

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);

constructor.Should().NotBeNull();

object? overlayControl = constructor?.Invoke([mockProvider.Object]);

overlayControl.Should().NotBeNull();
}

[Fact]
public void CanAccessOverlayControlControlsProperty()
{
Mock<IServiceProvider> mockProvider = new();

Type designerFrameType = typeof(DesignerFrame);

Type? overlayControlType = designerFrameType.GetNestedType("OverlayControl", BindingFlags.NonPublic);

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);

object? overlayControl = constructor?.Invoke([mockProvider.Object]);

PropertyInfo? controlsProperty = overlayControlType?.GetProperty("Controls", BindingFlags.Instance | BindingFlags.Public);

Control.ControlCollection? controls = controlsProperty?.GetValue(overlayControl) as Control.ControlCollection;

controls.Should().NotBeNull();
}

[Fact]
public void CanAddControlsToOverlayControl()
{
Mock<IServiceProvider> mockProvider = new();

Type designerFrameType = typeof(DesignerFrame);

Type? overlayControlType = designerFrameType.GetNestedType("OverlayControl", BindingFlags.NonPublic);

ConstructorInfo? constructor = overlayControlType?.GetConstructor([typeof(IServiceProvider)]);

object? overlayControl = constructor?.Invoke([mockProvider.Object]);

PropertyInfo? controlsProperty = overlayControlType?.GetProperty("Controls", BindingFlags.Instance | BindingFlags.Public);

Control.ControlCollection? controls = controlsProperty?.GetValue(overlayControl) as Control.ControlCollection;

controls.Should().NotBeNull();
controls.Should().BeOfType<Control.ControlCollection>();

Control control1 = new() { Bounds = new Rectangle(0, 0, 100, 100) };
Control control2 = new() { Bounds = new Rectangle(100, 100, 100, 100) };

controls?.Add(control1);
controls?.Add(control2);

List<Control>? controlsList = controls?.Cast<Control>().ToList();

controlsList.Should().Contain(control1);
controlsList.Should().Contain(control2);
}
}