Skip to content

Commit

Permalink
[Servicing 3.1] Fixing regression in Control.AccessibleName property (#…
Browse files Browse the repository at this point in the history
…3600)

Fixes #3504
  • Loading branch information
SergeySmirnov-Akvelon committed Aug 12, 2020
1 parent 96f8bef commit cf28cab
Show file tree
Hide file tree
Showing 12 changed files with 320 additions and 17 deletions.
17 changes: 9 additions & 8 deletions eng/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,15 @@ stages:

# Run Integration Tests
# Tests are run with /m:1 to avoid parallelism across different assemblies which can lead to
# UI race conditions
- script: eng\cibuild.cmd
-integrationTest
-configuration $(_BuildConfig)
$(_OfficialBuildIdArgs)
/bl:$(BUILD.SOURCESDIRECTORY)\artifacts\log\$(_BuildConfig)\IntegrationTest.binlog
/m:1
displayName: Run Integration Tests
# UI race conditions
# Flaky tests, see: https://github.com/dotnet/winforms/issues/3617
# - script: eng\cibuild.cmd
# -integrationTest
# -configuration $(_BuildConfig)
# $(_OfficialBuildIdArgs)
# /bl:$(BUILD.SOURCESDIRECTORY)\artifacts\log\$(_BuildConfig)\IntegrationTest.binlog
# /m:1
# displayName: Run Integration Tests

# Create Nuget package, sign, and publish
- script: eng\cibuild.cmd
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>InternalUtilitiesForTests</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RootNamespace>System</RootNamespace>
</PropertyGroup>

