355 lines
20 KiB
C#
355 lines
20 KiB
C#
//----------------------------------------------------------------------------------------------------------------------
|
|
// <copyright file="SelectionApiTests.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
// </copyright>
|
|
// <summary>UI Automation tests for the Selection Information API.</summary>
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
namespace Conhost.UIA.Tests
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
|
|
using Microsoft.Win32;
|
|
|
|
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;
|
|
|
|
[TestClass]
|
|
public class SelectionApiTests
|
|
{
|
|
public const int timeout = Globals.Timeout;
|
|
|
|
public TestContext TestContext { get; set; }
|
|
|
|
|
|
[TestMethod]
|
|
public void TestCtrlHomeEnd()
|
|
{
|
|
using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
|
|
{
|
|
using (ViewportArea area = new ViewportArea(app))
|
|
{
|
|
// Get console handle.
|
|
IntPtr hConsole = app.GetStdOutHandle();
|
|
Verify.IsNotNull(hConsole, "Ensure the STDOUT handle is valid.");
|
|
|
|
// Get us to an expected initial state.
|
|
app.UIRoot.SendKeys("C:" + Keys.Enter);
|
|
app.UIRoot.SendKeys(@"cd C:\" + Keys.Enter);
|
|
app.UIRoot.SendKeys("cls" + Keys.Enter);
|
|
|
|
// Get initial screen buffer position
|
|
WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
|
|
sbiexOriginal.cbSize = (uint)Marshal.SizeOf(sbiexOriginal);
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexOriginal), "Get initial viewport position.");
|
|
|
|
// Prep comparison structure
|
|
WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexCompare = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
|
|
sbiexCompare.cbSize = (uint)Marshal.SizeOf(sbiexCompare);
|
|
|
|
// Ctrl-End shouldn't move anything yet.
|
|
Log.Comment("Attempt Ctrl-End. Nothing should move yet.");
|
|
app.UIRoot.SendKeys(Keys.Control + Keys.End + Keys.Control);
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexCompare), "Get comparison position.");
|
|
Verify.AreEqual<WinCon.SMALL_RECT>(sbiexOriginal.srWindow, sbiexCompare.srWindow, "Compare viewport positions before and after.");
|
|
|
|
// Ctrl-Home shouldn't move anything yet.
|
|
Log.Comment("Attempt Ctrl-Home. Nothing should move yet.");
|
|
app.UIRoot.SendKeys(Keys.Control + Keys.Home + Keys.Control);
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
Log.Comment("Now test the line with some text in it.");
|
|
// Retrieve original position (including cursor)
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexOriginal), "Get position of viewport with nothing on edit line.");
|
|
|
|
// Put some text onto the edit line now
|
|
Log.Comment("Place some text onto the edit line to ensure behavior will change with edit line full.");
|
|
const string testText = "SomeTestText";
|
|
app.UIRoot.SendKeys(testText);
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
// Get the position of the cursor after the text is entered
|
|
WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexWithText = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
|
|
sbiexWithText.cbSize = (uint)Marshal.SizeOf(sbiexWithText);
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexWithText), "Get position of viewport with edit line text.");
|
|
|
|
// The cursor can't have moved down a line. We're going to verify the text by reading its "rectangle" out of the screen buffer.
|
|
// If it moved down a line, the calculation of what to select is more complicated than the simple rectangle assignment below.
|
|
Verify.AreEqual(sbiexOriginal.dwCursorPosition.Y, sbiexWithText.dwCursorPosition.Y, "There's an assumption here that the cursor stayed on the same line when we added our bit of text.");
|
|
|
|
// Prepare the read rectangle for what we want to get out of the buffer.
|
|
Rectangle readRectangle = new Rectangle(sbiexOriginal.dwCursorPosition.X,
|
|
sbiexOriginal.dwCursorPosition.Y,
|
|
(sbiexWithText.dwCursorPosition.X - sbiexOriginal.dwCursorPosition.X),
|
|
1);
|
|
|
|
Log.Comment("Verify that the text we keyed matches what's in the buffer.");
|
|
IEnumerable<string> text = area.GetLinesInRectangle(hConsole, readRectangle);
|
|
Verify.AreEqual(text.Count(), 1, "We should only have retrieved one line.");
|
|
Verify.AreEqual(text.First(), testText, "Verify text matches keyed input.");
|
|
|
|
// Move cursor into the middle of the text.
|
|
Log.Comment("Move cursor into the middle of the string.");
|
|
|
|
const int lefts = 4;
|
|
for (int i = 0; i < lefts; i++)
|
|
{
|
|
app.UIRoot.SendKeys(Keys.Left);
|
|
}
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
// Get cursor position now that it's moved.
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexWithText), "Get position of viewport with cursor moved into the middle of the edit line text.");
|
|
|
|
Log.Comment("Ctrl-End should trim the end of the input line from the cursor (and not move the cursor.)");
|
|
app.UIRoot.SendKeys(Keys.Control + Keys.End + Keys.Control);
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexCompare), "Get comparison position.");
|
|
Verify.AreEqual<WinCon.SMALL_RECT>(sbiexWithText.srWindow, sbiexCompare.srWindow, "Compare viewport positions before and after.");
|
|
Verify.AreEqual<WinCon.COORD>(sbiexWithText.dwCursorPosition, sbiexCompare.dwCursorPosition, "Compare cursor positions before and after.");
|
|
|
|
Log.Comment("Compare actual text visible on screen.");
|
|
text = area.GetLinesInRectangle(hConsole, readRectangle);
|
|
Verify.AreEqual(text.Count(), 1, "We should only have retrieved one line.");
|
|
|
|
// the substring length is the original length of the string minus the number of lefts
|
|
int substringCtrlEnd = testText.Length - lefts;
|
|
Verify.AreEqual(text.First().Trim(), testText.Substring(0, substringCtrlEnd), "Verify text matches keyed input without the last characters removed by Ctrl+End.");
|
|
|
|
Log.Comment("Ctrl-Home should trim the remainder of the edit line from the cursor to the beginning (restoring cursor to position before we entered anything.)");
|
|
app.UIRoot.SendKeys(Keys.Control + Keys.Home + Keys.Control);
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexCompare), "Get comparison position.");
|
|
Verify.AreEqual<WinCon.SMALL_RECT>(sbiexOriginal.srWindow, sbiexCompare.srWindow, "Compare viewport positions before and after.");
|
|
Verify.AreEqual<WinCon.COORD>(sbiexOriginal.dwCursorPosition, sbiexCompare.dwCursorPosition, "Compare cursor positions before and after.");
|
|
|
|
Log.Comment("Compare actual text visible on screen.");
|
|
text = area.GetLinesInRectangle(hConsole, readRectangle);
|
|
Verify.AreEqual(text.Count(), 1, "We should only have retrieved one line.");
|
|
|
|
Verify.AreEqual(text.First().Trim(), string.Empty, "Verify text is now empty after Ctrl+Home from the end of it.");
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void TestKeyboardSelection()
|
|
{
|
|
using (RegistryHelper reg = new RegistryHelper())
|
|
{
|
|
reg.BackupRegistry();
|
|
|
|
VersionSelector.SetConsoleVersion(reg, ConsoleVersion.V2);
|
|
|
|
using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
|
|
{
|
|
using (ViewportArea area = new ViewportArea(app))
|
|
{
|
|
WinCon.CONSOLE_SELECTION_INFO csi;
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Get initial selection state.");
|
|
Log.Comment("Selection Info: {0}", csi);
|
|
|
|
Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_NO_SELECTION, "Confirm no selection in progress.");
|
|
// ignore rectangle and coords. They're undefined when there is no selection.
|
|
|
|
// Get cursor position at the beginning of this operation. The anchor will start at the cursor position for v2 console.
|
|
// NOTE: It moved to 0,0 for the v1 console.
|
|
IntPtr hConsole = WinCon.GetStdHandle(WinCon.CONSOLE_STD_HANDLE.STD_OUTPUT_HANDLE);
|
|
Verify.IsNotNull(hConsole, "Ensure the STDOUT handle is valid.");
|
|
|
|
WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX cbiex = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
|
|
cbiex.cbSize = (uint)Marshal.SizeOf(cbiex);
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref cbiex), "Get initial cursor position (from screen buffer info)");
|
|
|
|
// The expected anchor when we're done is this initial cursor position
|
|
WinCon.COORD expectedAnchor = new WinCon.COORD();
|
|
expectedAnchor.X = cbiex.dwCursorPosition.X;
|
|
expectedAnchor.Y = cbiex.dwCursorPosition.Y;
|
|
|
|
// The expected rect is going to start from this cursor position. We'll modify it after we perform some operations.
|
|
WinCon.SMALL_RECT expectedRect = new WinCon.SMALL_RECT();
|
|
expectedRect.Top = expectedAnchor.Y;
|
|
expectedRect.Left = expectedAnchor.X;
|
|
expectedRect.Right = expectedAnchor.X;
|
|
expectedRect.Bottom = expectedAnchor.Y;
|
|
|
|
// Now set up the keyboard and enter mark mode.
|
|
// NOTE: We must wait after every keyboard sequence to give the console time to process before asking it for changes.
|
|
area.EnterMode(ViewportArea.ViewportStates.Mark);
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Get state on entering mark mode.");
|
|
Log.Comment("Selection Info: {0}", csi);
|
|
|
|
Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_IN_PROGRESS, "Selection should now be in progress since mark mode is started.");
|
|
|
|
// Select a small region
|
|
Log.Comment("1. Select a small region");
|
|
|
|
app.UIRoot.SendKeys(Keys.Shift + Keys.Right + Keys.Right + Keys.Right + Keys.Down + Keys.Shift);
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
// Adjust the expected rectangle for the commands we just entered.
|
|
expectedRect.Right += 3; // same as the number of Rights we put in
|
|
expectedRect.Bottom += 1; // same as the number of Downs we put in
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Get state of selected region.");
|
|
Log.Comment("Selection Info: {0}", csi);
|
|
|
|
Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_IN_PROGRESS | WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_NOT_EMPTY, "Selection in progress and is no longer empty now that we've selected a region.");
|
|
Verify.AreEqual(csi.Selection, expectedRect, "Verify that the selected rectangle matches the keystrokes we entered.");
|
|
Verify.AreEqual(csi.SelectionAnchor, expectedAnchor, "Verify anchor didn't go anywhere since we started in the top left.");
|
|
|
|
// End selection by moving
|
|
Log.Comment("2. End the selection by moving.");
|
|
|
|
app.UIRoot.SendKeys(Keys.Down);
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Move cursor to attempt to clear selection.");
|
|
Log.Comment("Selection Info: {0}", csi);
|
|
|
|
Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_IN_PROGRESS, "Selection should be still running, but empty.");
|
|
|
|
// Select another region to ensure anchor moved.
|
|
Log.Comment("3. Select one more region from new position to verify anchor");
|
|
|
|
app.UIRoot.SendKeys(Keys.Shift + Keys.Right + Keys.Shift);
|
|
|
|
Globals.WaitForTimeout();
|
|
|
|
expectedAnchor.X = expectedRect.Right;
|
|
expectedAnchor.Y = expectedRect.Bottom;
|
|
expectedAnchor.Y++; // +1 for the Down in step 2. Not incremented in the line above because C# is unhappy with adding +1 to a short while assigning.
|
|
|
|
Verify.AreEqual(csi.SelectionAnchor, expectedAnchor, "Verify anchor moved to the new start position.");
|
|
|
|
// Exit mark mode
|
|
area.EnterMode(ViewportArea.ViewportStates.Normal);
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Move cursor to attempt to clear selection.");
|
|
Log.Comment("Selection Info: {0}", csi);
|
|
|
|
Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_NO_SELECTION, "Selection should be empty when mode is exited.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void TestMouseSelection()
|
|
{
|
|
using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
|
|
{
|
|
using (ViewportArea area = new ViewportArea(app))
|
|
{
|
|
// Set up the area we're going to attempt to select
|
|
Point startPoint = new Point();
|
|
Point endPoint = new Point();
|
|
|
|
startPoint.X = 1;
|
|
startPoint.Y = 2;
|
|
|
|
endPoint.X = 10;
|
|
endPoint.Y = 10;
|
|
|
|
// Save expected anchor
|
|
WinCon.COORD expectedAnchor = new WinCon.COORD();
|
|
expectedAnchor.X = (short)startPoint.X;
|
|
expectedAnchor.Y = (short)startPoint.Y;
|
|
|
|
// Also save bottom right corner for the end of the selection
|
|
WinCon.COORD expectedBottomRight = new WinCon.COORD();
|
|
expectedBottomRight.X = (short)endPoint.X;
|
|
expectedBottomRight.Y = (short)endPoint.Y;
|
|
|
|
// Prepare the mouse by moving it into the start position. Prepare the structure
|
|
WinCon.CONSOLE_SELECTION_INFO csi;
|
|
WinCon.SMALL_RECT expectedRect = new WinCon.SMALL_RECT();
|
|
|
|
WinCon.CONSOLE_SELECTION_INFO_FLAGS flagsExpected = WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_NO_SELECTION;
|
|
|
|
// 1. Place mouse button down to start selection and check state
|
|
area.MouseMove(startPoint);
|
|
area.MouseDown();
|
|
|
|
Globals.WaitForTimeout(); // must wait after mouse operation. No good waiters since we have no UI objects
|
|
|
|
flagsExpected |= WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_IN_PROGRESS; // a selection is occurring
|
|
flagsExpected |= WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_MOUSE_SELECTION; // it's a "Select" mode not "Mark" mode selection
|
|
flagsExpected |= WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_MOUSE_DOWN; // the mouse is still down
|
|
flagsExpected |= WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_NOT_EMPTY; // mouse selections are never empty. minimum 1x1
|
|
|
|
expectedRect.Top = expectedAnchor.Y; // rectangle is just at the point itself 1x1 size
|
|
expectedRect.Left = expectedAnchor.X;
|
|
expectedRect.Bottom = expectedRect.Top;
|
|
expectedRect.Right = expectedRect.Left;
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Check state on mouse button down to start selection.");
|
|
Log.Comment("Selection Info: {0}", csi);
|
|
|
|
Verify.AreEqual(csi.Flags, flagsExpected, "Check initial mouse selection with button still down.");
|
|
Verify.AreEqual(csi.SelectionAnchor, expectedAnchor, "Check that the anchor is equal to the start point.");
|
|
Verify.AreEqual(csi.Selection, expectedRect, "Check that entire rectangle is the size of 1x1 and is just at the anchor point.");
|
|
|
|
// 2. Move to end point and release cursor
|
|
area.MouseMove(endPoint);
|
|
area.MouseUp();
|
|
|
|
Globals.WaitForTimeout(); // must wait after mouse operation. No good waiters since we have no UI objects
|
|
|
|
// on button up, remove mouse down flag
|
|
flagsExpected &= ~WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_MOUSE_DOWN;
|
|
|
|
// anchor remains the same
|
|
// bottom right of rectangle now changes to the end point
|
|
expectedRect.Bottom = expectedBottomRight.Y;
|
|
expectedRect.Right = expectedBottomRight.X;
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Check state after drag and release mouse.");
|
|
Log.Comment("Selection Info: {0}", csi);
|
|
|
|
Verify.AreEqual(csi.Flags, flagsExpected, "Check selection is still on and valid, but button is up.");
|
|
Verify.AreEqual(csi.SelectionAnchor, expectedAnchor, "Check that the anchor is still equal to the start point.");
|
|
Verify.AreEqual(csi.Selection, expectedRect, "Check that entire rectangle reaches from start to end point.");
|
|
|
|
// 3. Leave mouse selection
|
|
area.ExitModes();
|
|
|
|
flagsExpected = WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_NO_SELECTION;
|
|
|
|
NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Check state after exiting mouse selection.");
|
|
Log.Comment("Selection Info: {0}", csi);
|
|
|
|
Verify.AreEqual(csi.Flags, flagsExpected, "Check that selection state is reset.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|