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

[Servicing 3.1] Fixing regression in Control.AccessibleName property #3600

Merged
Show file tree
Hide file tree
Changes from 9 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
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace System
{
public static class ReflectionHelper
{
public static IEnumerable<object[]> 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,
RussKie marked this conversation as resolved.
Show resolved Hide resolved
binder: null,
types: Array.Empty<Type>(),
modifiers: null) == null)
{
continue;
}

yield return new object[] { type };
Tanya-Solyanik marked this conversation as resolved.
Show resolved Hide resolved
}
}

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 @@ -441,6 +441,17 @@ public override bool RaiseLiveRegionChanged()
internal override bool IsIAccessibleExSupported()
=> Owner is IAutomationLiveRegion ? true : base.IsIAccessibleExSupported();

internal override bool IsPatternSupported(int patternId)
{
// Override this, in your derived class, if you implement UIAutomation patterns
if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId)
{
return true;
}

return base.IsPatternSupported(patternId);
}

internal override object GetPropertyValue(int propertyID)
{
if (propertyID == NativeMethods.UIA_LiveSettingPropertyId && Owner is IAutomationLiveRegion)
Expand Down
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)
Tanya-Solyanik marked this conversation as resolved.
Show resolved Hide resolved
{
return propertyID == NativeMethods.UIA_NamePropertyId
? Name
: base.GetPropertyValue(propertyID);
}

internal override bool IsIAccessibleExSupported()
{
if (_owner != null)
Expand Down
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 @@ -188,8 +188,7 @@ internal override bool IsIAccessibleExSupported()
internal override bool IsPatternSupported(int patternId)
{
if (patternId == NativeMethods.UIA_ScrollPatternId ||
patternId == NativeMethods.UIA_SelectionPatternId ||
patternId == NativeMethods.UIA_LegacyIAccessiblePatternId)
Tanya-Solyanik marked this conversation as resolved.
Show resolved Hide resolved
patternId == NativeMethods.UIA_SelectionPatternId)
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,7 @@ internal override bool IsPatternSupported(int patternId) =>
var p when
p == NativeMethods.UIA_ValuePatternId ||
p == NativeMethods.UIA_GridPatternId ||
p == NativeMethods.UIA_TablePatternId ||
p == NativeMethods.UIA_LegacyIAccessiblePatternId => true,
p == NativeMethods.UIA_TablePatternId => true,
_ => base.IsPatternSupported(patternId)
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,21 +313,6 @@ internal override object GetPropertyValue(int propertyID)
}
}

/// <summary>
/// Indicates whether the specified pattern is supported.
/// </summary>
/// <param name="patternId">The pattern ID.</param>
/// <returns>True if specified pattern is supported, otherwise false.</returns>
internal override bool IsPatternSupported(int patternId)
Tanya-Solyanik marked this conversation as resolved.
Show resolved Hide resolved
{
if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId)
{
return true;
}

return base.IsPatternSupported(patternId);
}

/// <summary>
/// Gets the accessible role.
/// </summary>
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
10 changes: 10 additions & 0 deletions src/System.Windows.Forms/src/System/Windows/Forms/ToolStripItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4727,6 +4727,16 @@ internal override void SetFocus()
Owner.Select();
}

internal override bool IsPatternSupported(int patternId)
{
if (patternId == NativeMethods.UIA_LegacyIAccessiblePatternId)
{
return true;
}

return base.IsPatternSupported(patternId);
}

internal void RaiseFocusChanged()
{
ToolStrip root = ownerItem.RootToolStrip;
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,93 @@
// 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 Xunit;

namespace System.Windows.Forms.Tests.AccessibleObjects
{
public class Control_ControlAccessibleObjectTests
{
public static IEnumerable<object[]> ControlAccessibleObject_TestData()
{
return ReflectionHelper.GetPublicNotAbstractClasses<Control>();
}

[StaTheory]
Tanya-Solyanik marked this conversation as resolved.
Show resolved Hide resolved
[MemberData(nameof(ControlAccessibleObject_TestData))]
public void ControlAccessibleObject_LegacyIAccessible_Custom_Role_ReturnsExpected(Type type)
{
using Control control = ReflectionHelper.InvokePublicConstructor<Control>(type);

if (control == null || !control.SupportsUiaProviders)
SergeySmirnov-Akvelon marked this conversation as resolved.
Show resolved Hide resolved
{
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_IsPatternSupported_LegacyIAccessible_ReturnsTrue(Type type)
{
using Control control = ReflectionHelper.InvokePublicConstructor<Control>(type);

if (control == null || !control.SupportsUiaProviders)
SergeySmirnov-Akvelon marked this conversation as resolved.
Show resolved Hide resolved
{
return;
}

AccessibleObject controlAccessibleObject = control.AccessibilityObject;

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

Assert.True(supportsLegacyIAccessiblePatternId);
}

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

if (control == null || !control.SupportsUiaProviders)
SergeySmirnov-Akvelon marked this conversation as resolved.
Show resolved Hide resolved
{
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";
Tanya-Solyanik marked this conversation as resolved.
Show resolved Hide resolved
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()
Tanya-Solyanik marked this conversation as resolved.
Show resolved Hide resolved
{
DataGridView dataGridView = new DataGridView();

Expand Down
Loading