#region CPL License /* Nuclex Framework Copyright (C) 2002-2010 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion #if UNITTEST using System; using System.Threading; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using NUnit.Framework; using Nuclex.Input; using Nuclex.Support; using Nuclex.UserInterface.Input; namespace Nuclex.UserInterface.Controls.Desktop { /// Unit Test for the text input control [TestFixture] internal class InputControlTest { #region class DummyLocator /// Dummy implementation of an opening locator private class DummyLocator : IOpeningLocator { /// Initializes a new dummy text opening locator /// Index of the opening the dummy locator will report public DummyLocator(int index) { this.index = index; } /// /// Calculates which opening between two letters is closest to a position /// /// /// Boundaries of the control, should be in absolute coordinates /// /// Text in which the nearest opening will be located /// /// Position to which the closest opening will be found, /// should be in absolute coordinates /// /// The index of the opening closest to the provided position public int GetClosestOpening(RectangleF bounds, string text, Vector2 position) { return this.index; } /// Index the dummy locator will report private int index; } #endregion // class DummyLocator /// Verifies that simple text input is possible [Test] public void TestSimpleInput() { InputControl input = new InputControl(); IWritable writable = input as IWritable; Assert.IsNotNull(writable); writable.OnCharacterEntered('H'); writable.OnCharacterEntered('e'); writable.OnCharacterEntered('l'); writable.OnCharacterEntered('l'); writable.OnCharacterEntered('o'); Assert.AreEqual("Hello", input.Text); } /// /// Verifies that the title and description strings for the Guide can be assigned /// to the input control and queried via the IWritable interface /// [Test] public void TestGuideTitleAndDescription() { InputControl input = new InputControl(); input.GuideTitle = "Test123"; input.GuideDescription = "123Test"; IWritable writable = input as IWritable; Assert.IsNotNull(writable); Assert.AreEqual("Test123", writable.GuideTitle); Assert.AreEqual("123Test", writable.GuideDescription); } /// /// Tests whether the caret can be moved by the home and end keys /// [Test] public void TestHomeAndEnd() { Screen screen = new Screen(); InputControl input = new InputControl(); screen.Desktop.Children.Add(input); screen.FocusedControl = input; input.ProcessCharacter('W'); input.ProcessCharacter('o'); input.ProcessCharacter('r'); input.ProcessCharacter('l'); input.ProcessCharacter('d'); input.ProcessKeyPress(Keys.Home, false); input.ProcessCharacter('H'); input.ProcessCharacter('e'); input.ProcessCharacter('l'); input.ProcessCharacter('l'); input.ProcessCharacter('o'); input.ProcessCharacter(' '); input.ProcessKeyPress(Keys.End, false); input.ProcessCharacter('!'); Assert.AreEqual("Hello World!", input.Text); } /// /// Tests whether the caret can be moved using the cursor keys /// [Test] public void TestCaretMovementByCursor() { Screen screen = new Screen(); InputControl input = new InputControl(); screen.Desktop.Children.Add(input); screen.FocusedControl = input; input.ProcessCharacter('H'); input.ProcessCharacter('e'); input.ProcessCharacter('l'); input.ProcessCharacter('o'); input.ProcessKeyPress(Keys.Left, false); input.ProcessCharacter('l'); input.ProcessKeyPress(Keys.Right, false); input.ProcessCharacter('!'); Assert.AreEqual("Hello!", input.Text); } /// /// Tests whether the input control rejects presses of keys it doesn't handle /// [Test] public void TestUnhandledKeyPress() { Screen screen = new Screen(); InputControl input = new InputControl(); screen.Desktop.Children.Add(input); screen.FocusedControl = input; Assert.IsTrue(input.ProcessKeyPress(Keys.Left, false)); input.ProcessKeyRelease(Keys.Left); Assert.IsTrue(input.ProcessKeyPress(Keys.Right, false)); input.ProcessKeyRelease(Keys.Right); Assert.IsFalse(input.ProcessKeyPress(Keys.Up, false)); Assert.IsFalse(input.ProcessKeyPress(Keys.Down, false)); } /// /// Tests whether the input control rejects presses of normally handled keys /// when it isn't focused /// [Test] public void TestUnfocusedKeyPress() { InputControl input = new InputControl(); Assert.IsFalse(input.ProcessKeyPress(Keys.Left, false)); Assert.IsFalse(input.ProcessKeyPress(Keys.Right, false)); } /// /// Verifies that the backspace key can be used to delete a character /// [Test] public void TestBackspace() { Screen screen = new Screen(); InputControl input = new InputControl(); screen.Desktop.Children.Add(input); screen.FocusedControl = input; input.ProcessCharacter('H'); input.ProcessCharacter('e'); input.ProcessCharacter('y'); input.ProcessCharacter('o'); input.ProcessKeyPress(Keys.Left, false); input.ProcessKeyPress(Keys.Back, false); input.ProcessCharacter('l'); input.ProcessCharacter('l'); Assert.AreEqual("Hello", input.Text); } /// /// Tests whether the delete key deletes the character right of the caret /// [Test] public void TestDelete() { Screen screen = new Screen(); InputControl input = new InputControl(); screen.Desktop.Children.Add(input); screen.FocusedControl = input; input.ProcessCharacter('T'); input.ProcessCharacter('e'); input.ProcessCharacter('l'); input.ProcessCharacter('l'); input.ProcessCharacter('o'); input.ProcessKeyPress(Keys.Home, false); input.ProcessKeyPress(Keys.Delete, false); input.ProcessCharacter('H'); Assert.AreEqual("Hello", input.Text); } /// /// Tests whether assigning a text that is too short for the current caret /// position will adjust the caret's position /// [Test] public void TestTextShorteningAdjustsCaret() { Screen screen = new Screen(); InputControl input = new InputControl(); screen.Desktop.Children.Add(input); screen.FocusedControl = input; input.Text = "Hello World"; input.CaretPosition = 11; Assert.AreEqual(11, input.CaretPosition); input.Text = "Hello"; Assert.AreEqual(5, input.CaretPosition); } /// /// Verifies that an exception is thrown if an invalid caret position is assigned /// [Test] public void TestThrowOnInvalidCaretPosition() { Screen screen = new Screen(); InputControl input = new InputControl(); screen.Desktop.Children.Add(input); screen.FocusedControl = input; Assert.Throws( delegate() { input.CaretPosition = -1; } ); Assert.Throws( delegate() { input.CaretPosition = 1; } ); } /// /// Tests whether the control can tell whether it has the input focus /// [Test] public void TestInputFocus() { Screen screen = new Screen(); InputControl input = new InputControl(); screen.Desktop.Children.Add(input); Assert.IsFalse(input.HasFocus); screen.FocusedControl = input; Assert.IsTrue(input.HasFocus); } /// /// Verifies that the caret can be moved by clicking the mouse and that /// the input box performs its default action with no opening locator assigned. /// [Test] public void TestMoveCaretByMouseWithoutLocator() { Screen screen = new Screen(100, 100); InputControl input = new InputControl(); input.Bounds = new UniRectangle(10, 10, 100, 100); screen.Desktop.Children.Add(input); input.Text = "Hello World"; input.CaretPosition = 0; // This should move the caret. Because the control has no locator and it // doesn't know which font is being used (controls have no connection to // their rendering code), it can only move the caret to the end. input.ProcessMouseMove(100, 100, 50, 50); input.ProcessMousePress(MouseButtons.Left); Assert.AreEqual(input.Text.Length, input.CaretPosition); } /// /// Verifies that the caret can be moved by clicking the mouse and that /// the input box uses its opening locator if it is assigned. /// [Test] public void TestMoveCaretByMouseWitLocator() { Screen screen = new Screen(100, 100); InputControl input = new InputControl(); input.Bounds = new UniRectangle(10, 10, 100, 100); screen.Desktop.Children.Add(input); input.Text = "Hello World"; input.CaretPosition = 0; // Assign a dummy locator for text openings which will always report that // the mouse is between the 4th and 5th letters. input.OpeningLocator = new DummyLocator(4); // This should move the caret. Because the dummy locator is assigned, it // should be asked for the position the user has clicked on. input.ProcessMouseMove(100, 100, 50, 50); input.ProcessMousePress(MouseButtons.Left); Assert.AreEqual(4, input.CaretPosition); } /// /// Verifies that the input box tracks the milliseconds that have passed /// since the caret was last moved. /// [Test] public void TestMillisecondsSinceLastCaretMovement() { InputControl input = new InputControl(); input.ProcessCharacter('H'); int start = Environment.TickCount; int end; do { Thread.Sleep(1); end = Environment.TickCount; } while(start == end); int elapsedMilliseconds = end - start; Assert.GreaterOrEqual( input.MillisecondsSinceLastCaretMovement, elapsedMilliseconds ); } } } // namespace Nuclex.UserInterface.Controls.Desktop #endif // UNITTEST