<ItemGroup>
Expand Down
52 changes: 52 additions & 0 deletions src/Common/tests/InternalUtilitiesForTests/src/ReflectionHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace System
{
public static class ReflectionHelper
{
public static IEnumerable<Type> GetPublicNotAbstractClasses<T>()
{
var types = typeof(T).Assembly.GetTypes().Where(type => IsPublicNonAbstract<T>(type));
foreach (var type in types)
{
if (type.GetConstructor(
bindingAttr: BindingFlags.Public | BindingFlags.Instance,
binder: null,
types: Array.Empty<Type>(),
modifiers: null) == null)
{
continue;
}

yield return type ;
}
}

public static T InvokePublicConstructor<T>(Type type)
{
var ctor = type.GetConstructor(
bindingAttr: BindingFlags.Public | BindingFlags.Instance,
binder: null,
types: Array.Empty<Type>(),
modifiers: null);

if (ctor == null)
{
return default;
}

return (T)ctor.Invoke(Array.Empty<object>());
}

public static bool IsPublicNonAbstract<T>(Type type)
{
return !type.IsAbstract && type.IsPublic && typeof(T).IsAssignableFrom(type);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ internal FormAccessibleObject(Form owner) : base(owner)

internal override Rectangle BoundingRectangle => _owner.Bounds;

internal override object GetPropertyValue(int propertyID)
{
return propertyID == NativeMethods.UIA_NamePropertyId
? Name
: base.GetPropertyValue(propertyID);
}

internal override bool IsIAccessibleExSupported()
{
if (_owner != null)
Expand Down
2 changes: 2 additions & 0 deletions src/System.Windows.Forms/src/System/Windows/Forms/GroupBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,8 @@ internal override object GetPropertyValue(int propertyID)
return NativeMethods.UIA_GroupControlTypeId;
case NativeMethods.UIA_IsKeyboardFocusablePropertyId:
return true;
case NativeMethods.UIA_NamePropertyId:
return Name;
}

return base.GetPropertyValue(propertyID);
Expand Down
7 changes: 5 additions & 2 deletions src/System.Windows.Forms/src/System/Windows/Forms/Label.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1744,9 +1744,12 @@ public override AccessibleRole Role

internal override object GetPropertyValue(int propertyID)
{
if (propertyID == NativeMethods.UIA_ControlTypePropertyId)
switch (propertyID)
{
return NativeMethods.UIA_TextControlTypeId;
case NativeMethods.UIA_ControlTypePropertyId:
return NativeMethods.UIA_TextControlTypeId;
case NativeMethods.UIA_NamePropertyId:
return Name;
}

return base.GetPropertyValue(propertyID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5593,9 +5593,12 @@ internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNaviga

internal override object GetPropertyValue(int propertyID)
{
if (propertyID == NativeMethods.UIA_ControlTypePropertyId)
switch (propertyID)
{
return NativeMethods.UIA_ToolBarControlTypeId;
case NativeMethods.UIA_ControlTypePropertyId:
return NativeMethods.UIA_ToolBarControlTypeId;
case NativeMethods.UIA_NamePropertyId:
return Name;
}

return base.GetPropertyValue(propertyID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -856,9 +856,12 @@ public ToolStripTextBoxControlAccessibleObject(Control toolStripHostedControl, T

internal override object GetPropertyValue(int propertyID)
{
if (propertyID == NativeMethods.UIA_ControlTypePropertyId)
switch (propertyID)
{
return NativeMethods.UIA_EditControlTypeId;
case NativeMethods.UIA_ControlTypePropertyId:
return NativeMethods.UIA_EditControlTypeId;
case NativeMethods.UIA_NamePropertyId:
return Name;
}

return base.GetPropertyValue(propertyID);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

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

namespace System.Windows.Forms.Tests.AccessibleObjects
{
public class Control_ControlAccessibleObjectTests
{
public static IEnumerable<object[]> ControlAccessibleObject_IsPatternSupported_LegacyIAccessible_TestData()
{
var supportedLegacyIAccessiblePatternClasses = new List<Type> {
typeof(Form),
typeof(ListBox),
typeof(MonthCalendar),
typeof(PrintPreviewDialog)
};

return ReflectionHelper.GetPublicNotAbstractClasses<Control>()
.Select(type => new object[] { type, supportedLegacyIAccessiblePatternClasses.Contains(type) });
}

[StaTheory]
[MemberData(nameof(ControlAccessibleObject_IsPatternSupported_LegacyIAccessible_TestData))]
public void ControlAccessibleObject_IsPatternSupported_LegacyIAccessible_ReturnsExpected(Type type, bool legacyIAccessiblePatternSupported)
{
using Control control = ReflectionHelper.InvokePublicConstructor<Control>(type);

if (control == null)
{
return;
}

AccessibleObject controlAccessibleObject = control.AccessibilityObject;

bool supportsLegacyIAccessiblePatternId = controlAccessibleObject.IsPatternSupported(NativeMethods.UIA_LegacyIAccessiblePatternId);

Assert.Equal(legacyIAccessiblePatternSupported, supportsLegacyIAccessiblePatternId);
}

public static IEnumerable<object[]> ControlAccessibleObject_TestData()
{
return ReflectionHelper.GetPublicNotAbstractClasses<Control>().Select(type => new object[] { type });
}

[StaTheory]
[MemberData(nameof(ControlAccessibleObject_TestData))]
public void ControlAccessibleObject_Custom_Role_ReturnsExpected(Type type)
{
using Control control = ReflectionHelper.InvokePublicConstructor<Control>(type);

if (control == null)
{
return;
}

control.AccessibleRole = AccessibleRole.Link;
AccessibleObject controlAccessibleObject = control.AccessibilityObject;

var accessibleObjectRole = controlAccessibleObject.Role;

Assert.Equal(AccessibleRole.Link, accessibleObjectRole);
}

[StaTheory]
[MemberData(nameof(ControlAccessibleObject_TestData))]
public void ControlAccessibleObject_Custom_Description_ReturnsExpected(Type type)
{
using Control control = ReflectionHelper.InvokePublicConstructor<Control>(type);

if (control == null)
{
return;
}

control.AccessibleDescription = "Test Accessible Description";
AccessibleObject controlAccessibleObject = control.AccessibilityObject;

var accessibleObjectDescription = controlAccessibleObject.Description;

Assert.Equal("Test Accessible Description", accessibleObjectDescription);
}

[StaTheory]
[MemberData(nameof(ControlAccessibleObject_TestData))]
public void ControlAccessibleObject_GetPropertyValue_Custom_Name_ReturnsExpected(Type type)
{
using Control control = ReflectionHelper.InvokePublicConstructor<Control>(type);

if (control == null || !control.SupportsUiaProviders)
{
return;
}

control.Name = "Name1";
control.AccessibleName = "Test Name";
AccessibleObject controlAccessibleObject = control.AccessibilityObject;

var accessibleName = controlAccessibleObject.GetPropertyValue(NativeMethods.UIA_NamePropertyId);

Assert.Equal("Test Name", accessibleName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace System.Windows.Forms.Tests.AccessibleObjects
public class DataGridViewAccessibleObjectTests
{
[Fact]
public void PropertyGridAccessibleObject_Ctor_Default()
public void DataGridViewAccessibleObject_Ctor_Default()
{
DataGridView dataGridView = new DataGridView();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Drawing;
using Moq;
using System.Linq;
using Xunit;

namespace System.Windows.Forms.Tests
Expand All @@ -20,6 +21,7 @@ public void ToolStripItemAccessibleObject_Ctor_ToolStripItem()
AccessibleName = "Name",
AccessibleRole = AccessibleRole.MenuBar
};

var accessibleObject = new ToolStripItem.ToolStripItemAccessibleObject(item);
Assert.Equal(Rectangle.Empty, accessibleObject.Bounds);
Assert.Equal("DefaultActionDescription", accessibleObject.DefaultAction);
Expand All @@ -38,6 +40,63 @@ public void ToolStripItemAccessibleObject_Ctor_NullOwnerItem_ThrowsArgumentNullE
Assert.Throws<ArgumentNullException>("ownerItem", () => new ToolStripItem.ToolStripItemAccessibleObject(null));
}

public static IEnumerable<object[]> ToolStripItemAccessibleObject_TestData()
{
return ReflectionHelper.GetPublicNotAbstractClasses<ToolStripItem>().Select(type => new object[] { type });
}

[Theory]
[MemberData(nameof(ToolStripItemAccessibleObject_TestData))]
public void ToolStripItemAccessibleObject_Custom_Role_ReturnsExpected(Type type)
{
using ToolStripItem item = ReflectionHelper.InvokePublicConstructor<ToolStripItem>(type);
item.AccessibleRole = AccessibleRole.Link;
AccessibleObject toolStripItemAccessibleObject = item.AccessibilityObject;

var accessibleObjectRole = toolStripItemAccessibleObject.Role;

Assert.Equal(AccessibleRole.Link, accessibleObjectRole);
}

[WinFormsTheory]
[MemberData(nameof(ToolStripItemAccessibleObject_TestData))]
public void ToolStripItemAccessibleObject_IsPatternSupported_LegacyIAccessible_ReturnsFalse(Type type)
{
using ToolStripItem item = ReflectionHelper.InvokePublicConstructor<ToolStripItem>(type);
AccessibleObject toolStripItemAccessibleObject = item.AccessibilityObject;

bool supportsLegacyIAccessiblePatternId = toolStripItemAccessibleObject.IsPatternSupported(NativeMethods.UIA_LegacyIAccessiblePatternId);

Assert.False(supportsLegacyIAccessiblePatternId);
}

[WinFormsTheory]
[MemberData(nameof(ToolStripItemAccessibleObject_TestData))]
public void ToolStripItemAccessibleObject_Custom_Description_ReturnsExpected(Type type)
{
using ToolStripItem item = ReflectionHelper.InvokePublicConstructor<ToolStripItem>(type);
item.AccessibleDescription = "Test Accessible Description";
AccessibleObject toolStripItemAccessibleObject = item.AccessibilityObject;

var accessibleObjectDescription = toolStripItemAccessibleObject.Description;

Assert.Equal("Test Accessible Description", accessibleObjectDescription);
}

[WinFormsTheory]
[MemberData(nameof(ToolStripItemAccessibleObject_TestData))]
public void ToolStripItemAccessibleObject_GetPropertyValue_Custom_Name_ReturnsExpected(Type type)
{
using ToolStripItem item = ReflectionHelper.InvokePublicConstructor<ToolStripItem>(type);
item.Name = "Name1";
item.AccessibleName = "Test Name";

AccessibleObject toolStripItemAccessibleObject = item.AccessibilityObject;
var accessibleName = toolStripItemAccessibleObject.GetPropertyValue(NativeMethods.UIA_NamePropertyId);

Assert.Equal("Test Name", accessibleName);
}

private class SubToolStripItem : ToolStripItem
{
public SubToolStripItem() : base()
Expand Down

0 comments on commit cf28cab

Please sign in to comment.