2019-05-03 00:29:04 +02:00
//----------------------------------------------------------------------------------------------------------------------
// <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
2020-02-10 21:40:01 +01:00
flagsExpected | = WinCon . CONSOLE_SELECTION_INFO_FLAGS . CONSOLE_SELECTION_IN_PROGRESS ; // a selection is occurring
2019-05-03 00:29:04 +02:00
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." ) ;
}
}
}
}
}