From 74935272f4f9ee8aac347da0351a4952c3d09518 Mon Sep 17 00:00:00 2001 From: Vladimir Krestov Date: Wed, 6 May 2020 14:43:40 +0300 Subject: [PATCH 1/3] Added protection for MonthCalendarAccessibleObject against incorrect parameters of methods to avoid exception throwing Related Issues #2912 and #2475 Related PR #2911 --- .../System/Windows/Forms/DateTimePicker.cs | 6 ++ ...thCalendar.CalendarBodyAccessibleObject.cs | 6 ++ ...hCalendar.CalendarChildAccessibleObject.cs | 6 +- ...endar.CalendarGridChildAccessibleObject.cs | 4 +- ...hCalendar.MonthCalendarAccessibleObject.cs | 90 ++++++++++--------- 5 files changed, 67 insertions(+), 45 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/DateTimePicker.cs b/src/System.Windows.Forms/src/System/Windows/Forms/DateTimePicker.cs index 2327de62906..31282c104ba 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/DateTimePicker.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/DateTimePicker.cs @@ -1667,6 +1667,12 @@ internal static Kernel32.SYSTEMTIME DateTimeToSysTime(DateTime time) /// internal static DateTime SysTimeToDateTime(Kernel32.SYSTEMTIME s) { + if (s.wYear <= 0 || s.wMonth <= 0 || s.wDay <= 0) + { + Debug.Fail("Incorrect SYSTEMTIME info!"); + return DateTime.MinValue; + } + return new DateTime(s.wYear, s.wMonth, s.wDay, s.wHour, s.wMinute, s.wSecond); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarBodyAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarBodyAccessibleObject.cs index f9a6b517e4d..441e0ad9f6e 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarBodyAccessibleObject.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarBodyAccessibleObject.cs @@ -54,6 +54,12 @@ public CalendarChildAccessibleObject GetFromPoint(MCHITTESTINFO hitTestInfo) case MCHT.CALENDARWEEKNUM: case MCHT.CALENDARDATE: AccessibleObject rowAccessibleObject = _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, this, hitTestInfo.iRow); + + if (rowAccessibleObject == null) + { + return null; + } + return _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, rowAccessibleObject, hitTestInfo.iCol); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildAccessibleObject.cs index fa3f1b700bd..108fdb110cf 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildAccessibleObject.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildAccessibleObject.cs @@ -16,13 +16,13 @@ public partial class MonthCalendar /// internal abstract class CalendarChildAccessibleObject : AccessibleObject { - protected MonthCalendarAccessibleObject _calendarAccessibleObject; + protected readonly MonthCalendarAccessibleObject _calendarAccessibleObject; protected int _calendarIndex; protected CalendarChildType _itemType; public CalendarChildAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, CalendarChildType itemType) { - _calendarAccessibleObject = calendarAccessibleObject; + _calendarAccessibleObject = calendarAccessibleObject ?? throw new ArgumentNullException(nameof(calendarAccessibleObject)); _calendarIndex = calendarIndex; _itemType = itemType; } @@ -65,7 +65,7 @@ public void RaiseMouseClick() return; } - var rectangle = CalculateBoundingRectangle(); + RECT rectangle = CalculateBoundingRectangle(); int x = rectangle.left + ((rectangle.right - rectangle.left) / 2); int y = rectangle.top + ((rectangle.bottom - rectangle.top) / 2); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarGridChildAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarGridChildAccessibleObject.cs index 35a1b70786c..07c1826d239 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarGridChildAccessibleObject.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarGridChildAccessibleObject.cs @@ -13,12 +13,12 @@ public partial class MonthCalendar /// internal abstract class CalendarGridChildAccessibleObject : CalendarChildAccessibleObject { - protected AccessibleObject _parentAccessibleObject; + protected readonly AccessibleObject _parentAccessibleObject; public CalendarGridChildAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, CalendarChildType itemType, AccessibleObject parentAccessibleObject, int itemIndex) : base(calendarAccessibleObject, calendarIndex, itemType) { - _parentAccessibleObject = parentAccessibleObject; + _parentAccessibleObject = parentAccessibleObject ?? throw new ArgumentNullException(nameof(parentAccessibleObject)); } public override AccessibleObject Parent => _parentAccessibleObject; diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs index b57d94c9955..3b87c35642a 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs @@ -27,7 +27,7 @@ internal class MonthCalendarAccessibleObject : ControlAccessibleObject public MonthCalendarAccessibleObject(Control owner) : base(owner) { - _owner = owner as MonthCalendar; + _owner = (MonthCalendar)owner; } public UiaCore.UIA ControlType => @@ -83,10 +83,6 @@ public override string Name } name = string.Empty; - if (_owner == null) - { - return name; - } if (_owner._mcCurView == MCMV.MONTH) { @@ -134,47 +130,34 @@ public override string Value { get { - var value = string.Empty; - if (_owner == null) - { - return value; - } - try { if (_owner._mcCurView == MCMV.MONTH) { if (System.DateTime.Equals(_owner.SelectionStart.Date, _owner.SelectionEnd.Date)) { - value = _owner.SelectionStart.ToLongDateString(); - } - else - { - value = string.Format("{0} - {1}", _owner.SelectionStart.ToLongDateString(), _owner.SelectionEnd.ToLongDateString()); + return _owner.SelectionStart.ToLongDateString(); } + + return string.Format("{0} - {1}", _owner.SelectionStart.ToLongDateString(), _owner.SelectionEnd.ToLongDateString()); } - else if (_owner._mcCurView == MCMV.YEAR) + + if (_owner._mcCurView == MCMV.YEAR) { if (System.DateTime.Equals(_owner.SelectionStart.Month, _owner.SelectionEnd.Month)) { - value = _owner.SelectionStart.ToString("y"); + return _owner.SelectionStart.ToString("y"); } - else - { - value = string.Format("{0} - {1}", _owner.SelectionStart.ToString("y"), _owner.SelectionEnd.ToString("y")); - } - } - else - { - value = string.Format("{0} - {1}", _owner.SelectionRange.Start.ToString(), _owner.SelectionRange.End.ToString()); + + return string.Format("{0} - {1}", _owner.SelectionStart.ToString("y"), _owner.SelectionEnd.ToString("y")); } + + return string.Format("{0} - {1}", _owner.SelectionRange.Start.ToString(), _owner.SelectionRange.End.ToString()); } catch { - value = base.Value; + return base.Value; } - - return value; } set => base.Value = value; } @@ -195,6 +178,7 @@ internal override int ColumnCount int columnCount = 0; bool success = true; + while (success) { success = GetCalendarGridInfo( @@ -236,6 +220,7 @@ internal override int RowCount int rowCount = 0; bool success = true; + while (success) { success = GetCalendarGridInfo( @@ -271,6 +256,7 @@ internal override UiaCore.IRawElementProviderFragment ElementProviderFromPoint(d int innerY = (int)y; MCHITTESTINFO hitTestInfo = GetHitTestInfo(innerX, innerY); + switch ((MCHT)hitTestInfo.uHit) { case MCHT.TITLEBTNPREV: @@ -289,7 +275,7 @@ internal override UiaCore.IRawElementProviderFragment ElementProviderFromPoint(d case MCHT.CALENDARDATE: // Get calendar body's child. CalendarBodyAccessibleObject calendarBodyAccessibleObject = (CalendarBodyAccessibleObject)GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarBody); - return calendarBodyAccessibleObject.GetFromPoint(hitTestInfo); + return calendarBodyAccessibleObject?.GetFromPoint(hitTestInfo); case MCHT.TODAYLINK: return GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.TodayLink); @@ -361,7 +347,8 @@ public string GetCalendarChildName(int calendarIndex, CalendarChildType calendar private CalendarCellAccessibleObject GetCalendarCell(int calendarIndex, AccessibleObject parentAccessibleObject, int columnIndex) { - if (columnIndex < 0 || + if (parentAccessibleObject == null || + columnIndex < 0 || columnIndex >= MAX_DAYS || columnIndex >= ColumnCount) { @@ -379,12 +366,16 @@ private CalendarCellAccessibleObject GetCalendarCell(int calendarIndex, Accessib out Kernel32.SYSTEMTIME systemEndDate, out Kernel32.SYSTEMTIME systemStartDate); - DateTime endDate = DateTimePicker.SysTimeToDateTime(systemEndDate).Date; - DateTime startDate = DateTimePicker.SysTimeToDateTime(systemStartDate).Date; - if (getNameResult && !string.IsNullOrEmpty(text)) { - string cellName = GetCalendarCellName(endDate, startDate, text, rowIndex == -1); + string cellName = string.Empty; + + if (getDateResult) + { + DateTime endDate = DateTimePicker.SysTimeToDateTime(systemEndDate).Date; + DateTime startDate = DateTimePicker.SysTimeToDateTime(systemStartDate).Date; + cellName = GetCalendarCellName(endDate, startDate, text, rowIndex == -1); + } // The cell is present on the calendar, so create accessible object for it. return new CalendarCellAccessibleObject(this, calendarIndex, parentAccessibleObject, rowIndex, columnIndex, cellName); @@ -414,7 +405,8 @@ private string GetCalendarCellName(DateTime endDate, DateTime startDate, string private CalendarRowAccessibleObject GetCalendarRow(int calendarIndex, AccessibleObject parentAccessibleObject, int rowIndex) { - if ((HasHeaderRow ? rowIndex < -1 : rowIndex < 0) || + if (parentAccessibleObject == null || + (HasHeaderRow ? rowIndex < -1 : rowIndex < 0) || rowIndex >= RowCount) { return null; @@ -439,7 +431,7 @@ private CalendarRowAccessibleObject GetCalendarRow(int calendarIndex, Accessible SelectionRange cellsRange = _owner.GetDisplayRange(false); - if (cellsRange.Start > DateTimePicker.SysTimeToDateTime(endDate) || cellsRange.End < DateTimePicker.SysTimeToDateTime(startDate)) + if (cellsRange == null || cellsRange.Start > DateTimePicker.SysTimeToDateTime(endDate) || cellsRange.End < DateTimePicker.SysTimeToDateTime(startDate)) { // Do not create row if the row's first cell is out of the current calendar's view range. return null; @@ -648,6 +640,7 @@ private unsafe void SendMouseInput(int x, int y, User32.MOUSEEVENTF flags) public void RaiseAutomationEventForChild(UiaCore.UIA automationEventId, DateTime selectionStart, DateTime selectionEnd) { AccessibleObject calendarChildAccessibleObject = GetCalendarChildAccessibleObject(selectionStart, selectionEnd); + if (calendarChildAccessibleObject != null) { calendarChildAccessibleObject.RaiseAutomationEvent(automationEventId); @@ -661,13 +654,23 @@ public void RaiseAutomationEventForChild(UiaCore.UIA automationEventId, DateTime private AccessibleObject GetCalendarChildAccessibleObject(DateTime selectionStart, DateTime selectionEnd) { - int columnCount = ColumnCount; - AccessibleObject bodyAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarBody); + + if (bodyAccessibleObject == null) + { + return null; + } + for (int row = 0; row < RowCount; row++) { AccessibleObject rowAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, bodyAccessibleObject, row); - for (int column = 0; column < columnCount; column++) + + if (rowAccessibleObject == null) + { + continue; + } + + for (int column = 0; column < ColumnCount; column++) { bool success = GetCalendarGridInfo( MCGIF.DATE, @@ -685,6 +688,7 @@ private AccessibleObject GetCalendarChildAccessibleObject(DateTime selectionStar } AccessibleObject cellAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, rowAccessibleObject, column); + if (cellAccessibleObject == null) { continue; @@ -716,6 +720,12 @@ internal override UiaCore.IRawElementProviderSimple[] GetColumnHeaderItems() UiaCore.IRawElementProviderSimple[] headers = new UiaCore.IRawElementProviderSimple[MonthCalendarAccessibleObject.MAX_DAYS]; AccessibleObject headerRowAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, this, -1); + + if (headerRowAccessibleObject == null) + { + return null; + } + for (int columnIndex = 0; columnIndex < MonthCalendarAccessibleObject.MAX_DAYS; columnIndex++) { headers[columnIndex] = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, headerRowAccessibleObject, columnIndex); From 6c8a987f974f5aef18d493d6597b7dd98409e938 Mon Sep 17 00:00:00 2001 From: Vladimir Krestov Date: Wed, 6 May 2020 15:30:59 +0300 Subject: [PATCH 2/3] Added unit and maui tests to check MonthCalendarAccessibleObject --- Winforms.sln | 14 ++ .../src/Properties/InternalsVisibleTo.cs | 2 + .../src/Properties/InternalsVisibleTo.cs | 1 + .../MauiListViewTests/MauiListViewTests.cs | 107 +------- .../MauiListViewTests.csproj | 4 + .../MauiMonthCalendarTests.cs | 238 ++++++++++++++++++ .../MauiMonthCalendarTests.csproj | 17 ++ .../MauiTestsHelper/KeyboardHelper.cs | 30 +++ .../MauiTestsHelper/MauiTestsHelper.csproj | 13 + .../MauiTests/MauiTestsHelper/MouseHelper.cs | 91 +++++++ .../WinformsMauiMonthCalendarTests.cs | 37 +++ ...endar.CalendarBodyAccessibleObjectTests.cs | 30 +++ ...ndar.CalendarChildAccessibleObjectTests.cs | 25 ++ ....CalendarGridChildAccessibleObjectTests.cs | 36 +++ ...dar.CalendarHeaderAccessibleObjectTests.cs | 4 +- ...ndar.MonthCalendarAccessibleObjectTests.cs | 41 +++ .../Windows/Forms/DateTimePickerTests.cs | 16 ++ 17 files changed, 602 insertions(+), 104 deletions(-) create mode 100644 src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiMonthCalendarTests/MauiMonthCalendarTests.cs create mode 100644 src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiMonthCalendarTests/MauiMonthCalendarTests.csproj create mode 100644 src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/KeyboardHelper.cs create mode 100644 src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/MauiTestsHelper.csproj create mode 100644 src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/MouseHelper.cs create mode 100644 src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.Maui.IntegrationTests/WinformsMauiMonthCalendarTests.cs create mode 100644 src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarBodyAccessibleObjectTests.cs create mode 100644 src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarChildAccessibleObjectTests.cs create mode 100644 src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarGridChildAccessibleObjectTests.cs create mode 100644 src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.MonthCalendarAccessibleObjectTests.cs diff --git a/Winforms.sln b/Winforms.sln index 45a088b1913..03304b28b9a 100644 --- a/Winforms.sln +++ b/Winforms.sln @@ -126,6 +126,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Windows.Forms.Design.Editors.Facade3x", "src\System.Windows.Forms.Design.Editors\src\System.Windows.Forms.Design.Editors.Facade3x.csproj", "{E0681991-228A-420E-85D5-A9E796F0AAE0}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiMonthCalendarTests", "src\System.Windows.Forms\tests\IntegrationTests\MauiTests\MauimonthCalendarTests\MauiMonthCalendarTests.csproj", "{9DDC6936-9197-4C09-8640-AF0BE4918700}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiTestsHelper", "src\System.Windows.Forms\tests\IntegrationTests\MauiTests\MauiTestsHelper\MauiTestsHelper.csproj", "{44BB1092-1844-4EAF-8DF5-338DE4C3149A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -248,6 +252,14 @@ Global {E0681991-228A-420E-85D5-A9E796F0AAE0}.Debug|Any CPU.Build.0 = Debug|Any CPU {E0681991-228A-420E-85D5-A9E796F0AAE0}.Release|Any CPU.ActiveCfg = Release|Any CPU {E0681991-228A-420E-85D5-A9E796F0AAE0}.Release|Any CPU.Build.0 = Release|Any CPU + {9DDC6936-9197-4C09-8640-AF0BE4918700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9DDC6936-9197-4C09-8640-AF0BE4918700}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9DDC6936-9197-4C09-8640-AF0BE4918700}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9DDC6936-9197-4C09-8640-AF0BE4918700}.Release|Any CPU.Build.0 = Release|Any CPU + {44BB1092-1844-4EAF-8DF5-338DE4C3149A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44BB1092-1844-4EAF-8DF5-338DE4C3149A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44BB1092-1844-4EAF-8DF5-338DE4C3149A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44BB1092-1844-4EAF-8DF5-338DE4C3149A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -287,6 +299,8 @@ Global {90B27178-F535-43F7-886E-0AB75203F246} = {77FEDB47-F7F6-490D-AF7C-ABB4A9E0B9D7} {9BFDE7F2-C8F3-40D6-9A16-8DCD1A37E900} = {583F1292-AE8D-4511-B8D8-A81FE4642DDC} {E0681991-228A-420E-85D5-A9E796F0AAE0} = {434C00C3-E498-4BA7-9764-9F0FC8CFE457} + {9DDC6936-9197-4C09-8640-AF0BE4918700} = {8F20A905-BD37-4D80-B8DF-FA45276FC23F} + {44BB1092-1844-4EAF-8DF5-338DE4C3149A} = {8F20A905-BD37-4D80-B8DF-FA45276FC23F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7B1B0433-F612-4E5A-BE7E-FCF5B9F6E136} diff --git a/src/System.Windows.Forms.Primitives/src/Properties/InternalsVisibleTo.cs b/src/System.Windows.Forms.Primitives/src/Properties/InternalsVisibleTo.cs index b125d59a9ad..02ca6491933 100644 --- a/src/System.Windows.Forms.Primitives/src/Properties/InternalsVisibleTo.cs +++ b/src/System.Windows.Forms.Primitives/src/Properties/InternalsVisibleTo.cs @@ -16,6 +16,8 @@ [assembly: InternalsVisibleTo("MauiListViewTests, PublicKey=00000000000000000400000000000000")] [assembly: InternalsVisibleTo("System.Windows.Forms.IntegrationTests.Common, PublicKey=00000000000000000400000000000000")] [assembly: InternalsVisibleTo("System.Windows.Forms.Maui.IntegrationTests, PublicKey=00000000000000000400000000000000")] +[assembly: InternalsVisibleTo("MauiMonthCalendarTests, PublicKey=00000000000000000400000000000000")] +[assembly: InternalsVisibleTo("MauiTestsHelper, PublicKey=00000000000000000400000000000000")] // This is needed in order to Moq internal interfaces for testing [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/System.Windows.Forms/src/Properties/InternalsVisibleTo.cs b/src/System.Windows.Forms/src/Properties/InternalsVisibleTo.cs index 7bbe51eeb5e..0cd9da4ab7f 100644 --- a/src/System.Windows.Forms/src/Properties/InternalsVisibleTo.cs +++ b/src/System.Windows.Forms/src/Properties/InternalsVisibleTo.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("System.Windows.Forms.Tests, PublicKey=00000000000000000400000000000000")] +[assembly: InternalsVisibleTo("MauiMonthCalendarTests, PublicKey=00000000000000000400000000000000")] // This is needed in order to Moq internal interfaces for testing [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiListViewTests/MauiListViewTests.cs b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiListViewTests/MauiListViewTests.cs index 71088f38a1e..a52cb333f6a 100644 --- a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiListViewTests/MauiListViewTests.cs +++ b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiListViewTests/MauiListViewTests.cs @@ -46,15 +46,15 @@ public ScenarioResult Click_On_Second_Column_Does_Not_Alter_Checkboxes(TParams p return new ScenarioResult(false, "Precondition failed: all items must be unselected"); } - SendKey(Keys.ShiftKey, true); + KeyboardHelper.SendKey(Keys.ShiftKey, true); - var pt = GetCenter(_listView.RectangleToScreen(_listView.Items[0].SubItems[1].Bounds)); - MouseHelper.RaiseMouseClick(pt.x, pt.y); + var pt = MouseHelper.GetCenter(_listView.RectangleToScreen(_listView.Items[0].SubItems[1].Bounds)); + MouseHelper.SendClick(pt.x, pt.y); - pt = GetCenter(_listView.RectangleToScreen(_listView.Items[2].SubItems[1].Bounds)); - MouseHelper.RaiseMouseClick(pt.x, pt.y); + pt = MouseHelper.GetCenter(_listView.RectangleToScreen(_listView.Items[2].SubItems[1].Bounds)); + MouseHelper.SendClick(pt.x, pt.y); - SendKey(Keys.ShiftKey, false); + KeyboardHelper.SendKey(Keys.ShiftKey, false); Application.DoEvents(); foreach (ListViewItem item in _listView.Items) @@ -72,29 +72,6 @@ public ScenarioResult Click_On_Second_Column_Does_Not_Alter_Checkboxes(TParams p return new ScenarioResult(true); } - private unsafe void SendKey(Keys key, bool down) - { - var input = new User32.INPUT(); - input.type = User32.INPUTENUM.KEYBOARD; - input.inputUnion.ki.wVk = (ushort)key; - input.inputUnion.ki.dwFlags = 0; - input.inputUnion.ki.dwExtraInfo = IntPtr.Zero; - input.inputUnion.ki.time = 0; - input.inputUnion.ki.wScan = 0; - - if (!down) - input.inputUnion.ki.dwFlags |= User32.KEYEVENTF.KEYUP; - - User32.SendInput(1, &input, Marshal.SizeOf(input)); - } - - private (int x, int y) GetCenter(System.Drawing.Rectangle rect) - { - int x = rect.Left + ((rect.Right - rect.Left) / 2); - int y = rect.Top + ((rect.Bottom - rect.Top) / 2); - return (x, y); - } - private void InitializeItems(ListView listView, TParams p) { listView.Items.Clear(); @@ -113,76 +90,4 @@ private void InitializeItems(ListView listView, TParams p) listView.Items.AddRange(new[] { listViewItem1, listViewItem2, listViewItem3 }); } } - - internal static class MouseHelper - { - public static void RaiseMouseClick(int x, int y) - { - var previousPosition = new Point(); - BOOL setOldCursorPos = User32.GetPhysicalCursorPos(ref previousPosition); - - bool mouseSwapped = User32.GetSystemMetrics(User32.SystemMetric.SM_SWAPBUTTON) != 0; - - SendMouseInput(x, y, User32.MOUSEEVENTF.MOVE | User32.MOUSEEVENTF.ABSOLUTE); - SendMouseInput(0, 0, mouseSwapped ? User32.MOUSEEVENTF.RIGHTDOWN : User32.MOUSEEVENTF.LEFTDOWN); - SendMouseInput(0, 0, mouseSwapped ? User32.MOUSEEVENTF.RIGHTUP : User32.MOUSEEVENTF.LEFTUP); - - Threading.Thread.Sleep(50); - - // Set back the mouse position where it was. - if (setOldCursorPos.IsTrue()) - { - SendMouseInput(previousPosition.X, previousPosition.Y, User32.MOUSEEVENTF.MOVE | User32.MOUSEEVENTF.ABSOLUTE); - } - } - - private unsafe static void SendMouseInput(int x, int y, User32.MOUSEEVENTF flags) - { - if ((flags & User32.MOUSEEVENTF.ABSOLUTE) != 0) - { - int vscreenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXVIRTUALSCREEN); - int vscreenHeight = User32.GetSystemMetrics(User32.SystemMetric.SM_CYVIRTUALSCREEN); - int vscreenLeft = User32.GetSystemMetrics(User32.SystemMetric.SM_XVIRTUALSCREEN); - int vscreenTop = User32.GetSystemMetrics(User32.SystemMetric.SM_YVIRTUALSCREEN); - - const int DesktopNormilizedMax = 65536; - - // Absolute input requires that input is in 'normalized' coords - with the entire - // desktop being (0,0)...(65535,65536). Need to convert input x,y coords to this - // first. - // - // In this normalized world, any pixel on the screen corresponds to a block of values - // of normalized coords - eg. on a 1024x768 screen, - // y pixel 0 corresponds to range 0 to 85.333, - // y pixel 1 corresponds to range 85.333 to 170.666, - // y pixel 2 correpsonds to range 170.666 to 256 - and so on. - // Doing basic scaling math - (x-top)*65536/Width - gets us the start of the range. - // However, because int math is used, this can end up being rounded into the wrong - // pixel. For example, if we wanted pixel 1, we'd get 85.333, but that comes out as - // 85 as an int, which falls into pixel 0's range - and that's where the pointer goes. - // To avoid this, we add on half-a-"screen pixel"'s worth of normalized coords - to - // push us into the middle of any given pixel's range - that's the 65536/(Width*2) - // part of the formula. So now pixel 1 maps to 85+42 = 127 - which is comfortably - // in the middle of that pixel's block. - // The key ting here is that unlike points in coordinate geometry, pixels take up - // space, so are often better treated like rectangles - and if you want to target - // a particular pixel, target its rectangle's midpoint, not its edge. - x = ((x - vscreenLeft) * DesktopNormilizedMax) / vscreenWidth + DesktopNormilizedMax / (vscreenWidth * 2); - y = ((y - vscreenTop) * DesktopNormilizedMax) / vscreenHeight + DesktopNormilizedMax / (vscreenHeight * 2); - - flags |= User32.MOUSEEVENTF.VIRTUALDESK; - } - - var mouseInput = new User32.INPUT(); - mouseInput.type = User32.INPUTENUM.MOUSE; - mouseInput.inputUnion.mi.dx = x; - mouseInput.inputUnion.mi.dy = y; - mouseInput.inputUnion.mi.mouseData = 0; - mouseInput.inputUnion.mi.dwFlags = flags; - mouseInput.inputUnion.mi.time = 0; - mouseInput.inputUnion.mi.dwExtraInfo = IntPtr.Zero; - - User32.SendInput(1, &mouseInput, Marshal.SizeOf(mouseInput)); - } - } } diff --git a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiListViewTests/MauiListViewTests.csproj b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiListViewTests/MauiListViewTests.csproj index e5086425586..1012b409b1c 100644 --- a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiListViewTests/MauiListViewTests.csproj +++ b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiListViewTests/MauiListViewTests.csproj @@ -10,4 +10,8 @@ Exe + + + + \ No newline at end of file diff --git a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiMonthCalendarTests/MauiMonthCalendarTests.cs b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiMonthCalendarTests/MauiMonthCalendarTests.cs new file mode 100644 index 00000000000..6dc57c6de52 --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiMonthCalendarTests/MauiMonthCalendarTests.cs @@ -0,0 +1,238 @@ +// 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.Threading; +using WFCTestLib.Log; +using ReflectTools; +using System.Windows.Forms.IntegrationTests.Common; +using static Interop.Kernel32; +using static Interop.ComCtl32; +using static Interop.User32; +using System.Drawing; +using static System.Windows.Forms.MonthCalendar; +using System.Reflection; +using static Interop.UiaCore; + +namespace System.Windows.Forms.IntegrationTests.MauiTests +{ + public class MauiMonthCalendarTests : ReflectBase + { + private struct MonthCalendarWrapper : IDisposable + { + private ReflectBase _container; + + public MonthCalendarWrapper(ReflectBase container) + { + _container = container; + Calendar = new MonthCalendar(); + _container.Controls.Add(Calendar); + } + + public MonthCalendar Calendar { get; } + + public void Dispose() + { + _container.Controls.Remove(Calendar); + Calendar.Dispose(); + } + } + public MauiMonthCalendarTests(string[] args) : base(args) + { + this.BringToForeground(); + } + + public static void Main(string[] args) + { + Thread.CurrentThread.SetCulture("en-US"); + Application.Run(new MauiMonthCalendarTests(args)); + } + + [Scenario(true)] + public ScenarioResult MonthCalendar_SettingDate_DoesntCrashApplication_IfUseMouse(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + wrapper.Calendar.MinDate = new DateTime(2020, 4, 9); + wrapper.Calendar.MaxDate = new DateTime(2020, 4, 27); + wrapper.Calendar.SetDate(new DateTime(2020, 4, 14)); + Application.DoEvents(); + + Point position = wrapper.Calendar.PointToScreen(new Point(82, 102)); + MouseHelper.SendClick(position.X, position.Y); + Application.DoEvents(); + + return new ScenarioResult(true); + } + + [Scenario(true)] + public ScenarioResult MonthCalendar_SettingDate_DoesntCrashApplication_IfUseKeyboard(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + wrapper.Calendar.MinDate = new DateTime(2020, 4, 9); + wrapper.Calendar.MaxDate = new DateTime(2020, 4, 27); + wrapper.Calendar.SetDate(new DateTime(2020, 4, 14)); + Application.DoEvents(); + + KeyboardHelper.SendKey(Keys.Right, true); + Application.DoEvents(); + + KeyboardHelper.SendKey(Keys.Right, true); + Application.DoEvents(); + + return new ScenarioResult(true); + } + + [Scenario(true)] + public ScenarioResult MonthCalendar_SettingDate_DoesntCrashApplication_Programmatically(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + wrapper.Calendar.MinDate = new DateTime(2020, 4, 9); + wrapper.Calendar.MaxDate = new DateTime(2020, 4, 27); + wrapper.Calendar.SetDate(new DateTime(2020, 4, 14)); + Application.DoEvents(); + + DateTime selectedDate = new DateTime(2020, 4, 10); + + SYSTEMTIME date = new SYSTEMTIME + { + wYear = (short)selectedDate.Year, + wMonth = (short)selectedDate.Month, + wDay = (short)selectedDate.Day + }; + + if (IntPtr.Zero == wrapper.Calendar.Handle) + { + return new ScenarioResult(false); + } + + NMSELCHANGE lParam = new NMSELCHANGE + { + nmhdr = new NMHDR + { + code = (int)MCN.SELCHANGE, + }, + stSelStart = date, + stSelEnd = date, + }; + + SendMessageW(wrapper.Calendar.Handle, WM.REFLECT | WM.NOTIFY, IntPtr.Zero, ref lParam); + + return new ScenarioResult(true); + } + + [Scenario(true)] + public ScenarioResult MonthCalendar_GetFromPoint_ReturnsCorrectValue(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + Application.DoEvents(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)wrapper.Calendar.AccessibilityObject; + CalendarBodyAccessibleObject bodyAccessibleObject = new CalendarBodyAccessibleObject(accessibleObject, 0); + MCHITTESTINFO info = new MCHITTESTINFO + { + uHit = MCHT.CALENDARDAY, + iRow = 0 + }; + Application.DoEvents(); + CalendarChildAccessibleObject cell = bodyAccessibleObject.GetFromPoint(info); + + return new ScenarioResult(cell != null); + } + + [Scenario(true)] + public ScenarioResult CalendarBodyAccessibleObject_GetFromPoint_ReturnsNull_IfCalendarIndexIsIncorrect(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + Application.DoEvents(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)wrapper.Calendar.AccessibilityObject; + CalendarBodyAccessibleObject bodyAccessibleObject = new CalendarBodyAccessibleObject(accessibleObject, -10); + MCHITTESTINFO info = new MCHITTESTINFO + { + uHit = MCHT.CALENDARDAY, + iRow = 0 + }; + Application.DoEvents(); + CalendarChildAccessibleObject cell = bodyAccessibleObject.GetFromPoint(info); + + return new ScenarioResult(cell == null); + } + + [Scenario(true)] + public ScenarioResult CalendarBodyAccessibleObject_GetFromPoint_ReturnsNull_IfMCHITTESTINFOIsIncorrect(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + Application.DoEvents(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)wrapper.Calendar.AccessibilityObject; + CalendarBodyAccessibleObject bodyAccessibleObject = new CalendarBodyAccessibleObject(accessibleObject, 0); + MCHITTESTINFO info = new MCHITTESTINFO + { + uHit = MCHT.CALENDARDAY, + iRow = -10 + }; + Application.DoEvents(); + CalendarChildAccessibleObject cell = bodyAccessibleObject.GetFromPoint(info); + + return new ScenarioResult(cell == null); + } + + [Scenario(true)] + public ScenarioResult MonthCalendarAccessibleObject_GetCalendarChildAccessibleObject_ReturnsCorrecObject(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + DateTime selectedDate = new DateTime(2020, 4, 10); + wrapper.Calendar.SetDate(selectedDate); + Application.DoEvents(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)wrapper.Calendar.AccessibilityObject; + Type type = typeof(MonthCalendarAccessibleObject); + MethodInfo method = type.GetMethod("GetCalendarChildAccessibleObject", BindingFlags.NonPublic | BindingFlags.Instance); + + Application.DoEvents(); + object child = method.Invoke(accessibleObject, new object[] { selectedDate, selectedDate }); + + return new ScenarioResult(child != null); + } + + [Scenario(true)] + public ScenarioResult MonthCalendarAccessibleObject_GetCalendarChildAccessibleObject_ReturnsNull_IfCalendarIndexIsIncorrect(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + Application.DoEvents(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)wrapper.Calendar.AccessibilityObject; + Type type = typeof(MonthCalendarAccessibleObject); + type.GetField("_calendarIndex", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(accessibleObject, -1); + MethodInfo method = type.GetMethod("GetCalendarChildAccessibleObject", BindingFlags.NonPublic | BindingFlags.Instance); + + Application.DoEvents(); + object child = method.Invoke(accessibleObject, new object[] { new DateTime(2020, 4, 10), new DateTime(2020, 4, 10) }); + + return new ScenarioResult(child == null); + } + + [Scenario(true)] + public ScenarioResult MonthCalendarAccessibleObject_GetColumnHeaderItems_ReturnsCorrectCollection(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + Application.DoEvents(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)wrapper.Calendar.AccessibilityObject; + + Application.DoEvents(); + IRawElementProviderSimple[] items = accessibleObject.GetColumnHeaderItems(); + + return new ScenarioResult(items != null); + } + + [Scenario(true)] + public ScenarioResult MonthCalendarAccessibleObject_GetColumnHeaderItems_ReturnsNull_IfCalendarIndexIsIncorrect(TParams p) + { + using var wrapper = new MonthCalendarWrapper(this); + Application.DoEvents(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)wrapper.Calendar.AccessibilityObject; + Type type = typeof(MonthCalendarAccessibleObject); + type.GetField("_calendarIndex", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(accessibleObject, -1); + + Application.DoEvents(); + IRawElementProviderSimple[] items = accessibleObject.GetColumnHeaderItems(); + + return new ScenarioResult(items == null); + } + } +} diff --git a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiMonthCalendarTests/MauiMonthCalendarTests.csproj b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiMonthCalendarTests/MauiMonthCalendarTests.csproj new file mode 100644 index 00000000000..1012b409b1c --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiMonthCalendarTests/MauiMonthCalendarTests.csproj @@ -0,0 +1,17 @@ + + + + + + true + + + + Exe + + + + + + + \ No newline at end of file diff --git a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/KeyboardHelper.cs b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/KeyboardHelper.cs new file mode 100644 index 00000000000..623bb1ffbbc --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/KeyboardHelper.cs @@ -0,0 +1,30 @@ +// 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.Runtime.InteropServices; +using System.Windows.Forms; +using static Interop.User32; + +namespace System.Windows.Forms.IntegrationTests.MauiTests +{ + public static class KeyboardHelper + { + public unsafe static void SendKey(Keys key, bool down) + { + var input = new INPUT(); + input.type = INPUTENUM.KEYBOARD; + input.inputUnion.ki.wVk = (ushort)key; + input.inputUnion.ki.dwFlags = 0; + input.inputUnion.ki.dwExtraInfo = IntPtr.Zero; + input.inputUnion.ki.time = 0; + input.inputUnion.ki.wScan = 0; + + if (!down) + input.inputUnion.ki.dwFlags |= KEYEVENTF.KEYUP; + + SendInput(1, &input, Marshal.SizeOf(input)); + } + } +} diff --git a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/MauiTestsHelper.csproj b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/MauiTestsHelper.csproj new file mode 100644 index 00000000000..a99ef20d426 --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/MauiTestsHelper.csproj @@ -0,0 +1,13 @@ + + + + + + true + + + + Library + + + diff --git a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/MouseHelper.cs b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/MouseHelper.cs new file mode 100644 index 00000000000..dfab320a1c4 --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/MauiTestsHelper/MouseHelper.cs @@ -0,0 +1,91 @@ +// 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.Drawing; +using System.Runtime.InteropServices; +using System.Threading; +using static Interop; +using static Interop.User32; + +namespace System.Windows.Forms.IntegrationTests.MauiTests +{ + public static class MouseHelper + { + public static void SendClick(int x, int y) + { + var previousPosition = new Point(); + BOOL setOldCursorPos = GetPhysicalCursorPos(ref previousPosition); + + bool mouseSwapped = GetSystemMetrics(SystemMetric.SM_SWAPBUTTON) != 0; + + SendMouseInput(x, y, MOUSEEVENTF.MOVE | MOUSEEVENTF.ABSOLUTE); + SendMouseInput(0, 0, mouseSwapped ? MOUSEEVENTF.RIGHTDOWN : MOUSEEVENTF.LEFTDOWN); + SendMouseInput(0, 0, mouseSwapped ? MOUSEEVENTF.RIGHTUP : MOUSEEVENTF.LEFTUP); + + Thread.Sleep(50); + + // Set back the mouse position where it was. + if (setOldCursorPos.IsTrue()) + { + SendMouseInput(previousPosition.X, previousPosition.Y, MOUSEEVENTF.MOVE | MOUSEEVENTF.ABSOLUTE); + } + } + + private unsafe static void SendMouseInput(int x, int y, MOUSEEVENTF flags) + { + if ((flags & MOUSEEVENTF.ABSOLUTE) != 0) + { + int vscreenWidth = GetSystemMetrics(SystemMetric.SM_CXVIRTUALSCREEN); + int vscreenHeight = GetSystemMetrics(SystemMetric.SM_CYVIRTUALSCREEN); + int vscreenLeft = GetSystemMetrics(SystemMetric.SM_XVIRTUALSCREEN); + int vscreenTop = GetSystemMetrics(SystemMetric.SM_YVIRTUALSCREEN); + + const int DesktopNormilizedMax = 65536; + + // Absolute input requires that input is in 'normalized' coords - with the entire + // desktop being (0,0)...(65535,65536). Need to convert input x,y coords to this + // first. + // + // In this normalized world, any pixel on the screen corresponds to a block of values + // of normalized coords - eg. on a 1024x768 screen, + // y pixel 0 corresponds to range 0 to 85.333, + // y pixel 1 corresponds to range 85.333 to 170.666, + // y pixel 2 correpsonds to range 170.666 to 256 - and so on. + // Doing basic scaling math - (x-top)*65536/Width - gets us the start of the range. + // However, because int math is used, this can end up being rounded into the wrong + // pixel. For example, if we wanted pixel 1, we'd get 85.333, but that comes out as + // 85 as an int, which falls into pixel 0's range - and that's where the pointer goes. + // To avoid this, we add on half-a-"screen pixel"'s worth of normalized coords - to + // push us into the middle of any given pixel's range - that's the 65536/(Width*2) + // part of the formula. So now pixel 1 maps to 85+42 = 127 - which is comfortably + // in the middle of that pixel's block. + // The key ting here is that unlike points in coordinate geometry, pixels take up + // space, so are often better treated like rectangles - and if you want to target + // a particular pixel, target its rectangle's midpoint, not its edge. + x = ((x - vscreenLeft) * DesktopNormilizedMax) / vscreenWidth + DesktopNormilizedMax / (vscreenWidth * 2); + y = ((y - vscreenTop) * DesktopNormilizedMax) / vscreenHeight + DesktopNormilizedMax / (vscreenHeight * 2); + + flags |= MOUSEEVENTF.VIRTUALDESK; + } + + var mouseInput = new INPUT(); + mouseInput.type = INPUTENUM.MOUSE; + mouseInput.inputUnion.mi.dx = x; + mouseInput.inputUnion.mi.dy = y; + mouseInput.inputUnion.mi.mouseData = 0; + mouseInput.inputUnion.mi.dwFlags = flags; + mouseInput.inputUnion.mi.time = 0; + mouseInput.inputUnion.mi.dwExtraInfo = IntPtr.Zero; + + SendInput(1, &mouseInput, Marshal.SizeOf(mouseInput)); + } + + public static (int x, int y) GetCenter(System.Drawing.Rectangle rect) + { + int x = rect.Left + ((rect.Right - rect.Left) / 2); + int y = rect.Top + ((rect.Bottom - rect.Top) / 2); + return (x, y); + } + } +} diff --git a/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.Maui.IntegrationTests/WinformsMauiMonthCalendarTests.cs b/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.Maui.IntegrationTests/WinformsMauiMonthCalendarTests.cs new file mode 100644 index 00000000000..2dd256a9efb --- /dev/null +++ b/src/System.Windows.Forms/tests/IntegrationTests/System.Windows.Forms.Maui.IntegrationTests/WinformsMauiMonthCalendarTests.cs @@ -0,0 +1,37 @@ +// 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 Xunit; + +namespace System.Windows.Forms.Maui.IntegrationTests +{ + /// + /// This class runs a maui executable, which contains one or more scenarios. + /// + /// We want to be able to represent each scenario as a seperate xUnit test, but it's not + /// possible to run them independently. The workaround is to have a MauiTestRunner execute all + /// the scenarios once and store the results, then feed the scenario names in as member data. + /// + /// However, MemberData is resolved before any constructors (even static) are called. + /// This means the scenario names will not be available yet. + /// + /// The solution to this is to inherit from MemberDataAttribute and execute custom code + /// (running the maui test) before returning the expected data. See MauiMemberDataAttribute.cs for more info. + /// + /// Also [Collection("Maui")] is used put all maui tests in the same collection, which makes them run sequentially + /// instead of in parallel. This is to migitate race conditions of multiple forms open at once. + /// + [Collection("Maui")] + public class WinformsMauiMonthCalendarTests + { + private const string ProjectName = "MauiMonthCalendarTests"; + + [Theory] + [MauiData(ProjectName)] + public void MauiMonthCalendarTest(string scenarioName) + { + MauiTestHelper.ValidateScenarioPassed(ProjectName, scenarioName); + } + } +} diff --git a/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarBodyAccessibleObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarBodyAccessibleObjectTests.cs new file mode 100644 index 00000000000..c251d5ce079 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarBodyAccessibleObjectTests.cs @@ -0,0 +1,30 @@ +// 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.Threading; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; +using static System.Windows.Forms.MonthCalendar; +using static Interop.ComCtl32; + +namespace System.Windows.Forms.Tests.AccessibleObjects +{ + public class CalendarBodyAccessibleObjectTests : IClassFixture + { + [WinFormsFact] + public void CalendarBodyAccessibleObject_ctor_Default() + { + using MonthCalendar calendar = new MonthCalendar(); + MonthCalendarAccessibleObject calendarAccessibleObject = new MonthCalendarAccessibleObject(calendar); + CalendarBodyAccessibleObject bodyAccessibleObject = new CalendarBodyAccessibleObject(calendarAccessibleObject, 0); + Assert.Equal(calendarAccessibleObject, bodyAccessibleObject.Parent); + } + + [WinFormsFact] + public void CalendarBodyAccessibleObject_ctor_ThrowsException_IfCalendarAccessibleObjectIsNull() + { + Assert.Throws(() => new CalendarBodyAccessibleObject(null, 0)); + } + } +} diff --git a/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarChildAccessibleObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarChildAccessibleObjectTests.cs new file mode 100644 index 00000000000..bdedc3aa8f0 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarChildAccessibleObjectTests.cs @@ -0,0 +1,25 @@ +// 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 Xunit; +using static System.Windows.Forms.MonthCalendar; + +namespace System.Windows.Forms.Tests.AccessibleObjects +{ + public class CalendarChildAccessibleObjectTests : IClassFixture + { + [WinFormsFact] + public void CalendarChildAccessibleObject_ctor_ThrowsException_IfCalendarAccessibleObjectIsNull() + { + Assert.Throws(() => new SubObject(null, 0, CalendarChildType.CalendarBody)); + } + + private class SubObject : CalendarChildAccessibleObject + { + public SubObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, CalendarChildType itemType) + : base(calendarAccessibleObject, calendarIndex, itemType) + { } + } + } +} diff --git a/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarGridChildAccessibleObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarGridChildAccessibleObjectTests.cs new file mode 100644 index 00000000000..9d2befe4b56 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarGridChildAccessibleObjectTests.cs @@ -0,0 +1,36 @@ +// 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 Xunit; +using static System.Windows.Forms.MonthCalendar; + +namespace System.Windows.Forms.Tests.AccessibleObjects +{ + public class CalendarGridChildAccessibleObjectTests : IClassFixture + { + [WinFormsFact] + public void CalendarChildAccessibleObject_ctor_ThrowsException_IfCalendarAccessibleObjectIsNull() + { + Assert.Throws(() => new SubObject(null, 0, CalendarChildType.CalendarBody, new AccessibleObject(), 0)); + } + + [WinFormsFact] + public void CalendarChildAccessibleObject_ctor_ThrowsException_IfParentAccessibleObjectIsNull() + { + using MonthCalendar calendar = new MonthCalendar(); + MonthCalendarAccessibleObject calendarAccessibleObject = new MonthCalendarAccessibleObject(calendar); + Assert.Throws( + () => new SubObject(calendarAccessibleObject, 0, CalendarChildType.CalendarBody, null, 0)); + } + + private class SubObject : CalendarGridChildAccessibleObject + { + public SubObject(MonthCalendarAccessibleObject calendarAccessibleObject, + int calendarIndex, CalendarChildType itemType, + AccessibleObject parentAccessibleObject, + int itemIndex) : base(calendarAccessibleObject, calendarIndex, itemType, parentAccessibleObject, itemIndex) + { } + } + } +} diff --git a/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarHeaderAccessibleObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarHeaderAccessibleObjectTests.cs index c685f7ae6e9..da7df401eec 100644 --- a/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarHeaderAccessibleObjectTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.CalendarHeaderAccessibleObjectTests.cs @@ -1,10 +1,8 @@ -// 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 Xunit; -using static Interop; namespace System.Windows.Forms.Tests.AccessibleObjects { diff --git a/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.MonthCalendarAccessibleObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.MonthCalendarAccessibleObjectTests.cs new file mode 100644 index 00000000000..d7fe3e0955f --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/AccessibleObjects/MonthCalendar.MonthCalendarAccessibleObjectTests.cs @@ -0,0 +1,41 @@ +// 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.Reflection; +using System.Threading; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; +using static System.Windows.Forms.MonthCalendar; + +namespace System.Windows.Forms.Tests.AccessibleObjects +{ + public class MonthCalendarAccessibleObjectTests + { + [WinFormsFact] + public void MonthCalendarAccessibleObject_ctor_ThrowsException_IfOwnerIsNull() + { + Assert.Throws(() => new MonthCalendarAccessibleObject(null)); + } + + [WinFormsFact] + public void MonthCalendarAccessibleObject_GetCalendarCell_DoesntThrowException_If_ParentAccessibleObject_IsNull() + { + using MonthCalendar monthCalendar = new MonthCalendar(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)monthCalendar.AccessibilityObject; + Type type = typeof(MonthCalendarAccessibleObject); + MethodInfo method = type.GetMethod("GetCalendarCell", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.Null(method.Invoke(accessibleObject, new object[] { 0, /*parentAccessibleObject*/ null, 0 })); + } + + [WinFormsFact] + public void MonthCalendarAccessibleObject_GetCalendarRow_DoesntThrowException_If_ParentAccessibleObject_IsNull() + { + using MonthCalendar monthCalendar = new MonthCalendar(); + MonthCalendarAccessibleObject accessibleObject = (MonthCalendarAccessibleObject)monthCalendar.AccessibilityObject; + Type type = typeof(MonthCalendarAccessibleObject); + MethodInfo method = type.GetMethod("GetCalendarCell", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.Null(method.Invoke(accessibleObject, new object[] { 0, /*parentAccessibleObject*/ null, 0 })); + } + } +} diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DateTimePickerTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DateTimePickerTests.cs index 588e3396851..6e932ac96f4 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DateTimePickerTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DateTimePickerTests.cs @@ -7,6 +7,7 @@ using System.Windows.Forms.Design; using Xunit; using static Interop; +using static Interop.Kernel32; namespace System.Windows.Forms.Tests { @@ -185,6 +186,21 @@ public void DateTimePicker_GetTopLevel_Invoke_ReturnsExpected() Assert.False(control.GetTopLevel()); } + [WinFormsFact] + public void DateTimePicker_SysTimeToDateTime_DoesnThrowException_If_SYSTEMTIME_IsIncorrect() + { + // We expect to hit Debug.Fail in this test and unless we clear listeners we will crash to xUnit runner: + // "The active test run was aborted. Reason: Test host process crashed : Process terminated. Assertion failed." + using (new NoAssertContext()) + { + // An empty SYSTEMTIME has year, month and day as 0, but DateTime can't have these parameters. + // So an empty SYSTEMTIME is incorrect in this case. + SYSTEMTIME systemTime = new SYSTEMTIME(); + DateTime dateTime = DateTimePicker.SysTimeToDateTime(systemTime); + Assert.Equal(DateTime.MinValue, dateTime); + } + } + public class SubDateTimePicker : DateTimePicker { public new bool CanEnableIme => base.CanEnableIme; From 1635bc5ca700617147df4dee693920f7606aaf01 Mon Sep 17 00:00:00 2001 From: Vladimir Krestov Date: Sun, 10 May 2020 11:30:54 +0300 Subject: [PATCH 3/3] Fixed getting ColumnHeaderItems AccessibleObjects for MonthCalendar --- .../Forms/MonthCalendar.MonthCalendarAccessibleObject.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs index 3b87c35642a..cc3a544b161 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs @@ -719,7 +719,9 @@ internal override UiaCore.IRawElementProviderSimple[] GetColumnHeaderItems() UiaCore.IRawElementProviderSimple[] headers = new UiaCore.IRawElementProviderSimple[MonthCalendarAccessibleObject.MAX_DAYS]; - AccessibleObject headerRowAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, this, -1); + + AccessibleObject bodyAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarBody, this, -1); + AccessibleObject headerRowAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, bodyAccessibleObject, -1); if (headerRowAccessibleObject == null) {