private static void BufferVerificationHelper(CmdApp app, ViewportArea area, IntPtr hConsole, GetExpectedChar expectedCharAlgorithm) { WinCon.SMALL_RECT viewport = app.GetViewport(hConsole); Rectangle selectRect = new Rectangle(viewport.Left, viewport.Top, viewport.Width, viewport.Height); IEnumerable <string> scrapedText = area.GetLinesInRectangle(hConsole, selectRect); Verify.AreEqual(viewport.Height, scrapedText.Count(), "Verify the rows scraped is equal to the entire viewport height."); bool isValidState = true; string[] rows = scrapedText.ToArray(); for (int i = 0; i < rows.Length; i++) { for (int j = 0; j < viewport.Width; j++) { char actual = rows[i][j]; char expected = expectedCharAlgorithm(i, j, rows.Length, viewport.Width); isValidState = actual == expected; if (!isValidState) { Verify.Fail(string.Format("Text buffer verification failed at Row: {0} Col: {1} Expected: '{2}' Actual: '{3}'", i, j, expected, actual)); break; } } if (!isValidState) { break; } } }
// Adapted from .NET source code... // See: http://referencesource.microsoft.com/#mscorlib/system/console.cs,fcb364a853d81c57 private static void SetWindowPosition(int left, int top) { AutoHelpers.LogInvariant("Attempt to set console viewport buffer to Left: {0} and Top: {1}", left, top); IntPtr hConsole = WinCon.GetStdHandle(WinCon.CONSOLE_STD_HANDLE.STD_OUTPUT_HANDLE); // Get the size of the current console window WinCon.CONSOLE_SCREEN_BUFFER_INFO csbi = new WinCon.CONSOLE_SCREEN_BUFFER_INFO(); NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfo(hConsole, out csbi), "Get console screen buffer for viewport size information."); WinCon.SMALL_RECT srWindow = csbi.srWindow; AutoHelpers.LogInvariant("Initial viewport position: {0}", srWindow); // Check for arithmetic underflows & overflows. int newRight = left + srWindow.Right - srWindow.Left + 1; if (left < 0 || newRight > csbi.dwSize.X || newRight < 0) { throw new ArgumentOutOfRangeException("left"); } int newBottom = top + srWindow.Bottom - srWindow.Top + 1; if (top < 0 || newBottom > csbi.dwSize.Y || newBottom < 0) { throw new ArgumentOutOfRangeException("top"); } // Preserve the size, but move the position. srWindow.Bottom -= (short)(srWindow.Top - top); srWindow.Right -= (short)(srWindow.Left - left); srWindow.Left = (short)left; srWindow.Top = (short)top; NativeMethods.Win32BoolHelper(WinCon.SetConsoleWindowInfo(hConsole, true, ref srWindow), string.Format("Attempt to update viewport position to {0}.", srWindow)); }
private static void TestInsertDelete(CmdApp app, ViewportArea area, IntPtr hConsole) { Log.Comment("--Insert/Delete Commands"); ScreenFillHelper(app, area, hConsole); Log.Comment("Move cursor to the middle-ish"); Point cursorExpected = new Point(); // H is at 5, 1. VT coords are 1-based and buffer is 0-based so adjust. cursorExpected.Y = 5 - 1; cursorExpected.X = 1 - 1; app.UIRoot.SendKeys("H"); // Move to middle-ish from here. 10 Bs and 10 Cs should about do it. for (int i = 0; i < 10; i++) { app.UIRoot.SendKeys("BC"); cursorExpected.Y++; cursorExpected.X++; } WinCon.SMALL_RECT viewport = app.GetViewport(hConsole); // The entire buffer should be Zs except for what we're about to insert and delete. app.UIRoot.SendKeys("O"); // insert WinCon.CHAR_INFO ciCursor = area.GetCharInfoAt(hConsole, cursorExpected); Verify.AreEqual(' ', ciCursor.UnicodeChar); Point endOfCursorLine = new Point(viewport.Right, cursorExpected.Y); app.UIRoot.SendKeys("P"); // delete WinCon.CHAR_INFO ciEndOfLine = area.GetCharInfoAt(hConsole, endOfCursorLine); Verify.AreEqual(' ', ciEndOfLine.UnicodeChar); ciCursor = area.GetCharInfoAt(hConsole, cursorExpected); Verify.AreEqual('Z', ciCursor.UnicodeChar); // Move to end of line and check both insert and delete operations while (cursorExpected.X < viewport.Right) { app.UIRoot.SendKeys("C"); cursorExpected.X++; } // move up a line to get some fresh Z app.UIRoot.SendKeys("A"); cursorExpected.Y--; app.UIRoot.SendKeys("O"); // insert at end of line ciCursor = area.GetCharInfoAt(hConsole, cursorExpected); Verify.AreEqual(' ', ciCursor.UnicodeChar); // move up a line to get some fresh Z app.UIRoot.SendKeys("A"); cursorExpected.Y--; app.UIRoot.SendKeys("P"); // delete at end of line ciCursor = area.GetCharInfoAt(hConsole, cursorExpected); Verify.AreEqual(' ', ciCursor.UnicodeChar); }
public WinCon.CHAR_INFO[,] GetCharInfoInRectangle(IntPtr handle, Rectangle rect) { WinCon.SMALL_RECT readRectangle = new WinCon.SMALL_RECT(); readRectangle.Top = (short)rect.Top; readRectangle.Bottom = (short)(rect.Bottom - 1); readRectangle.Left = (short)rect.Left; readRectangle.Right = (short)(rect.Right - 1); WinCon.COORD dataBufferSize = new WinCon.COORD(); dataBufferSize.X = (short)rect.Width; dataBufferSize.Y = (short)rect.Height; WinCon.COORD dataBufferPos = new WinCon.COORD(); dataBufferPos.X = 0; dataBufferPos.Y = 0; WinCon.CHAR_INFO[,] data = new WinCon.CHAR_INFO[dataBufferSize.Y, dataBufferSize.X]; NativeMethods.Win32BoolHelper(WinCon.ReadConsoleOutput(handle, data, dataBufferSize, dataBufferPos, ref readRectangle), string.Format("Attempting to read rectangle (L: {0}, T: {1}, R: {2}, B: {3}) from output buffer.", readRectangle.Left, readRectangle.Top, readRectangle.Right, readRectangle.Bottom)); return(data); }
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."); } } }
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."); } } } }