terminal/src/host/ft_uia/WinEventTests.cs
pi1024e 2872f147f8
Remove unneeded whitespace (#5162)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Every single time a PR is run, there are a bunch of warnings about whitespace in the .cs files, so I ran the code format on those files, without changing their contents, so it won't be flagged anymore.
<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? --> 
## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [X] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [X] Tests added/passed

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Ran the code-format utility on the .cs files
2020-03-30 14:33:32 +00:00

432 lines
19 KiB
C#

//----------------------------------------------------------------------------------------------------------------------
// <copyright file="WinEventTests.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// </copyright>
// <summary>UI Automation tests for the Accessibility WinEvents feature.</summary>
//----------------------------------------------------------------------------------------------------------------------
namespace Conhost.UIA.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WEX.Common.Managed;
using WEX.Logging.Interop;
using WEX.TestExecution;
using WEX.TestExecution.Markup;
using Conhost.UIA.Tests.Common;
using Conhost.UIA.Tests.Common.NativeMethods;
using Conhost.UIA.Tests.Elements;
using OpenQA.Selenium;
using System.Threading;
using System.Runtime.InteropServices;
using System.Drawing;
// Test hooks adapted from C++ WinEvent accessibility sample at https://msdn.microsoft.com/en-us/library/ms971319.aspx#atg_consoleaccessibility_topic05
[TestClass]
public class WinEventTests : IWinEventCallbacks
{
public TestContext TestContext { get; set; }
enum EventType
{
CaretSelection,
CaretVisible,
EndApplication,
Layout,
StartApplication,
UpdateRegion,
UpdateScroll,
UpdateSimple
}
class EventData
{
EventType type;
// Of all the WinEvents that we return in the console, the max number of parameters returned is 4.
int[] data = new int[4];
public EventData(EventType type, int a = 0, int b = 0, int c = 0, int d = 0)
{
this.type = type;
data[0] = a;
data[1] = b;
data[2] = c;
data[3] = d;
}
public override bool Equals(object obj)
{
if (typeof(EventData) == obj.GetType())
{
return Equals((EventData)obj);
}
else
{
return base.Equals(obj);
}
}
public bool Equals(EventData other)
{
return type == other.type &&
data[0] == other.data[0] &&
data[1] == other.data[1] &&
data[2] == other.data[2] &&
data[3] == other.data[3];
}
public override string ToString()
{
return $"\r\nType: {type} Data[0]: {data[0]} Data[1]: {data[1]} Data[2]: {data[2]} Data[3]: {data[3]}\r\n";
}
public override int GetHashCode()
{
return type.GetHashCode() ^ data[0].GetHashCode() ^ data[1].GetHashCode() ^ data[2].GetHashCode() ^ data[3].GetHashCode();
}
}
Queue<EventData> received = new Queue<EventData>();
public void CaretSelection(int x, int y)
{
Log.Comment($"Event Console CARET SELECTION! X: {x} Y: {y}");
received.Enqueue(new EventData(EventType.CaretSelection, x, y));
}
public void CaretVisible(int x, int y)
{
Log.Comment($"Event Console CARET VISIBLE! X: {x} Y: {y}");
received.Enqueue(new EventData(EventType.CaretVisible, x, y));
}
public void EndApplication(int processId, int childId)
{
Log.Comment($"Event Console END APPLICATION! Process ID: {processId} - Child ID: {childId}");
received.Enqueue(new EventData(EventType.EndApplication)); // we don't have a great way of detecting pids now so don't store it for comparison
}
public void Layout()
{
Log.Comment("Event Console LAYOUT!");
received.Enqueue(new EventData(EventType.Layout));
}
public void StartApplication(int processId, int childId)
{
Log.Comment($"Event Console START APPLICATION! Process ID: {processId} - Child ID: {childId}");
received.Enqueue(new EventData(EventType.StartApplication)); // we don't have a great way of detecting pids now so don't store it for comparison
}
public void UpdateRegion(int left, int top, int right, int bottom)
{
Log.Comment($"Event Console UPDATE REGION! Left: {left} - Top: {top} - Right: {right} - Bottom: {bottom}");
received.Enqueue(new EventData(EventType.UpdateRegion, left, top, right, bottom));
}
public void UpdateScroll(int deltaX, int deltaY)
{
Log.Comment($"Event Console UPDATE SCROLL! dx: {deltaX} - dy: {deltaY}");
received.Enqueue(new EventData(EventType.UpdateScroll, deltaX, deltaY));
}
public void UpdateSimple(int x, int y, int character, int attribute)
{
Log.Comment($"Event Console UPDATE SIMPLE! X: {x} - Y: {y} - Char: {character} - Attr: {attribute}");
received.Enqueue(new EventData(EventType.UpdateSimple, x, y, character, attribute));
}
void VerifyQueue(Queue<EventData> testQueue)
{
while (testQueue.Count > 0)
{
Verify.AreEqual(testQueue.Dequeue(), received.Dequeue());
}
}
public void TestTypeStringHelper(string str, CmdApp app, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex)
{
Queue<EventData> expected = new Queue<EventData>();
foreach (char c in str)
{
// For each character, we expect a pair of messages.
// 1. A Update Simple where the cursor was to represent the letter inserted where the cursor was.
expected.Enqueue(new EventData(EventType.UpdateSimple, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y, c, (int)sbiex.wAttributes));
// Move cursor right by 1.
sbiex.dwCursorPosition.X++;
// 2. A caret visible to represent where the cursor moved after the letter was inserted.
expected.Enqueue(new EventData(EventType.CaretVisible, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
// Type in the letter
app.UIRoot.SendKeys($"{c}");
Globals.WaitForTimeout();
}
// Verify that we saw the right messages
this.VerifyQueue(expected);
}
delegate void AccessibilityTest(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue<EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal);
void RunTest(AccessibilityTest test)
{
using (RegistryHelper reg = new RegistryHelper())
{
reg.BackupRegistry();
using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
{
using (WinEventSystem sys = app.AttachWinEventSystem(this))
{
using (ViewportArea area = new ViewportArea(app))
{
Globals.WaitForTimeout(); // wait for everything to settle with winevents
IntPtr hConsole = app.GetStdOutHandle();
// Prep structures to hold cursor and size of buffer.
WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
sbiex.cbSize = (uint)Marshal.SizeOf(sbiex);
// this is where we will hold our expected messages
Queue<EventData> expected = new Queue<EventData>();
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Get initial console data.");
WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal = sbiex; // keep a copy of the original data for later.
// Run the test
test(app, area, hConsole, sbiex, expected, sbiexOriginal);
}
}
}
}
}
[TestMethod]
public void TestSelection()
{
RunTest(TestSelectionImpl);
}
private void TestSelectionImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue<EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
{
// A. Test single area click
{
// Move mouse pointer to where the cursor is
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
Point pt = new Point(sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y);
area.MouseMove(pt);
// Click on this area.
expected.Enqueue(new EventData(EventType.CaretSelection, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
area.MouseDown();
area.MouseUp();
Globals.WaitForTimeout();
VerifyQueue(expected);
// We may receive more than one caret and that's OK. Clear it out.
this.received.Clear();
// End selection with escape
app.UIRoot.SendKeys(Keys.Escape);
Globals.WaitForTimeout();
// Expect to see the caret again after leaving selection mode
expected.Enqueue(new EventData(EventType.CaretVisible, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
VerifyQueue(expected);
}
// B. Drag area click
{
// Move mouse pointer to where the cursor is
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
Point pt = new Point(sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y);
area.MouseMove(pt);
// Click on this area.
expected.Enqueue(new EventData(EventType.CaretSelection, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
area.MouseDown();
Globals.WaitForTimeout();
Point ptDrag = pt;
// Drag down and right for "some" distance. 10 isn't for a specific reason, it's just "some".
ptDrag.X += 10;
ptDrag.Y += 10;
area.MouseMove(ptDrag);
Globals.WaitForTimeout();
area.MouseUp();
Globals.WaitForTimeout();
// Verify that the first one in the queue starts with where we put the mouse down.
VerifyQueue(expected);
// Now we have to take the final message in the queue and make sure it is where we released the mouse
EventData expectedLast = new EventData(EventType.CaretSelection, ptDrag.X, ptDrag.Y);
EventData actualLast = received.Last();
Verify.AreEqual(expectedLast, actualLast);
// Empty the received queue.
received.Clear();
// End selection with escape
app.UIRoot.SendKeys(Keys.Escape);
Globals.WaitForTimeout();
// Expect to see the caret again after leaving selection mode
expected.Enqueue(new EventData(EventType.CaretVisible, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
VerifyQueue(expected);
}
}
[TestMethod]
public void TestLaunchAndExitChild()
{
RunTest(TestLaunchAndExitChildImpl);
}
private void TestLaunchAndExitChildImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue<EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
{
// A. We're going to type "cmd" into the prompt to start a command prompt.
{
Log.Comment("Type 'cmd' to get ready to start nested prompt.");
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
TestTypeStringHelper("cmd", app, sbiex);
}
// B. Now we're going to press enter to launch the CMD application
{
Log.Comment("Press enter to launch and observe launch events.");
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
expected.Enqueue(new EventData(EventType.StartApplication));
expected.Enqueue(new EventData(EventType.UpdateRegion, 0, 0, sbiex.dwSize.X - 1, sbiex.dwSize.Y - 1));
sbiex.dwCursorPosition.Y++;
expected.Enqueue(new EventData(EventType.UpdateRegion, 0, sbiex.dwCursorPosition.Y, "Microsoft Windows [Version 10.0.14974.1001]".Length - 1, sbiex.dwCursorPosition.Y));
sbiex.dwCursorPosition.Y++;
expected.Enqueue(new EventData(EventType.UpdateRegion, 0, sbiex.dwCursorPosition.Y, "(c) 2016 Microsoft Corporation. All rights reserved.".Length - 1, sbiex.dwCursorPosition.Y));
sbiex.dwCursorPosition.Y++;
sbiex.dwCursorPosition.Y++;
expected.Enqueue(new EventData(EventType.UpdateRegion, 0, sbiex.dwCursorPosition.Y, sbiexOriginal.dwCursorPosition.X - 1, sbiex.dwCursorPosition.Y));
expected.Enqueue(new EventData(EventType.CaretVisible, sbiexOriginal.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
app.UIRoot.SendKeys(Keys.Enter);
Globals.WaitForTimeout();
Globals.WaitForTimeout();
VerifyQueue(expected);
}
// C. Now we're going to type exit to leave the nested CMD application
{
Log.Comment("Type 'exit' to get ready to exit nested prompt.");
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
TestTypeStringHelper("exit", app, sbiex);
}
// D. Now we're going to press enter to exit the CMD application
{
Log.Comment("Press enter to launch and observe exit events.");
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
expected.Enqueue(new EventData(EventType.EndApplication));
sbiex.dwCursorPosition.Y++;
sbiex.dwCursorPosition.Y++;
expected.Enqueue(new EventData(EventType.UpdateRegion, 0, sbiex.dwCursorPosition.Y, sbiexOriginal.dwCursorPosition.X - 1, sbiex.dwCursorPosition.Y));
expected.Enqueue(new EventData(EventType.CaretVisible, sbiexOriginal.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
app.UIRoot.SendKeys(Keys.Enter);
Globals.WaitForTimeout();
Globals.WaitForTimeout();
VerifyQueue(expected);
}
}
[TestMethod]
public void TestScrollByWheel()
{
RunTest(TestScrollByWheelImpl);
}
private void TestScrollByWheelImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue<EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
{
int rowsPerScroll = app.GetRowsPerScroll();
int scrollDelta;
// A. Scroll down.
{
scrollDelta = -1;
expected.Enqueue(new EventData(EventType.UpdateScroll, 0, scrollDelta * rowsPerScroll));
expected.Enqueue(new EventData(EventType.Layout));
app.ScrollWindow(scrollDelta);
Globals.WaitForTimeout();
VerifyQueue(expected);
}
// B. Scroll up.
{
scrollDelta = 1;
expected.Enqueue(new EventData(EventType.UpdateScroll, 0, scrollDelta * rowsPerScroll));
expected.Enqueue(new EventData(EventType.Layout));
app.ScrollWindow(scrollDelta);
Globals.WaitForTimeout();
VerifyQueue(expected);
}
}
[TestMethod]
public void TestScrollByOverflow()
{
RunTest(TestScrollByOverflowImpl);
}
private void TestScrollByOverflowImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue<EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
{
// Get original screen information
sbiexOriginal = app.GetScreenBufferInfo();
short promptLineEnd = sbiexOriginal.dwCursorPosition.X;
promptLineEnd--; // prompt line ended one position left of cursor
// Resize the window to only have two lines left at the bottom to test overflow when we echo some text
sbiex = sbiexOriginal;
sbiex.srWindow.Bottom = sbiex.dwCursorPosition.Y;
sbiex.srWindow.Bottom += 3;
app.SetScreenBufferInfo(sbiex);
string echoText = "foo";
string echoCommand = "echo";
int echoLine = sbiexOriginal.dwCursorPosition.Y + 1;
expected.Enqueue(new EventData(EventType.UpdateRegion, 0, echoLine, echoText.Length - 1, echoLine));
expected.Enqueue(new EventData(EventType.UpdateScroll, 0, -1));
int newPromptLine = echoLine + 2;
expected.Enqueue(new EventData(EventType.UpdateRegion, 0, newPromptLine, promptLineEnd, newPromptLine));
expected.Enqueue(new EventData(EventType.Layout));
expected.Enqueue(new EventData(EventType.CaretVisible, promptLineEnd + 1, newPromptLine));
// type command to echo foo and press enter
app.UIRoot.SendKeys($"{echoCommand} {echoText}");
Globals.WaitForTimeout();
received.Clear();
app.UIRoot.SendKeys(Keys.Enter);
Globals.WaitForTimeout();
VerifyQueue(expected);
}
}
}