diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 692419b9bd..7c4c8ff44b 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -20,7 +20,8 @@ jobs: dotnet-version: 6.0.100 - name: Install dependencies - run: dotnet restore + run: | + dotnet restore - name: Build Debug run: dotnet build --configuration Debug --no-restore diff --git a/Terminal.Gui UnitTests/ScenarioTests.cs b/Terminal.Gui UnitTests/ScenarioTests.cs new file mode 100644 index 0000000000..f5f1dc57bb --- /dev/null +++ b/Terminal.Gui UnitTests/ScenarioTests.cs @@ -0,0 +1,566 @@ +using NStack; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Terminal.Gui; +using UICatalog; +using Xunit; +using Xunit.Abstractions; + +// Alias Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.FakeConsole; + +namespace UICatalog { + public class ScenarioTests { + readonly ITestOutputHelper output; + + public ScenarioTests (ITestOutputHelper output) + { +#if DEBUG_IDISPOSABLE + Responder.Instances.Clear (); +#endif + this.output = output; + } + + int CreateInput (string input) + { + // Put a control-q in at the end + FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true)); + foreach (var c in input.Reverse ()) { + if (char.IsLetter (c)) { + FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (char.ToLower (c), (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false)); + } else { + FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false)); + } + } + return FakeConsole.MockKeyPresses.Count; + } + + + /// + /// + /// This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run. + /// + /// + /// Should find any Scenarios which crash on load or do not respond to . + /// + /// + [Fact] + public void Run_All_Scenarios () + { + List scenarios = Scenario.GetScenarios (); + Assert.NotEmpty (scenarios); + + foreach (var scenario in scenarios) { + + output.WriteLine ($"Running Scenario '{scenario}'"); + + Func closeCallback = (MainLoop loop) => { + Application.RequestStop (); + return false; + }; + + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + + // Close after a short period of time + var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), closeCallback); + + scenario.Init (Colors.Base); + scenario.Setup (); + scenario.Run (); + Application.Shutdown (); +#if DEBUG_IDISPOSABLE + foreach (var inst in Responder.Instances) { + Assert.True (inst.WasDisposed); + } + Responder.Instances.Clear (); +#endif + } +#if DEBUG_IDISPOSABLE + foreach (var inst in Responder.Instances) { + Assert.True (inst.WasDisposed); + } + Responder.Instances.Clear (); +#endif + } + + [Fact] + public void Run_Generic () + { + List scenarios = Scenario.GetScenarios (); + Assert.NotEmpty (scenarios); + + var item = scenarios.FindIndex (s => s.GetName ().Equals ("Generic", StringComparison.OrdinalIgnoreCase)); + var generic = scenarios [item]; + // Setup some fake keypresses + // Passing empty string will cause just a ctrl-q to be fired + int stackSize = CreateInput (""); + + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + + int iterations = 0; + Application.Iteration = () => { + iterations++; + // Stop if we run out of control... + if (iterations == 10) { + Application.RequestStop (); + } + }; + + var ms = 1000; + var abortCount = 0; + Func abortCallback = (MainLoop loop) => { + abortCount++; + Application.RequestStop (); + return false; + }; + var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback); + + Application.Top.KeyPress += (View.KeyEventEventArgs args) => { + Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key); + }; + + generic.Init (Colors.Base); + generic.Setup (); + // There is no need to call Application.Begin because Init already creates the Application.Top + // If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run. + //var rs = Application.Begin (Application.Top); + generic.Run (); + + //Application.End (rs); + + Assert.Equal (0, abortCount); + // # of key up events should match # of iterations + Assert.Equal (1, iterations); + // Using variable in the left side of Assert.Equal/NotEqual give error. Must be used literals values. + //Assert.Equal (stackSize, iterations); + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); + +#if DEBUG_IDISPOSABLE + foreach (var inst in Responder.Instances) { + Assert.True (inst.WasDisposed); + } + Responder.Instances.Clear (); +#endif + } + + [Fact] + public void Run_All_Views_Tester_Scenario () + { + Window _leftPane; + ListView _classListView; + FrameView _hostPane; + + Dictionary _viewClasses; + View _curView = null; + + // Settings + FrameView _settingsPane; + CheckBox _computedCheckBox; + FrameView _locationFrame; + RadioGroup _xRadioGroup; + TextField _xText; + int _xVal = 0; + RadioGroup _yRadioGroup; + TextField _yText; + int _yVal = 0; + + FrameView _sizeFrame; + RadioGroup _wRadioGroup; + TextField _wText; + int _wVal = 0; + RadioGroup _hRadioGroup; + TextField _hText; + int _hVal = 0; + List posNames = new List { "Factor", "AnchorEnd", "Center", "Absolute" }; + List dimNames = new List { "Factor", "Fill", "Absolute" }; + + + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + + var Top = Application.Top; + + _viewClasses = GetAllViewClassesCollection () + .OrderBy (t => t.Name) + .Select (t => new KeyValuePair (t.Name, t)) + .ToDictionary (t => t.Key, t => t.Value); + + _leftPane = new Window ("Classes") { + X = 0, + Y = 0, + Width = 15, + Height = Dim.Fill (1), // for status bar + CanFocus = false, + ColorScheme = Colors.TopLevel, + }; + + _classListView = new ListView (_viewClasses.Keys.ToList ()) { + X = 0, + Y = 0, + Width = Dim.Fill (0), + Height = Dim.Fill (0), + AllowsMarking = false, + ColorScheme = Colors.TopLevel, + }; + _leftPane.Add (_classListView); + + _settingsPane = new FrameView ("Settings") { + X = Pos.Right (_leftPane), + Y = 0, // for menu + Width = Dim.Fill (), + Height = 10, + CanFocus = false, + ColorScheme = Colors.TopLevel, + }; + _computedCheckBox = new CheckBox ("Computed Layout", true) { X = 0, Y = 0 }; + _settingsPane.Add (_computedCheckBox); + + var radioItems = new ustring [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" }; + _locationFrame = new FrameView ("Location (Pos)") { + X = Pos.Left (_computedCheckBox), + Y = Pos.Bottom (_computedCheckBox), + Height = 3 + radioItems.Length, + Width = 36, + }; + _settingsPane.Add (_locationFrame); + + var label = new Label ("x:") { X = 0, Y = 0 }; + _locationFrame.Add (label); + _xRadioGroup = new RadioGroup (radioItems) { + X = 0, + Y = Pos.Bottom (label), + }; + _xText = new TextField ($"{_xVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 }; + _locationFrame.Add (_xText); + + _locationFrame.Add (_xRadioGroup); + + radioItems = new ustring [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" }; + label = new Label ("y:") { X = Pos.Right (_xRadioGroup) + 1, Y = 0 }; + _locationFrame.Add (label); + _yText = new TextField ($"{_yVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 }; + _locationFrame.Add (_yText); + _yRadioGroup = new RadioGroup (radioItems) { + X = Pos.X (label), + Y = Pos.Bottom (label), + }; + _locationFrame.Add (_yRadioGroup); + + _sizeFrame = new FrameView ("Size (Dim)") { + X = Pos.Right (_locationFrame), + Y = Pos.Y (_locationFrame), + Height = 3 + radioItems.Length, + Width = 40, + }; + + radioItems = new ustring [] { "Percent(width)", "Fill(width)", "Sized(width)" }; + label = new Label ("width:") { X = 0, Y = 0 }; + _sizeFrame.Add (label); + _wRadioGroup = new RadioGroup (radioItems) { + X = 0, + Y = Pos.Bottom (label), + }; + _wText = new TextField ($"{_wVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 }; + _sizeFrame.Add (_wText); + _sizeFrame.Add (_wRadioGroup); + + radioItems = new ustring [] { "Percent(height)", "Fill(height)", "Sized(height)" }; + label = new Label ("height:") { X = Pos.Right (_wRadioGroup) + 1, Y = 0 }; + _sizeFrame.Add (label); + _hText = new TextField ($"{_hVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 }; + _sizeFrame.Add (_hText); + + _hRadioGroup = new RadioGroup (radioItems) { + X = Pos.X (label), + Y = Pos.Bottom (label), + }; + _sizeFrame.Add (_hRadioGroup); + + _settingsPane.Add (_sizeFrame); + + _hostPane = new FrameView ("") { + X = Pos.Right (_leftPane), + Y = Pos.Bottom (_settingsPane), + Width = Dim.Fill (), + Height = Dim.Fill (1), // + 1 for status bar + ColorScheme = Colors.Dialog, + }; + + _classListView.OpenSelectedItem += (a) => { + _settingsPane.SetFocus (); + }; + _classListView.SelectedItemChanged += (args) => { + ClearClass (_curView); + _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]); + }; + + _computedCheckBox.Toggled += (previousState) => { + if (_curView != null) { + _curView.LayoutStyle = previousState ? LayoutStyle.Absolute : LayoutStyle.Computed; + _hostPane.LayoutSubviews (); + } + }; + + _xRadioGroup.SelectedItemChanged += (selected) => DimPosChanged (_curView); + + _xText.TextChanged += (args) => { + try { + _xVal = int.Parse (_xText.Text.ToString ()); + DimPosChanged (_curView); + } catch { + + } + }; + + _yText.TextChanged += (args) => { + try { + _yVal = int.Parse (_yText.Text.ToString ()); + DimPosChanged (_curView); + } catch { + + } + }; + + _yRadioGroup.SelectedItemChanged += (selected) => DimPosChanged (_curView); + + _wRadioGroup.SelectedItemChanged += (selected) => DimPosChanged (_curView); + + _wText.TextChanged += (args) => { + try { + _wVal = int.Parse (_wText.Text.ToString ()); + DimPosChanged (_curView); + } catch { + + } + }; + + _hText.TextChanged += (args) => { + try { + _hVal = int.Parse (_hText.Text.ToString ()); + DimPosChanged (_curView); + } catch { + + } + }; + + _hRadioGroup.SelectedItemChanged += (selected) => DimPosChanged (_curView); + + Top.Add (_leftPane, _settingsPane, _hostPane); + + Top.LayoutSubviews (); + + _curView = CreateClass (_viewClasses.First ().Value); + + int iterations = 0; + + Application.Iteration += () => { + iterations++; + + if (iterations < _viewClasses.Count) { + _classListView.MoveDown (); + Assert.Equal (_curView.GetType ().Name, + _viewClasses.Values.ToArray () [_classListView.SelectedItem].Name); + } else { + Application.RequestStop (); + } + }; + + Application.Run (); + + Assert.Equal (_viewClasses.Count, iterations); + + Application.Shutdown (); + + + void DimPosChanged (View view) + { + if (view == null) { + return; + } + + var layout = view.LayoutStyle; + + try { + view.LayoutStyle = LayoutStyle.Absolute; + + switch (_xRadioGroup.SelectedItem) { + case 0: + view.X = Pos.Percent (_xVal); + break; + case 1: + view.X = Pos.AnchorEnd (_xVal); + break; + case 2: + view.X = Pos.Center (); + break; + case 3: + view.X = Pos.At (_xVal); + break; + } + + switch (_yRadioGroup.SelectedItem) { + case 0: + view.Y = Pos.Percent (_yVal); + break; + case 1: + view.Y = Pos.AnchorEnd (_yVal); + break; + case 2: + view.Y = Pos.Center (); + break; + case 3: + view.Y = Pos.At (_yVal); + break; + } + + switch (_wRadioGroup.SelectedItem) { + case 0: + view.Width = Dim.Percent (_wVal); + break; + case 1: + view.Width = Dim.Fill (_wVal); + break; + case 2: + view.Width = Dim.Sized (_wVal); + break; + } + + switch (_hRadioGroup.SelectedItem) { + case 0: + view.Height = Dim.Percent (_hVal); + break; + case 1: + view.Height = Dim.Fill (_hVal); + break; + case 2: + view.Height = Dim.Sized (_hVal); + break; + } + } catch (Exception e) { + MessageBox.ErrorQuery ("Exception", e.Message, "Ok"); + } finally { + view.LayoutStyle = layout; + } + UpdateTitle (view); + } + + void UpdateSettings (View view) + { + var x = view.X.ToString (); + var y = view.Y.ToString (); + _xRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => x.Contains (s)).First ()); + _yRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => y.Contains (s)).First ()); + _xText.Text = $"{view.Frame.X}"; + _yText.Text = $"{view.Frame.Y}"; + + var w = view.Width.ToString (); + var h = view.Height.ToString (); + _wRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => w.Contains (s)).First ()); + _hRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => h.Contains (s)).First ()); + _wText.Text = $"{view.Frame.Width}"; + _hText.Text = $"{view.Frame.Height}"; + } + + void UpdateTitle (View view) + { + _hostPane.Title = $"{view.GetType ().Name} - {view.X.ToString ()}, {view.Y.ToString ()}, {view.Width.ToString ()}, {view.Height.ToString ()}"; + } + + List GetAllViewClassesCollection () + { + List types = new List (); + foreach (Type type in typeof (View).Assembly.GetTypes () + .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsPublic && myType.IsSubclassOf (typeof (View)))) { + types.Add (type); + } + return types; + } + + void ClearClass (View view) + { + // Remove existing class, if any + if (view != null) { + view.LayoutComplete -= LayoutCompleteHandler; + _hostPane.Remove (view); + view.Dispose (); + _hostPane.Clear (); + } + } + + View CreateClass (Type type) + { + // If we are to create a generic Type + if (type.IsGenericType) { + + // For each of the arguments + List typeArguments = new List (); + + // use + foreach (var arg in type.GetGenericArguments ()) { + typeArguments.Add (typeof (object)); + } + + // And change what type we are instantiating from MyClass to MyClass + type = type.MakeGenericType (typeArguments.ToArray ()); + } + // Instantiate view + var view = (View)Activator.CreateInstance (type); + + //_curView.X = Pos.Center (); + //_curView.Y = Pos.Center (); + view.Width = Dim.Percent (75); + view.Height = Dim.Percent (75); + + // Set the colorscheme to make it stand out if is null by default + if (view.ColorScheme == null) { + view.ColorScheme = Colors.Base; + } + + // If the view supports a Text property, set it so we have something to look at + if (view.GetType ().GetProperty ("Text") != null) { + try { + view.GetType ().GetProperty ("Text")?.GetSetMethod ()?.Invoke (view, new [] { ustring.Make ("Test Text") }); + } catch (TargetInvocationException e) { + MessageBox.ErrorQuery ("Exception", e.InnerException.Message, "Ok"); + view = null; + } + } + + // If the view supports a Title property, set it so we have something to look at + if (view != null && view.GetType ().GetProperty ("Title") != null) { + view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { ustring.Make ("Test Title") }); + } + + // If the view supports a Source property, set it so we have something to look at + if (view != null && view.GetType ().GetProperty ("Source") != null && view.GetType ().GetProperty ("Source").PropertyType == typeof (Terminal.Gui.IListDataSource)) { + var source = new ListWrapper (new List () { ustring.Make ("Test Text #1"), ustring.Make ("Test Text #2"), ustring.Make ("Test Text #3") }); + view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, new [] { source }); + } + + // Set Settings + _computedCheckBox.Checked = view.LayoutStyle == LayoutStyle.Computed; + + // Add + _hostPane.Add (view); + //DimPosChanged (); + _hostPane.LayoutSubviews (); + _hostPane.Clear (); + _hostPane.SetNeedsDisplay (); + UpdateSettings (view); + UpdateTitle (view); + + view.LayoutComplete += LayoutCompleteHandler; + + return view; + } + + void LayoutCompleteHandler (View.LayoutEventArgs args) + { + UpdateTitle (_curView); + } + } + } +} diff --git a/Terminal.Gui UnitTests/UnitTests.csproj b/Terminal.Gui UnitTests/UnitTests.csproj new file mode 100644 index 0000000000..f01db4c0c9 --- /dev/null +++ b/Terminal.Gui UnitTests/UnitTests.csproj @@ -0,0 +1,57 @@ + + + net6.0 + false + + + + + 1.0 + 1.0 + 1.0 + 1.0 + + + TRACE + + + TRACE;DEBUG_IDISPOSABLE + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + True + + + [UICatalog]* + + + + + + + + + + False + + + \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 8c831fdb2c..d8d38392f7 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -6,6 +6,7 @@ // using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -954,11 +955,13 @@ public override void UpdateOffScreen () public static bool Is_WSL_Platform () { - if (new CursesClipboard ().IsSupported) { - return false; - } - var result = BashRunner.Run ("uname -a", runCurses: false); - if (result.Contains ("microsoft") && result.Contains ("WSL")) { + // xclip does not work on WSL, so we need to use the Windows clipboard vis Powershell + //if (new CursesClipboard ().IsSupported) { + // // If xclip is installed on Linux under WSL, this will return true. + // return false; + //} + var (exitCode, result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true); + if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL")) { return true; } return false; @@ -1259,134 +1262,79 @@ static public bool Suspend () } } + /// + /// A clipboard implementation for Linux. + /// This implementation uses the xclip command to access the clipboard. + /// + /// + /// If xclip is not installed, this implementation will not work. + /// class CursesClipboard : ClipboardBase { public CursesClipboard () { IsSupported = CheckSupport (); } + string xclipPath = string.Empty; public override bool IsSupported { get; } bool CheckSupport () { try { - var result = BashRunner.Run ("which xclip", runCurses: false); - return result.FileExists (); + var (exitCode, result) = ClipboardProcessRunner.Bash ("which xclip", waitForOutput: true); + if (exitCode == 0 && result.FileExists ()) { + xclipPath = result; + return true; + } } catch (Exception) { // Permissions issue. - return false; } + return false; } protected override string GetClipboardDataImpl () { var tempFileName = System.IO.Path.GetTempFileName (); - try { - // BashRunner.Run ($"xsel -o --clipboard > {tempFileName}"); - BashRunner.Run ($"xclip -selection clipboard -o > {tempFileName}"); - return System.IO.File.ReadAllText (tempFileName); - } finally { - System.IO.File.Delete (tempFileName); - } - } - - protected override void SetClipboardDataImpl (string text) - { - // var tempFileName = System.IO.Path.GetTempFileName (); - // System.IO.File.WriteAllText (tempFileName, text); - // try { - // // BashRunner.Run ($"cat {tempFileName} | xsel -i --clipboard"); - // BashRunner.Run ($"cat {tempFileName} | xclip -selection clipboard"); - // } finally { - // System.IO.File.Delete (tempFileName); - // } - - BashRunner.Run ("xclip -selection clipboard -i", false, text); - } - } + var xclipargs = "-selection clipboard -o"; - static class BashRunner { - public static string Run (string commandLine, bool output = true, string inputText = "", bool runCurses = true) - { - var arguments = $"-c \"{commandLine}\""; - - if (output) { - var errorBuilder = new System.Text.StringBuilder (); - var outputBuilder = new System.Text.StringBuilder (); - - using (var process = new System.Diagnostics.Process { - StartInfo = new System.Diagnostics.ProcessStartInfo { - FileName = "bash", - Arguments = arguments, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = false, - } - }) { - process.Start (); - process.OutputDataReceived += (sender, args) => { outputBuilder.AppendLine (args.Data); }; - process.BeginOutputReadLine (); - process.ErrorDataReceived += (sender, args) => { errorBuilder.AppendLine (args.Data); }; - process.BeginErrorReadLine (); - if (!process.DoubleWaitForExit ()) { - var timeoutError = $@"Process timed out. Command line: bash {arguments}. - Output: {outputBuilder} - Error: {errorBuilder}"; - throw new Exception (timeoutError); - } - if (process.ExitCode == 0) { - if (runCurses && Application.Driver is CursesDriver) { - Curses.raw (); - Curses.noecho (); - } - return outputBuilder.ToString (); - } - - var error = $@"Could not execute process. Command line: bash {arguments}. - Output: {outputBuilder} - Error: {errorBuilder}"; - throw new Exception (error); - } - } else { - using (var process = new System.Diagnostics.Process { - StartInfo = new System.Diagnostics.ProcessStartInfo { - FileName = "bash", - Arguments = arguments, - RedirectStandardInput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = false - } - }) { - process.Start (); - process.StandardInput.Write (inputText); - process.StandardInput.Close (); - process.WaitForExit (); - if (runCurses && Application.Driver is CursesDriver) { + try { + var (exitCode, result) = ClipboardProcessRunner.Bash ($"{xclipPath} {xclipargs} > {tempFileName}", waitForOutput: false); + if (exitCode == 0) { + if (Application.Driver is CursesDriver) { Curses.raw (); Curses.noecho (); } - return inputText; + return System.IO.File.ReadAllText (tempFileName); } + } catch (Exception e) { + throw new NotSupportedException ($"\"{xclipPath} {xclipargs}\" failed.", e); + } finally { + System.IO.File.Delete (tempFileName); } + return string.Empty; } - public static bool DoubleWaitForExit (this System.Diagnostics.Process process) + protected override void SetClipboardDataImpl (string text) { - var result = process.WaitForExit (500); - if (result) { - process.WaitForExit (); + var xclipargs = "-selection clipboard -i"; + try { + var (exitCode, _) = ClipboardProcessRunner.Bash ($"{xclipPath} {xclipargs}", text, waitForOutput: false); + if (exitCode == 0 && Application.Driver is CursesDriver) { + Curses.raw (); + Curses.noecho (); + } + } catch (Exception e) { + throw new NotSupportedException ($"\"{xclipPath} {xclipargs} < {text}\" failed", e); } - return result; - } - - public static bool FileExists (this string value) - { - return !string.IsNullOrEmpty (value) && !value.Contains ("not found"); } } + /// + /// A clipboard implementation for MacOSX. + /// This implementation uses the Mac clipboard API (via P/Invoke) to copy/paste. + /// The existance of the Mac pbcopy and pbpaste commands + /// is used to determine if copy/paste is supported. + /// class MacOSXClipboard : ClipboardBase { IntPtr nsString = objc_getClass ("NSString"); IntPtr nsPasteboard = objc_getClass ("NSPasteboard"); @@ -1413,12 +1361,12 @@ public MacOSXClipboard () bool CheckSupport () { - var result = BashRunner.Run ("which pbcopy"); - if (!result.FileExists ()) { + var (exitCode, result) = ClipboardProcessRunner.Bash ("which pbcopy", waitForOutput: true); + if (exitCode != 0 || !result.FileExists ()) { return false; } - result = BashRunner.Run ("which pbpaste"); - return result.FileExists (); + (exitCode, result) = ClipboardProcessRunner.Bash ("which pbpaste", waitForOutput: true); + return exitCode == 0 && result.FileExists (); } protected override string GetClipboardDataImpl () @@ -1461,95 +1409,77 @@ protected override void SetClipboardDataImpl (string text) static extern IntPtr sel_registerName (string selectorName); } + /// + /// A clipboard implementation for Linux, when running under WSL. + /// This implementation uses the Windows clipboard to store the data, and uses Windows' + /// powershell.exe (launched via WSL interop services) to set/get the Windows + /// clipboard. + /// class WSLClipboard : ClipboardBase { + bool isSupported = false; public WSLClipboard () { - IsSupported = CheckSupport (); + isSupported = CheckSupport (); } - public override bool IsSupported { get; } + public override bool IsSupported { + get { + return isSupported = CheckSupport (); + } + } + + private static string powershellPath = string.Empty; bool CheckSupport () { - try { - var result = BashRunner.Run ("which powershell.exe"); - return result.FileExists (); - } catch (System.Exception) { - return false; - } + if (string.IsNullOrEmpty (powershellPath)) { + // Specify pwsh.exe (not pwsh) to ensure we get the Windows version (invoked via WSL) + var (exitCode, result) = ClipboardProcessRunner.Bash ("which pwsh.exe", waitForOutput: true); + if (exitCode > 0) { + (exitCode, result) = ClipboardProcessRunner.Bash ("which powershell.exe", waitForOutput: true); + } - //var result = BashRunner.Run ("which powershell.exe"); - //if (!result.FileExists ()) { - // return false; - //} - //result = BashRunner.Run ("which clip.exe"); - //return result.FileExists (); + if (exitCode == 0) { + powershellPath = result; + } + } + return !string.IsNullOrEmpty (powershellPath); } protected override string GetClipboardDataImpl () { - using (var powershell = new System.Diagnostics.Process { - StartInfo = new System.Diagnostics.ProcessStartInfo { - RedirectStandardOutput = true, - FileName = "powershell.exe", - Arguments = "-noprofile -command \"Get-Clipboard\"", - UseShellExecute = false, - CreateNoWindow = true - } - }) { - powershell.Start (); - var result = powershell.StandardOutput.ReadToEnd (); - powershell.StandardOutput.Close (); - if (!powershell.DoubleWaitForExit ()) { - var timeoutError = $@"Process timed out. Command line: bash {powershell.StartInfo.Arguments}. - Output: {powershell.StandardOutput.ReadToEnd ()} - Error: {powershell.StandardError.ReadToEnd ()}"; - throw new Exception (timeoutError); - } + if (!IsSupported) { + return string.Empty; + } + + var (exitCode, output) = ClipboardProcessRunner.Process (powershellPath, "-noprofile -command \"Get-Clipboard\""); + if (exitCode == 0) { if (Application.Driver is CursesDriver) { Curses.raw (); Curses.noecho (); } - if (result.EndsWith ("\r\n")) { - result = result.Substring (0, result.Length - 2); + + if (output.EndsWith ("\r\n")) { + output = output.Substring (0, output.Length - 2); } - return result; + return output; } + return string.Empty; } protected override void SetClipboardDataImpl (string text) { - using (var powershell = new System.Diagnostics.Process { - StartInfo = new System.Diagnostics.ProcessStartInfo { - FileName = "powershell.exe", - Arguments = $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\"" - } - }) { - powershell.Start (); - powershell.WaitForExit (); - if (!powershell.DoubleWaitForExit ()) { - var timeoutError = $@"Process timed out. Command line: bash {powershell.StartInfo.Arguments}. - Output: {powershell.StandardOutput.ReadToEnd ()} - Error: {powershell.StandardError.ReadToEnd ()}"; - throw new Exception (timeoutError); - } + if (!IsSupported) { + return; + } + + var (exitCode, output) = ClipboardProcessRunner.Process (powershellPath, $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\""); + if (exitCode == 0) { if (Application.Driver is CursesDriver) { Curses.raw (); Curses.noecho (); } } - - //using (var clipExe = new System.Diagnostics.Process { - // StartInfo = new System.Diagnostics.ProcessStartInfo { - // FileName = "clip.exe", - // RedirectStandardInput = true - // } - //}) { - // clipExe.Start (); - // clipExe.StandardInput.Write (text); - // clipExe.StandardInput.Close (); - // clipExe.WaitForExit (); - //} } } } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs index 64dbb3b188..30ebd56070 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs @@ -38,6 +38,11 @@ namespace Terminal.Gui { /// can watch file descriptors using the AddWatch methods. /// internal class UnixMainLoop : IMainLoopDriver { + public UnixMainLoop (ConsoleDriver consoleDriver = null) + { + // UnixDriver doesn't use the consoleDriver parameter, but the WindowsDriver does. + } + public const int KEY_RESIZE = unchecked((int)0xffffffffffffffff); [StructLayout (LayoutKind.Sequential)] diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index ff85286fd4..77526629b9 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -6,10 +6,12 @@ // using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using NStack; + // Alias Console to MockConsole so we don't accidentally use Console using Console = Terminal.Gui.FakeConsole; @@ -19,6 +21,27 @@ namespace Terminal.Gui { /// public class FakeDriver : ConsoleDriver { #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + + public class Behaviors { + + public bool UseFakeClipboard { get; internal set; } + public bool FakeClipboardAlwaysThrowsNotSupportedException { get; internal set; } + public bool FakeClipboardIsSupportedAlwaysFalse { get; internal set; } + + public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsNotSupportedException = false, bool fakeClipboardIsSupportedAlwaysTrue = false) + { + UseFakeClipboard = useFakeClipboard; + FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; + FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; + + // double check usage is correct + Debug.Assert (useFakeClipboard == false && fakeClipboardAlwaysThrowsNotSupportedException == false); + Debug.Assert (useFakeClipboard == false && fakeClipboardIsSupportedAlwaysTrue == false); + } + } + + public static FakeDriver.Behaviors FakeBehaviors = new Behaviors (); + int cols, rows, left, top; public override int Cols => cols; public override int Rows => rows; @@ -26,7 +49,8 @@ public class FakeDriver : ConsoleDriver { public override int Left => 0; public override int Top => 0; public override bool HeightAsBuffer { get; set; } - public override IClipboard Clipboard { get; } + private IClipboard clipboard = null; + public override IClipboard Clipboard => clipboard; // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag int [,,] contents; @@ -59,15 +83,19 @@ public class FakeDriver : ConsoleDriver { public FakeDriver () { - if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { - Clipboard = new WindowsClipboard (); - } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - Clipboard = new MacOSXClipboard (); + if (FakeBehaviors.UseFakeClipboard) { + clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse); } else { - if (CursesDriver.Is_WSL_Platform ()) { - Clipboard = new WSLClipboard (); + if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { + clipboard = new WindowsClipboard (); + } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { + clipboard = new MacOSXClipboard (); } else { - Clipboard = new CursesClipboard (); + if (CursesDriver.Is_WSL_Platform ()) { + clipboard = new WSLClipboard (); + } else { + clipboard = new CursesClipboard (); + } } } } @@ -646,6 +674,41 @@ public override void UncookMouse () } #endregion + + public class FakeClipboard : ClipboardBase { + public Exception FakeException = null; + + string contents = string.Empty; + + bool isSupportedAlwaysFalse = false; + + public override bool IsSupported => !isSupportedAlwaysFalse; + + public FakeClipboard (bool fakeClipboardThrowsNotSupportedException = false, bool isSupportedAlwaysFalse = false) + { + this.isSupportedAlwaysFalse = isSupportedAlwaysFalse; + if (fakeClipboardThrowsNotSupportedException) { + FakeException = new NotSupportedException ("Fake clipboard exception"); + } + } + + protected override string GetClipboardDataImpl () + { + if (FakeException != null) { + throw FakeException; + } + return contents; + } + + protected override void SetClipboardDataImpl (string text) + { + if (FakeException != null) { + throw FakeException; + } + contents = text; + } + } + #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member } } \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs index d590729721..efa1fcec68 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs @@ -15,7 +15,7 @@ public class FakeMainLoop : IMainLoopDriver { AutoResetEvent waitForProbe = new AutoResetEvent (false); ConsoleKeyInfo? keyResult = null; MainLoop mainLoop; - Func consoleKeyReaderFn = null; + Func consoleKeyReaderFn = () => FakeConsole.ReadKey (true); /// /// Invoked when a Key is pressed. @@ -23,18 +23,12 @@ public class FakeMainLoop : IMainLoopDriver { public Action KeyPressed; /// - /// Initializes the class. + /// Creates an instance of the FakeMainLoop. is not used. /// - /// - /// Passing a consoleKeyReaderfn is provided to support unit test scenarios. - /// - /// The method to be called to get a key from the console. - public FakeMainLoop (Func consoleKeyReaderFn = null) + /// + public FakeMainLoop (ConsoleDriver consoleDriver = null) { - if (consoleKeyReaderFn == null) { - throw new ArgumentNullException ("key reader function must be provided."); - } - this.consoleKeyReaderFn = consoleKeyReaderFn; + // consoleDriver is not needed/used in FakeConsole } void WindowsKeyReader () diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index e024986079..b5f6e5984d 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -375,14 +375,14 @@ internal static void InternalInit (Func topLevelFactory, ConsoleDriver ResetState (); } - // FakeDriver (for UnitTests) + // For UnitTests if (driver != null) { - if (mainLoopDriver == null) { - throw new ArgumentNullException ("InternalInit mainLoopDriver cannot be null if driver is provided."); - } - if (!(driver is FakeDriver)) { - throw new InvalidOperationException ("InternalInit can only be called with FakeDriver."); - } + //if (mainLoopDriver == null) { + // throw new ArgumentNullException ("InternalInit mainLoopDriver cannot be null if driver is provided."); + //} + //if (!(driver is FakeDriver)) { + // throw new InvalidOperationException ("InternalInit can only be called with FakeDriver."); + //} Driver = driver; } @@ -391,18 +391,34 @@ internal static void InternalInit (Func topLevelFactory, ConsoleDriver if (ForceFakeConsole) { // For Unit Testing only Driver = new FakeDriver (); - mainLoopDriver = new FakeMainLoop (() => FakeConsole.ReadKey (true)); } else if (UseSystemConsole) { Driver = new NetDriver (); - mainLoopDriver = new NetMainLoop (Driver); } else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) { Driver = new WindowsDriver (); - mainLoopDriver = new WindowsMainLoop (Driver); } else { - mainLoopDriver = new UnixMainLoop (); Driver = new CursesDriver (); } + if (Driver == null) { + throw new InvalidOperationException ("Init could not determine the ConsoleDriver to use."); + } } + + if (mainLoopDriver == null) { + // TODO: Move this logic into ConsoleDriver + if (Driver is FakeDriver) { + mainLoopDriver = new FakeMainLoop (Driver); + } else if (Driver is NetDriver) { + mainLoopDriver = new NetMainLoop (Driver); + } else if (Driver is WindowsDriver) { + mainLoopDriver = new WindowsMainLoop (Driver); + } else if (Driver is CursesDriver) { + mainLoopDriver = new UnixMainLoop (Driver); + } + if (mainLoopDriver == null) { + throw new InvalidOperationException ("Init could not determine the MainLoopDriver to use."); + } + } + MainLoop = new MainLoop (mainLoopDriver); try { diff --git a/Terminal.Gui/Core/Clipboard/Clipboard.cs b/Terminal.Gui/Core/Clipboard/Clipboard.cs index 914dd0153c..4aed183da0 100644 --- a/Terminal.Gui/Core/Clipboard/Clipboard.cs +++ b/Terminal.Gui/Core/Clipboard/Clipboard.cs @@ -3,13 +3,32 @@ namespace Terminal.Gui { /// - /// Provides cut, copy, and paste support for the clipboard with OS interaction. + /// Provides cut, copy, and paste support for the OS clipboard. /// + /// + /// + /// On Windows, the class uses the Windows Clipboard APIs via P/Invoke. + /// + /// + /// On Linux, when not running under Windows Subsystem for Linux (WSL), + /// the class uses the xclip command line tool. If xclip is not installed, + /// the clipboard will not work. + /// + /// + /// On Linux, when running under Windows Subsystem for Linux (WSL), + /// the class launches Windows' powershell.exe via WSL interop and uses the + /// "Set-Clipboard" and "Get-Clipboard" Powershell CmdLets. + /// + /// + /// On the Mac, the class uses the MacO OS X pbcopy and pbpaste command line tools + /// and the Mac clipboard APIs vai P/Invoke. + /// + /// public static class Clipboard { static ustring contents; /// - /// Get or sets the operation system clipboard, otherwise the contents field. + /// Gets (copies from) or sets (pastes to) the contents of the OS clipboard. /// public static ustring Contents { get { @@ -25,10 +44,15 @@ public static ustring Contents { } set { try { - if (IsSupported && value != null) { + if (IsSupported) { + if (value == null) { + value = string.Empty; + } Application.Driver.Clipboard.SetClipboardData (value.ToString ()); } contents = value; + } catch (NotSupportedException e) { + throw e; } catch (Exception) { contents = value; } @@ -38,32 +62,35 @@ public static ustring Contents { /// /// Returns true if the environmental dependencies are in place to interact with the OS clipboard. /// + /// + /// public static bool IsSupported { get => Application.Driver.Clipboard.IsSupported; } /// - /// Gets the operation system clipboard if possible. + /// Copies the contents of the OS clipboard to if possible. /// - /// Clipboard contents read - /// true if it was possible to read the OS clipboard. + /// The contents of the OS clipboard if successful, if not. + /// the OS clipboard was retrieved, otherwise. public static bool TryGetClipboardData (out string result) { - if (Application.Driver.Clipboard.TryGetClipboardData (out result)) { + if (IsSupported && Application.Driver.Clipboard.TryGetClipboardData (out result)) { if (contents != result) { contents = result; } return true; } + result = string.Empty; return false; } /// - /// Sets the operation system clipboard if possible. + /// Pastes the to the OS clipboard if possible. /// - /// - /// True if the clipboard content was set successfully. + /// The text to paste to the OS clipboard. + /// the OS clipboard was set, otherwise. public static bool TrySetClipboardData (string text) { - if (Application.Driver.Clipboard.TrySetClipboardData (text)) { + if (IsSupported && Application.Driver.Clipboard.TrySetClipboardData (text)) { contents = text; return true; } diff --git a/Terminal.Gui/Core/Clipboard/ClipboardBase.cs b/Terminal.Gui/Core/Clipboard/ClipboardBase.cs index 9eb17e174f..ee2e437d41 100644 --- a/Terminal.Gui/Core/Clipboard/ClipboardBase.cs +++ b/Terminal.Gui/Core/Clipboard/ClipboardBase.cs @@ -15,48 +15,52 @@ public abstract class ClipboardBase : IClipboard { public abstract bool IsSupported { get; } /// - /// Get the operation system clipboard. + /// Returns the contents of the OS clipboard if possible. /// - /// Thrown if it was not possible to read the clipboard contents + /// The contents of the OS clipboard if successful. + /// Thrown if it was not possible to copy from the OS clipboard. public string GetClipboardData () { try { return GetClipboardDataImpl (); - } catch (Exception ex) { - throw new NotSupportedException ("Failed to read clipboard.", ex); + } catch (NotSupportedException ex) { + throw new NotSupportedException ("Failed to copy from the OS clipboard.", ex); } } /// - /// Get the operation system clipboard. + /// Returns the contents of the OS clipboard if possible. Implemented by -specific subclasses. /// + /// The contents of the OS clipboard if successful. + /// Thrown if it was not possible to copy from the OS clipboard. protected abstract string GetClipboardDataImpl (); /// - /// Sets the operation system clipboard. + /// Pastes the to the OS clipboard if possible. /// - /// - /// Thrown if it was not possible to set the clipboard contents + /// The text to paste to the OS clipboard. + /// Thrown if it was not possible to paste to the OS clipboard. public void SetClipboardData (string text) { try { SetClipboardDataImpl (text); - } catch (Exception ex) { - throw new NotSupportedException ("Failed to write to clipboard.", ex); + } catch (NotSupportedException ex) { + throw new NotSupportedException ("Failed to paste to the OS clipboard.", ex); } } /// - /// Sets the operation system clipboard. + /// Pastes the to the OS clipboard if possible. Implemented by -specific subclasses. /// - /// + /// The text to paste to the OS clipboard. + /// Thrown if it was not possible to paste to the OS clipboard. protected abstract void SetClipboardDataImpl (string text); /// - /// Gets the operation system clipboard if possible. + /// Copies the contents of the OS clipboard to if possible. /// - /// Clipboard contents read - /// true if it was possible to read the OS clipboard. + /// The contents of the OS clipboard if successful, if not. + /// the OS clipboard was retrieved, otherwise. public bool TryGetClipboardData (out string result) { // Don't even try to read because environment is not set up. @@ -71,17 +75,18 @@ public bool TryGetClipboardData (out string result) result = GetClipboardDataImpl (); } return true; - } catch (Exception) { + } catch (NotSupportedException ex) { + System.Diagnostics.Debug.WriteLine ($"TryGetClipboardData: {ex.Message}"); result = null; return false; } } /// - /// Sets the operation system clipboard if possible. + /// Pastes the to the OS clipboard if possible. /// - /// - /// True if the clipboard content was set successfully + /// The text to paste to the OS clipboard. + /// the OS clipboard was set, otherwise. public bool TrySetClipboardData (string text) { // Don't even try to set because environment is not set up @@ -92,7 +97,7 @@ public bool TrySetClipboardData (string text) try { SetClipboardDataImpl (text); return true; - } catch (Exception ex) { + } catch (NotSupportedException ex) { System.Diagnostics.Debug.WriteLine ($"TrySetClipboardData: {ex.Message}"); return false; } diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index edd0a8d74d..300191b150 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -8,8 +8,11 @@ using NStack; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Unix.Terminal; namespace Terminal.Gui { /// @@ -1389,4 +1392,74 @@ public void CreateColors (bool hasColors = true) Colors.Error.Disabled = MakeColor (Color.DarkGray, Color.White); } } + + /// + /// Helper class for console drivers to invoke shell commands to interact with the clipboard. + /// Used primarily by CursesDriver, but also used in Unit tests which is why it is in + /// ConsoleDriver.cs. + /// + internal static class ClipboardProcessRunner { + public static (int exitCode, string result) Bash (string commandLine, string inputText = "", bool waitForOutput = false) + { + var arguments = $"-c \"{commandLine}\""; + var (exitCode, result) = Process ("bash", arguments, inputText, waitForOutput); + + return (exitCode, result.TrimEnd ()); + } + + public static (int exitCode, string result) Process (string cmd, string arguments, string input = null, bool waitForOutput = true) + { + var output = string.Empty; + + using (Process process = new Process { + StartInfo = new ProcessStartInfo { + FileName = cmd, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }) { + var eventHandled = new TaskCompletionSource (); + process.Start (); + if (!string.IsNullOrEmpty (input)) { + process.StandardInput.Write (input); + process.StandardInput.Close (); + } + + if (!process.WaitForExit (5000)) { + var timeoutError = $@"Process timed out. Command line: {process.StartInfo.FileName} {process.StartInfo.Arguments}."; + throw new TimeoutException (timeoutError); + } + + if (waitForOutput && process.StandardOutput.Peek () != -1) { + output = process.StandardOutput.ReadToEnd (); + } + + if (process.ExitCode > 0) { + output = $@"Process failed to run. Command line: {cmd} {arguments}. + Output: {output} + Error: {process.StandardError.ReadToEnd ()}"; + } + + return (process.ExitCode, output); + } + } + + public static bool DoubleWaitForExit (this System.Diagnostics.Process process) + { + var result = process.WaitForExit (500); + if (result) { + process.WaitForExit (); + } + return result; + } + + public static bool FileExists (this string value) + { + return !string.IsNullOrEmpty (value) && !value.Contains ("not found"); + } + } } diff --git a/Terminal.Gui/Core/MainLoop.cs b/Terminal.Gui/Core/MainLoop.cs index 9b6492fe04..4ec3824f6f 100644 --- a/Terminal.Gui/Core/MainLoop.cs +++ b/Terminal.Gui/Core/MainLoop.cs @@ -102,7 +102,8 @@ public ReadOnlyCollection> IdleHandlers { /// /// Creates a new Mainloop. /// - /// Should match the (one of the implementations UnixMainLoop, NetMainLoop or WindowsMainLoop). + /// Should match the + /// (one of the implementations FakeMainLoop, UnixMainLoop, NetMainLoop or WindowsMainLoop). public MainLoop (IMainLoopDriver driver) { Driver = driver; diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 7ef00076c2..ce22fd3cd0 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -10,12 +10,12 @@ - 1.0 - 1.0 - 1.0 - 1.0 + 1.9 + 1.9 + 1.9 + diff --git a/Terminal.Gui/Windows/FileDialog.cs b/Terminal.Gui/Windows/FileDialog.cs index 878c8f143b..c2d20f3f12 100644 --- a/Terminal.Gui/Windows/FileDialog.cs +++ b/Terminal.Gui/Windows/FileDialog.cs @@ -83,6 +83,7 @@ where IsAllowed (x) && (!canChooseFiles ? x.Attributes.HasFlag (FileAttributes.D case DirectoryNotFoundException _: case ArgumentException _: dirInfo = null; + watcher?.Dispose (); watcher = null; infos.Clear (); valid = true; @@ -104,7 +105,15 @@ protected override void Dispose (bool disposing) { if (!_disposedValue) { if (disposing) { + if (watcher != null) { + watcher.Changed -= Watcher_Changed; + watcher.Created -= Watcher_Changed; + watcher.Deleted -= Watcher_Changed; + watcher.Renamed -= Watcher_Changed; + watcher.Error -= Watcher_Error; + } watcher?.Dispose (); + watcher = null; } _disposedValue = true; diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json index ecd1a78c78..b4df9cb03a 100644 --- a/UICatalog/Properties/launchSettings.json +++ b/UICatalog/Properties/launchSettings.json @@ -3,10 +3,16 @@ "UICatalog": { "commandName": "Project" }, - "UICatalog : -usc": { + "UICatalog -usc": { "commandName": "Project", "commandLineArgs": "-usc" }, + "WSL: UICatalog -usc": { + "commandName": "Executable", + "executablePath": "wsl", + "commandLineArgs": "dotnet UICatalog.dll -usc", + "distributionName": "" + }, "Wizards": { "commandName": "Project", "commandLineArgs": "Wizards" @@ -35,20 +41,6 @@ "commandName": "Project", "commandLineArgs": "\"Character Map\"" }, - "WSL2": { - "commandName": "Executable", - "executablePath": "wsl", - "commandLineArgs": "dotnet UICatalog.dll" - }, - "WSL2 : -usc": { - "commandName": "Executable", - "executablePath": "wsl", - "commandLineArgs": "dotnet UICatalog.dll -usc" - }, - "WSL": { - "commandName": "WSL2", - "distributionName": "" - }, "All Views Tester": { "commandName": "Project", "commandLineArgs": "\"All Views Tester\"" @@ -56,6 +48,12 @@ "Windows & FrameViews": { "commandName": "Project", "commandLineArgs": "\"Windows & FrameViews\"" + }, + "WSL : UICatalog": { + "commandName": "Executable", + "executablePath": "wsl", + "commandLineArgs": "dotnet UICatalog.dll", + "distributionName": "" } } } \ No newline at end of file diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 6eda3a686e..0958a1a4f1 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -343,7 +343,7 @@ private void ReplaceAll () private bool CanCloseFile () { if (_textView.Text == _originalText) { - System.Diagnostics.Debug.Assert (!_textView.IsDirty); + //System.Diagnostics.Debug.Assert (!_textView.IsDirty); return true; } diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index bdb6dbe219..14cd47ecec 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -4,10 +4,10 @@ using System.Diagnostics; using System.Globalization; using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; using System.Text; using Terminal.Gui; +using Microsoft.DotNet.PlatformAbstractions; using Rune = System.Rune; /// @@ -160,6 +160,7 @@ class UICatalogTopLevel : Toplevel { public StatusItem Numlock; public StatusItem Scrolllock; public StatusItem DriverName; + public StatusItem OS; public UICatalogTopLevel () { @@ -177,19 +178,17 @@ public UICatalogTopLevel () "About UI Catalog", () => MessageBox.Query ("About UI Catalog", _aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A), }), }); - + Capslock = new StatusItem (Key.CharMask, "Caps", null); Numlock = new StatusItem (Key.CharMask, "Num", null); Scrolllock = new StatusItem (Key.CharMask, "Scroll", null); DriverName = new StatusItem (Key.CharMask, "Driver:", null); + OS = new StatusItem (Key.CharMask, "OS:", null); StatusBar = new StatusBar () { Visible = true, }; StatusBar.Items = new StatusItem [] { - Capslock, - Numlock, - Scrolllock, new StatusItem(Key.Q | Key.CtrlMask, "~CTRL-Q~ Quit", () => { if (_selectedScenario is null){ // This causes GetScenarioToRun to return null @@ -199,7 +198,7 @@ public UICatalogTopLevel () _selectedScenario.RequestStop(); } }), - new StatusItem(Key.F10, "~F10~ Hide/Show Status Bar", () => { + new StatusItem(Key.F10, "~F10~ Status Bar", () => { StatusBar.Visible = !StatusBar.Visible; LeftPane.Height = Dim.Fill(StatusBar.Visible ? 1 : 0); RightPane.Height = Dim.Fill(StatusBar.Visible ? 1 : 0); @@ -207,6 +206,7 @@ public UICatalogTopLevel () SetChildNeedsDisplay(); }), DriverName, + OS }; LeftPane = new FrameView ("Categories") { @@ -281,6 +281,7 @@ void LoadedHandler () miIsMouseDisabled.Checked = Application.IsMouseDisabled; miHeightAsBuffer.Checked = Application.HeightAsBuffer; DriverName.Title = $"Driver: {Driver.GetType ().Name}"; + OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}"; if (_selectedScenario != null) { _selectedScenario = null; diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index ea6dc254cc..40d346438f 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -1,7 +1,7 @@  Exe - net6.0 + net7.0 8.0 UICatalog.UICatalogApp @@ -20,6 +20,7 @@ + diff --git a/UnitTests/AllViewsTests.cs b/UnitTests/AllViewsTests.cs index 5b62591f77..a68d2f5104 100644 --- a/UnitTests/AllViewsTests.cs +++ b/UnitTests/AllViewsTests.cs @@ -10,7 +10,7 @@ public class AllViewsTests { [Fact] public void AllViews_Tests_All_Constructors () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); foreach (var type in GetAllViewClassesCollection ()) { Assert.True (Constructors_FullTest (type)); @@ -127,7 +127,7 @@ List GetAllViewClassesCollection () public void AllViews_Enter_Leave_Events () { foreach (var type in GetAllViewClassesCollection ()) { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; var vType = GetTypeInitializer (type, type.GetConstructor (Array.Empty ())); diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs index 43eaf27ad7..b9bc4101cf 100644 --- a/UnitTests/ApplicationTests.cs +++ b/UnitTests/ApplicationTests.cs @@ -45,7 +45,7 @@ void Post_Init_State () void Init () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); Assert.NotNull (Application.Driver); Assert.NotNull (Application.MainLoop); Assert.NotNull (SynchronizationContext.Current); @@ -62,7 +62,7 @@ public void Init_Shutdown_Cleans_Up () // Verify initial state is per spec Pre_Init_State (); - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); // Verify post-Init state is correct Post_Init_State (); @@ -75,33 +75,36 @@ public void Init_Shutdown_Cleans_Up () // Verify state is back to initial Pre_Init_State (); - +#if DEBUG_IDISPOSABLE // Validate there are no outstanding Responder-based instances // after a scenario was selected to run. This proves the main UI Catalog // 'app' closed cleanly. foreach (var inst in Responder.Instances) { Assert.True (inst.WasDisposed); } +#endif } [Fact] public void Init_Shutdown_Toplevel_Not_Disposed () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); Application.Shutdown (); +#if DEBUG_IDISPOSABLE Assert.Single (Responder.Instances); Assert.True (Responder.Instances [0].WasDisposed); +#endif } [Fact] public void Init_Unbalanced_Throwss () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); Toplevel topLevel = null; - Assert.Throws (() => Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)))); + Assert.Throws (() => Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ())); Shutdown (); Assert.Null (Application.Top); @@ -110,9 +113,9 @@ public void Init_Unbalanced_Throwss () // Now try the other way topLevel = null; - Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ()); - Assert.Throws (() => Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)))); + Assert.Throws (() => Application.Init (new FakeDriver ())); Shutdown (); Assert.Null (Application.Top); @@ -182,7 +185,7 @@ public void InitWithTopLevelFactory_Begin_End_Cleans_Up () // NOTE: Run, when called after Init has been called behaves differently than // when called if Init has not been called. Toplevel topLevel = null; - Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.InternalInit (() => topLevel = new TestToplevel (), new FakeDriver ()); Application.RunState runstate = null; Action NewRunStateFn = (rs) => { @@ -255,7 +258,7 @@ public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Throws () Init (); // Run when already initialized with a Driver will throw (because Toplevel is not dervied from TopLevel) - Assert.Throws (() => Application.Run (errorHandler: null, new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)))); + Assert.Throws (() => Application.Run (errorHandler: null, new FakeDriver ())); Shutdown (); @@ -354,7 +357,7 @@ public void Run_T_NoInit_WithDriver_DoesNotThrow () }; // Init has NOT been called and we're passing a valid driver to Run. This is ok. - Application.Run (errorHandler: null, new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Run (errorHandler: null, new FakeDriver ()); Shutdown (); diff --git a/UnitTests/AttributeTests.cs b/UnitTests/AttributeTests.cs index 7137e0878f..2b2684e211 100644 --- a/UnitTests/AttributeTests.cs +++ b/UnitTests/AttributeTests.cs @@ -13,7 +13,7 @@ public class AttributeTests { public void Constuctors_Constuct () { var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); driver.Init (() => { }); // Test parameterless constructor @@ -59,7 +59,7 @@ public void Constuctors_Constuct () public void Implicit_Assign () { var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); driver.Init (() => { }); var attr = new Attribute (); @@ -100,7 +100,7 @@ public void Make_Asserts_IfNotInit () public void Make_Creates () { var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); driver.Init (() => { }); var fg = new Color (); @@ -128,7 +128,7 @@ public void Get_Asserts_IfNotInit () public void Get_Gets () { var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); driver.Init (() => { }); var value = 42; diff --git a/UnitTests/ClipboardTests.cs b/UnitTests/ClipboardTests.cs index 25c59d180b..6cbb6302c7 100644 --- a/UnitTests/ClipboardTests.cs +++ b/UnitTests/ClipboardTests.cs @@ -1,25 +1,104 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; using System.Runtime.InteropServices; using Xunit; +using Xunit.Abstractions; +using static AutoInitShutdownAttribute; -namespace Terminal.Gui.Core { +namespace Terminal.Gui.ConsoleDrivers { public class ClipboardTests { - [Fact] - [AutoInitShutdown] + readonly ITestOutputHelper output; + + public ClipboardTests (ITestOutputHelper output) + { + this.output = output; + } + + [Fact, AutoInitShutdown (useFakeClipboard: true, fakeClipboardAlwaysThrowsNotSupportedException: true)] + public void IClipboard_GetClipBoardData_Throws_NotSupportedException () + { + IClipboard iclip = Application.Driver.Clipboard; + Assert.Throws (() => iclip.GetClipboardData ()); + } + + [Fact, AutoInitShutdown (useFakeClipboard: true, fakeClipboardAlwaysThrowsNotSupportedException: true)] + public void IClipboard_SetClipBoardData_Throws_NotSupportedException () + { + IClipboard iclip = Application.Driver.Clipboard; + Assert.Throws (() => iclip.SetClipboardData ("foo")); + } + + [Fact, AutoInitShutdown (useFakeClipboard: true)] + public void Contents_Fake_Gets_Sets () + { + if (!Clipboard.IsSupported) { + output.WriteLine ($"The Clipboard not supported on this platform."); + return; + } + + var clipText = "The Contents_Gets_Sets unit test pasted this to the OS clipboard."; + Clipboard.Contents = clipText; + + Application.Iteration += () => Application.RequestStop (); + Application.Run (); + + Assert.Equal (clipText, Clipboard.Contents.ToString ()); + } + + [Fact, AutoInitShutdown (useFakeClipboard: false)] public void Contents_Gets_Sets () { - var clipText = "This is a clipboard unit test."; + if (!Clipboard.IsSupported) { + output.WriteLine ($"The Clipboard not supported on this platform."); + return; + } + + var clipText = "The Contents_Gets_Sets unit test pasted this to the OS clipboard."; Clipboard.Contents = clipText; Application.Iteration += () => Application.RequestStop (); + Application.Run (); + + Assert.Equal (clipText, Clipboard.Contents.ToString ()); + } + + [Fact, AutoInitShutdown (useFakeClipboard: false)] + public void Contents_Gets_Sets_When_IsSupportedFalse () + { + + if (!Clipboard.IsSupported) { + output.WriteLine ($"The Clipboard not supported on this platform."); + return; + } + + var clipText = "The Contents_Gets_Sets unit test pasted this to the OS clipboard."; + Clipboard.Contents = clipText; + Application.Iteration += () => Application.RequestStop (); Application.Run (); - Assert.Equal (clipText, Clipboard.Contents); + Assert.Equal (clipText, Clipboard.Contents.ToString ()); } - [Fact] - [AutoInitShutdown] + [Fact, AutoInitShutdown (useFakeClipboard: true)] + public void Contents_Fake_Gets_Sets_When_IsSupportedFalse () + { + + if (!Clipboard.IsSupported) { + output.WriteLine ($"The Clipboard not supported on this platform."); + return; + } + + var clipText = "The Contents_Gets_Sets unit test pasted this to the OS clipboard."; + Clipboard.Contents = clipText; + + Application.Iteration += () => Application.RequestStop (); + Application.Run (); + + Assert.Equal (clipText, Clipboard.Contents.ToString ()); + } + + [Fact, AutoInitShutdown (useFakeClipboard: false)] public void IsSupported_Get () { if (Clipboard.IsSupported) { @@ -29,11 +108,10 @@ public void IsSupported_Get () } } - [Fact] - [AutoInitShutdown] + [Fact, AutoInitShutdown (useFakeClipboard: false)] public void TryGetClipboardData_Gets_From_OS_Clipboard () { - var clipText = "Trying to get from the OS clipboard."; + var clipText = "The TryGetClipboardData_Gets_From_OS_Clipboard unit test pasted this to the OS clipboard."; Clipboard.Contents = clipText; Application.Iteration += () => Application.RequestStop (); @@ -49,11 +127,10 @@ public void TryGetClipboardData_Gets_From_OS_Clipboard () } } - [Fact] - [AutoInitShutdown] + [Fact, AutoInitShutdown (useFakeClipboard: false)] public void TrySetClipboardData_Sets_The_OS_Clipboard () { - var clipText = "Trying to set the OS clipboard."; + var clipText = "The TrySetClipboardData_Sets_The_OS_Clipboard unit test pasted this to the OS clipboard."; if (Clipboard.IsSupported) { Assert.True (Clipboard.TrySetClipboardData (clipText)); } else { @@ -71,113 +148,67 @@ public void TrySetClipboardData_Sets_The_OS_Clipboard () } } - [Fact] - [AutoInitShutdown] - public void Contents_Gets_From_OS_Clipboard () + + [Fact, AutoInitShutdown (useFakeClipboard: false)] + public void Contents_Copies_From_OS_Clipboard () { - var clipText = "This is a clipboard unit test to get clipboard from OS."; - var exit = false; + if (!Clipboard.IsSupported) { + output.WriteLine ($"The Clipboard not supported on this platform."); + return; + } + + var clipText = "The Contents_Copies_From_OS_Clipboard unit test pasted this to the OS clipboard."; + var failed = false; var getClipText = ""; Application.Iteration += () => { + int exitCode = 0; + string result = ""; + output.WriteLine ($"Pasting to OS clipboard: {clipText}..."); + if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { - // using (Process clipExe = new Process { - // StartInfo = new ProcessStartInfo { - // RedirectStandardInput = true, - // FileName = "clip" - // } - // }) { - // clipExe.Start (); - // clipExe.StandardInput.Write (clipText); - // clipExe.StandardInput.Close (); - // var result = clipExe.WaitForExit (500); - // if (result) { - // clipExe.WaitForExit (); - // } - // } - - using (Process pwsh = new Process { - StartInfo = new ProcessStartInfo { - FileName = "powershell", - Arguments = $"-command \"Set-Clipboard -Value \\\"{clipText}\\\"\"" - } - }) { - pwsh.Start (); - pwsh.WaitForExit (); - } + (exitCode, result) = ClipboardProcessRunner.Process ("pwsh", $"-command \"Set-Clipboard -Value \\\"{clipText}\\\"\""); + output.WriteLine ($" Windows: pwsh Set-Clipboard: exitCode = {exitCode}, result = {result}"); getClipText = Clipboard.Contents.ToString (); } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - using (Process copy = new Process { - StartInfo = new ProcessStartInfo { - RedirectStandardInput = true, - FileName = "pbcopy" - } - }) { - copy.Start (); - copy.StandardInput.Write (clipText); - copy.StandardInput.Close (); - copy.WaitForExit (); - } + (exitCode, result) = ClipboardProcessRunner.Process ("pbcopy", string.Empty, clipText); + output.WriteLine ($" OSX: pbcopy: exitCode = {exitCode}, result = {result}"); getClipText = Clipboard.Contents.ToString (); } else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) { if (Is_WSL_Platform ()) { try { - using (Process bash = new Process { - StartInfo = new ProcessStartInfo { - FileName = "powershell.exe", - Arguments = $"-noprofile -command \"Set-Clipboard -Value \\\"{clipText}\\\"\"" - } - }) { - bash.Start (); - bash.WaitForExit (); - } - - //using (Process clipExe = new Process { - // StartInfo = new ProcessStartInfo { - // RedirectStandardInput = true, - // FileName = "clip.exe" - // } - //}) { - // clipExe.Start (); - // clipExe.StandardInput.Write (clipText); - // clipExe.StandardInput.Close (); - // clipExe.WaitForExit (); - // //var result = clipExe.WaitForExit (500); - // //if (result) { - // // clipExe.WaitForExit (); - // //} - //} + // This runs the WINDOWS version of powershell.exe via WSL. + (exitCode, result) = ClipboardProcessRunner.Process ("powershell.exe", $"-noprofile -command \"Set-Clipboard -Value \\\"{clipText}\\\"\""); + output.WriteLine ($" WSL: powershell.exe Set-Clipboard: exitCode = {exitCode}, result = {result}"); } catch { - exit = true; + failed = true; } - if (!exit) { + + if (!failed) { + // If we set the OS clipboard via Powershell, then getting Contents should return the same text. getClipText = Clipboard.Contents.ToString (); + output.WriteLine ($" WSL: Clipboard.Contents: {getClipText}"); } Application.RequestStop (); return; } - if (exit = xclipExists () == false) { - // xclip doesn't exist then exit. + + if (failed = xclipExists () == false) { + // if xclip doesn't exist then exit. + output.WriteLine ($" WSL: no xclip found."); Application.RequestStop (); return; } - using (Process bash = new Process { - StartInfo = new ProcessStartInfo { - FileName = "bash", - Arguments = $"-c \"xclip -sel clip -i\"", - RedirectStandardInput = true, - } - }) { - bash.Start (); - bash.StandardInput.Write (clipText); - bash.StandardInput.Close (); - bash.WaitForExit (); - } - if (!exit) { + // If we get here, powershell didn't work and xclip exists... + (exitCode, result) = ClipboardProcessRunner.Process ("bash", $"-c \"xclip -sel clip -i\"", clipText); + output.WriteLine ($" Linux: bash xclip -sel clip -i: exitCode = {exitCode}, result = {result}"); + + if (!failed) { getClipText = Clipboard.Contents.ToString (); + output.WriteLine ($" Linux via xclip: Clipboard.Contents: {getClipText}"); } } @@ -186,84 +217,57 @@ public void Contents_Gets_From_OS_Clipboard () Application.Run (); - if (!exit) { + if (!failed) { Assert.Equal (clipText, getClipText); } } - [Fact] - [AutoInitShutdown] - public void Contents_Sets_The_OS_Clipboard () + [Fact, AutoInitShutdown (useFakeClipboard: false)] + public void Contents_Pastes_To_OS_Clipboard () { - var clipText = "This is a clipboard unit test to set the OS clipboard."; + if (!Clipboard.IsSupported) { + output.WriteLine ($"The Clipboard not supported on this platform."); + return; + } + + var clipText = "The Contents_Pastes_To_OS_Clipboard unit test pasted this via Clipboard.Contents."; var clipReadText = ""; - var exit = false; + var failed = false; Application.Iteration += () => { Clipboard.Contents = clipText; + int exitCode = 0; + output.WriteLine ($"Getting OS clipboard..."); + if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { - using (Process pwsh = new Process { - StartInfo = new ProcessStartInfo { - RedirectStandardOutput = true, - FileName = "powershell.exe", - Arguments = "-noprofile -command \"Get-Clipboard\"" - } - }) { - pwsh.Start (); - clipReadText = pwsh.StandardOutput.ReadToEnd ().TrimEnd (); - pwsh.StandardOutput.Close (); - pwsh.WaitForExit (); - } + (exitCode, clipReadText) = ClipboardProcessRunner.Process ("pwsh", "-noprofile -command \"Get-Clipboard\""); + output.WriteLine ($" Windows: pwsh Get-Clipboard: exitCode = {exitCode}, result = {clipReadText}"); + } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - using (Process paste = new Process { - StartInfo = new ProcessStartInfo { - RedirectStandardOutput = true, - FileName = "pbpaste" - } - }) { - paste.Start (); - clipReadText = paste.StandardOutput.ReadToEnd (); - paste.StandardOutput.Close (); - paste.WaitForExit (); - } + (exitCode, clipReadText) = ClipboardProcessRunner.Process ("pbpaste", ""); + output.WriteLine ($" OSX: pbpaste: exitCode = {exitCode}, result = {clipReadText}"); + } else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) { if (Is_WSL_Platform ()) { - try { - using (Process bash = new Process { - StartInfo = new ProcessStartInfo { - RedirectStandardOutput = true, - FileName = "powershell.exe", - Arguments = "-noprofile -command \"Get-Clipboard\"" - } - }) { - bash.Start (); - clipReadText = bash.StandardOutput.ReadToEnd (); - bash.StandardOutput.Close (); - bash.WaitForExit (); - } - } catch { - exit = true; + (exitCode, clipReadText) = ClipboardProcessRunner.Process ("powershell.exe", "-noprofile -command \"Get-Clipboard\""); + output.WriteLine ($" WSL: powershell.exe Get-Clipboard: exitCode = {exitCode}, result = {clipReadText}"); + if (exitCode == 0) { + Application.RequestStop (); + return; } - Application.RequestStop (); + failed = true; } - if (exit = xclipExists () == false) { + + if (failed = xclipExists () == false) { // xclip doesn't exist then exit. Application.RequestStop (); + return; } - using (Process bash = new Process { - StartInfo = new ProcessStartInfo { - RedirectStandardOutput = true, - FileName = "bash", - Arguments = $"-c \"xclip -sel clip -o\"" - } - }) { - bash.Start (); - clipReadText = bash.StandardOutput.ReadToEnd (); - bash.StandardOutput.Close (); - bash.WaitForExit (); - } + (exitCode, clipReadText) = ClipboardProcessRunner.Process ("bash", $"-c \"xclip -sel clip -o\""); + output.WriteLine ($" Linux: bash xclip -sel clip -o: exitCode = {exitCode}, result = {clipReadText}"); + Assert.Equal (0, exitCode); } Application.RequestStop (); @@ -271,51 +275,23 @@ public void Contents_Sets_The_OS_Clipboard () Application.Run (); - if (!exit) { - Assert.Equal (clipText, clipReadText); + if (!failed) { + Assert.Equal (clipText, clipReadText.TrimEnd ()); } + } bool Is_WSL_Platform () { - using (Process bash = new Process { - StartInfo = new ProcessStartInfo { - FileName = "bash", - Arguments = $"-c \"uname -a\"", - RedirectStandardOutput = true, - } - }) { - bash.Start (); - var result = bash.StandardOutput.ReadToEnd (); - var isWSL = false; - if (result.Contains ("microsoft") && result.Contains ("WSL")) { - isWSL = true; - } - bash.StandardOutput.Close (); - bash.WaitForExit (); - return isWSL; - } + var (_, result) = ClipboardProcessRunner.Process ("bash", $"-c \"uname -a\""); + return result.Contains ("microsoft") && result.Contains ("WSL"); } bool xclipExists () { try { - using (Process bash = new Process { - StartInfo = new ProcessStartInfo { - FileName = "bash", - Arguments = $"-c \"which xclip\"", - RedirectStandardOutput = true, - } - }) { - bash.Start (); - bool exist = bash.StandardOutput.ReadToEnd ().TrimEnd () != ""; - bash.StandardOutput.Close (); - bash.WaitForExit (); - if (exist) { - return true; - } - } - return false; + var (_, result) = ClipboardProcessRunner.Process ("bash", $"-c \"which xclip\""); + return result.TrimEnd () != ""; } catch (System.Exception) { return false; } diff --git a/UnitTests/ConsoleDriverTests.cs b/UnitTests/ConsoleDriverTests.cs index 11aa2bb4ff..db8d167530 100644 --- a/UnitTests/ConsoleDriverTests.cs +++ b/UnitTests/ConsoleDriverTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using Terminal.Gui.Views; using Xunit; @@ -18,11 +19,15 @@ public ConsoleDriverTests (ITestOutputHelper output) this.output = output; } - [Fact] - public void Init_Inits () + [Theory] + [InlineData (typeof (FakeDriver))] + //[InlineData (typeof (NetDriver))] + //[InlineData (typeof (CursesDriver))] + //[InlineData (typeof (WindowsDriver))] + public void Init_Inits (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + Application.Init (driver); driver.Init (() => { }); Assert.Equal (80, Console.BufferWidth); @@ -37,11 +42,15 @@ public void Init_Inits () Application.Shutdown (); } - [Fact] - public void End_Cleans_Up () + [Theory] + [InlineData (typeof (FakeDriver))] + //[InlineData (typeof (NetDriver))] + //[InlineData (typeof (CursesDriver))] + //[InlineData (typeof (WindowsDriver))] + public void End_Cleans_Up (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + Application.Init (driver); driver.Init (() => { }); FakeConsole.ForegroundColor = ConsoleColor.Red; @@ -63,11 +72,15 @@ public void End_Cleans_Up () Application.Shutdown (); } - [Fact] - public void SetColors_Changes_Colors () + [Theory] + [InlineData (typeof (FakeDriver))] + //[InlineData (typeof (NetDriver))] + //[InlineData (typeof (CursesDriver))] + //[InlineData (typeof (WindowsDriver))] + public void SetColors_Changes_Colors (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + Application.Init (driver); driver.Init (() => { }); Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor); Assert.Equal (ConsoleColor.Black, Console.BackgroundColor); @@ -87,10 +100,12 @@ public void SetColors_Changes_Colors () Application.Shutdown (); } - [Fact] - public void FakeDriver_Only_Sends_Keystrokes_Through_MockKeyPresses () + [Theory] + [InlineData (typeof (FakeDriver))] + public void FakeDriver_Only_Sends_Keystrokes_Through_MockKeyPresses (Type driverType) { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + Application.Init (driver); var top = Application.Top; var view = new View (); @@ -117,10 +132,12 @@ public void FakeDriver_Only_Sends_Keystrokes_Through_MockKeyPresses () Application.Shutdown (); } - [Fact] - public void FakeDriver_MockKeyPresses () + [Theory] + [InlineData (typeof (FakeDriver))] + public void FakeDriver_MockKeyPresses (Type driverType) { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + Application.Init (driver); var text = "MockKeyPresses"; var mKeys = new Stack (); @@ -159,10 +176,12 @@ public void FakeDriver_MockKeyPresses () Application.Shutdown (); } - [Fact] - public void SendKeys_Test () + [Theory] + [InlineData (typeof (FakeDriver))] + public void SendKeys_Test (Type driverType) { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + Application.Init (driver); var top = Application.Top; var view = new View (); @@ -256,11 +275,12 @@ void SendKeys () Application.Shutdown (); } - [Fact] - public void TerminalResized_Simulation () + [Theory] + [InlineData (typeof (FakeDriver))] + public void TerminalResized_Simulation (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); var wasTerminalResized = false; Application.Resized = (e) => { wasTerminalResized = true; @@ -297,11 +317,12 @@ public void TerminalResized_Simulation () Application.Shutdown (); } - [Fact] - public void HeightAsBuffer_Is_False_Left_And_Top_Is_Always_Zero () + [Theory] + [InlineData (typeof (FakeDriver))] + public void HeightAsBuffer_Is_False_Left_And_Top_Is_Always_Zero (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); Assert.False (Application.HeightAsBuffer); Assert.Equal (0, Console.WindowLeft); @@ -314,11 +335,12 @@ public void HeightAsBuffer_Is_False_Left_And_Top_Is_Always_Zero () Application.Shutdown (); } - [Fact] - public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth () + [Theory] + [InlineData (typeof (FakeDriver))] + public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); Application.HeightAsBuffer = true; Assert.True (Application.HeightAsBuffer); @@ -330,11 +352,12 @@ public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth () Application.Shutdown (); } - [Fact] - public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth () + [Theory] + [InlineData (typeof (FakeDriver))] + public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); Application.HeightAsBuffer = true; Assert.True (Application.HeightAsBuffer); @@ -369,11 +392,12 @@ public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus Application.Shutdown (); } - [Fact] - public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight () + [Theory] + [InlineData (typeof (FakeDriver))] + public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); Application.HeightAsBuffer = true; Assert.True (Application.HeightAsBuffer); @@ -385,11 +409,12 @@ public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight () Application.Shutdown (); } - [Fact] - public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight () + [Theory] + [InlineData (typeof (FakeDriver))] + public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType) { - var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var driver = (FakeDriver)Activator.CreateInstance (driverType); + Application.Init (driver); Application.HeightAsBuffer = true; Assert.True (Application.HeightAsBuffer); diff --git a/UnitTests/DialogTests.cs b/UnitTests/DialogTests.cs index d9a34e4cab..1917b13e1d 100644 --- a/UnitTests/DialogTests.cs +++ b/UnitTests/DialogTests.cs @@ -575,5 +575,16 @@ public void Add_Button_Works () TestHelpers.AssertDriverContentsWithFrameAre ($"{topRow}\n{buttonRow}\n{bottomRow}", output); Application.End (runstate); } + + [Fact] + [AutoInitShutdown] + public void FileDialog_FileSystemWatcher () + { + for (int i = 0; i < 8; i++) { + var fd = new FileDialog (); + fd.Ready += () => Application.RequestStop (); + Application.Run (fd); + } + } } } \ No newline at end of file diff --git a/UnitTests/DimTests.cs b/UnitTests/DimTests.cs index 9bdd8d9b97..d474f61350 100644 --- a/UnitTests/DimTests.cs +++ b/UnitTests/DimTests.cs @@ -252,7 +252,7 @@ public void Percent_ThrowsOnIvalid () [Fact] public void ForceValidatePosDim_True_Dim_Validation_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -292,7 +292,7 @@ public void ForceValidatePosDim_True_Dim_Validation_Throws_If_NewValue_Is_DimAbs [Fact] public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Null () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -313,7 +313,7 @@ public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue [Fact] public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -346,7 +346,7 @@ public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assignin { // Testing with the Button because it properly handles the Dim class. - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -541,7 +541,7 @@ public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assignin [Fact] public void DimCombine_Do_Not_Throws () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -588,7 +588,7 @@ public void DimCombine_Do_Not_Throws () [Fact] public void PosCombine_Will_Throws () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -622,7 +622,7 @@ public void PosCombine_Will_Throws () public void Dim_Add_Operator () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; @@ -989,7 +989,7 @@ public void Dim_Add_Operator () public void Dim_Add_Operator_With_Text () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; @@ -1056,7 +1056,7 @@ public void Dim_Add_Operator_With_Text () public void Dim_Subtract_Operator () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; @@ -1116,7 +1116,7 @@ public void Dim_Subtract_Operator () public void Dim_Subtract_Operator_With_Text () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; diff --git a/UnitTests/GraphViewTests.cs b/UnitTests/GraphViewTests.cs index 9d3f03dabe..17ef78697b 100644 --- a/UnitTests/GraphViewTests.cs +++ b/UnitTests/GraphViewTests.cs @@ -58,7 +58,7 @@ public static FakeDriver InitFakeDriver () { var driver = new FakeDriver (); try { - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); } catch (InvalidOperationException) { // close it so that we don't get a thousand of these errors in a row @@ -1506,7 +1506,7 @@ public void PathAnnotation_Diamond () public void LabelChangeText_RendersCorrectly (bool useFill) { var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); driver.Init (() => { }); // create a wide window diff --git a/UnitTests/MainLoopTests.cs b/UnitTests/MainLoopTests.cs index 92bd338aea..b66497b9eb 100644 --- a/UnitTests/MainLoopTests.cs +++ b/UnitTests/MainLoopTests.cs @@ -14,12 +14,17 @@ using Console = Terminal.Gui.FakeConsole; namespace Terminal.Gui.Core { + /// + /// Tests MainLoop using the FakeMainLoop. + /// public class MainLoopTests { + // TODO: Expand to test all the MainLoop implementations. + [Fact] public void Constructor_Setups_Driver () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); Assert.NotNull (ml.Driver); } @@ -27,7 +32,7 @@ public void Constructor_Setups_Driver () [Fact] public void AddIdle_Adds_And_Removes () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); Func fnTrue = () => true; Func fnFalse = () => false; @@ -81,7 +86,7 @@ public void AddIdle_Adds_And_Removes () [Fact] public void AddIdle_Function_GetsCalled_OnIteration () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var functionCalled = 0; Func fn = () => { @@ -97,7 +102,7 @@ public void AddIdle_Function_GetsCalled_OnIteration () [Fact] public void RemoveIdle_Function_NotCalled () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var functionCalled = 0; Func fn = () => { @@ -113,7 +118,7 @@ public void RemoveIdle_Function_NotCalled () [Fact] public void AddThenRemoveIdle_Function_NotCalled () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var functionCalled = 0; Func fn = () => { @@ -130,7 +135,7 @@ public void AddThenRemoveIdle_Function_NotCalled () [Fact] public void AddTwice_Function_CalledTwice () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var functionCalled = 0; Func fn = () => { @@ -161,7 +166,7 @@ public void AddTwice_Function_CalledTwice () [Fact] public void False_Idle_Stops_It_Being_Called_Again () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var functionCalled = 0; Func fn1 = () => { @@ -195,7 +200,7 @@ public void False_Idle_Stops_It_Being_Called_Again () [Fact] public void AddIdle_Twice_Returns_False_Called_Twice () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var functionCalled = 0; Func fn1 = () => { @@ -227,7 +232,7 @@ public void AddIdle_Twice_Returns_False_Called_Twice () [Fact] public void Run_Runs_Idle_Stop_Stops_Idle () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var functionCalled = 0; Func fn = () => { @@ -249,7 +254,7 @@ public void Run_Runs_Idle_Stop_Stops_Idle () [Fact] public void AddTimer_Adds_Removes_NoFaults () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var ms = 100; var callbackCount = 0; @@ -270,7 +275,7 @@ public void AddTimer_Adds_Removes_NoFaults () [Fact] public void AddTimer_Run_Called () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var ms = 100; var callbackCount = 0; @@ -290,7 +295,7 @@ public void AddTimer_Run_Called () [Fact] public async Task AddTimer_Duplicate_Keys_Not_Allowed () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); const int ms = 100; object token1 = null, token2 = null; @@ -322,7 +327,7 @@ public async Task AddTimer_Duplicate_Keys_Not_Allowed () [Fact] public void AddTimer_In_Parallel_Wont_Throw () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); const int ms = 100; object token1 = null, token2 = null; @@ -359,7 +364,7 @@ class MillisecondTolerance : IEqualityComparer { [Fact] public void AddTimer_Run_CalledAtApproximatelyRightTime () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var ms = TimeSpan.FromMilliseconds (50); var watch = new System.Diagnostics.Stopwatch (); @@ -385,7 +390,7 @@ public void AddTimer_Run_CalledAtApproximatelyRightTime () [Fact] public void AddTimer_Run_CalledTwiceApproximatelyRightTime () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var ms = TimeSpan.FromMilliseconds (50); var watch = new System.Diagnostics.Stopwatch (); @@ -413,7 +418,7 @@ public void AddTimer_Run_CalledTwiceApproximatelyRightTime () [Fact] public void AddTimer_Remove_NotCalled () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var ms = TimeSpan.FromMilliseconds (50); // Force stop if 10 iterations @@ -442,7 +447,7 @@ public void AddTimer_Remove_NotCalled () [Fact] public void AddTimer_ReturnFalse_StopsBeingCalled () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var ms = TimeSpan.FromMilliseconds (50); // Force stop if 10 iterations @@ -475,7 +480,7 @@ public void AddTimer_ReturnFalse_StopsBeingCalled () [Fact] public void Invoke_Adds_Idle () { - var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop ()); var actionCalled = 0; ml.Invoke (() => { actionCalled++; }); diff --git a/UnitTests/MdiTests.cs b/UnitTests/MdiTests.cs index b509c49d6d..83860bf363 100644 --- a/UnitTests/MdiTests.cs +++ b/UnitTests/MdiTests.cs @@ -22,7 +22,7 @@ public MdiTests () [Fact] public void Dispose_Toplevel_IsMdiContainer_False_With_Begin_End () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = new Toplevel (); var rs = Application.Begin (top); @@ -30,25 +30,28 @@ public void Dispose_Toplevel_IsMdiContainer_False_With_Begin_End () Application.Shutdown (); +#if DEBUG_IDISPOSABLE Assert.Equal (2, Responder.Instances.Count); Assert.True (Responder.Instances [0].WasDisposed); Assert.True (Responder.Instances [1].WasDisposed); +#endif } [Fact] public void Dispose_Toplevel_IsMdiContainer_True_With_Begin () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var mdi = new Toplevel { IsMdiContainer = true }; var rs = Application.Begin (mdi); Application.End (rs); Application.Shutdown (); - +#if DEBUG_IDISPOSABLE Assert.Equal (2, Responder.Instances.Count); Assert.True (Responder.Instances [0].WasDisposed); Assert.True (Responder.Instances [1].WasDisposed); +#endif } [Fact, AutoInitShutdown] diff --git a/UnitTests/PosTests.cs b/UnitTests/PosTests.cs index ee10488624..24a29f0397 100644 --- a/UnitTests/PosTests.cs +++ b/UnitTests/PosTests.cs @@ -555,7 +555,7 @@ public void LeftTopBottomRight_Win_ShouldNotThrow () // Setup Fake driver (Window win, Button button) setup () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); Application.Iteration = () => { Application.RequestStop (); }; @@ -700,7 +700,7 @@ public void Percent_ThrowsOnIvalid () [Fact] public void ForceValidatePosDim_True_Pos_Validation_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -733,7 +733,7 @@ public void ForceValidatePosDim_True_Pos_Validation_Throws_If_NewValue_Is_PosAbs [Fact] public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Null () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -755,7 +755,7 @@ public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue [Fact] public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -788,7 +788,7 @@ public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue [Fact] public void PosCombine_Do_Not_Throws () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -835,7 +835,7 @@ public void PosCombine_Do_Not_Throws () [Fact] public void PosCombine_Will_Throws () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -868,7 +868,7 @@ public void PosCombine_Will_Throws () public void Pos_Add_Operator () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; @@ -917,7 +917,7 @@ public void Pos_Add_Operator () public void Pos_Subtract_Operator () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; diff --git a/UnitTests/RunStateTests.cs b/UnitTests/RunStateTests.cs index afe6886dfb..501525ecd7 100644 --- a/UnitTests/RunStateTests.cs +++ b/UnitTests/RunStateTests.cs @@ -40,8 +40,9 @@ public void Dispose_Cleans_Up_RunState () // Should not throw because Toplevel was null rs.Dispose (); +#if DEBUG_IDISPOSABLE Assert.True (rs.WasDisposed); - +#endif var top = new Toplevel (); rs = new Application.RunState (top); Assert.NotNull (rs); @@ -52,13 +53,15 @@ public void Dispose_Cleans_Up_RunState () rs.Toplevel.Dispose (); rs.Toplevel = null; rs.Dispose (); +#if DEBUG_IDISPOSABLE Assert.True (rs.WasDisposed); Assert.True (top.WasDisposed); +#endif } void Init () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); Assert.NotNull (Application.Driver); Assert.NotNull (Application.MainLoop); Assert.NotNull (SynchronizationContext.Current); @@ -67,10 +70,12 @@ void Init () void Shutdown () { Application.Shutdown (); +#if DEBUG_IDISPOSABLE // Validate there are no outstanding RunState-based instances left foreach (var inst in Application.RunState.Instances) { Assert.True (inst.WasDisposed); } +#endif } [Fact] @@ -95,7 +100,9 @@ public void Begin_End_Cleans_Up_RunState () Shutdown (); +#if DEBUG_IDISPOSABLE Assert.True (rs.WasDisposed); +#endif Assert.Null (Application.Top); Assert.Null (Application.MainLoop); diff --git a/UnitTests/ScenarioTests.cs b/UnitTests/ScenarioTests.cs index f5f1dc57bb..18d87bd891 100644 --- a/UnitTests/ScenarioTests.cs +++ b/UnitTests/ScenarioTests.cs @@ -61,7 +61,7 @@ public void Run_All_Scenarios () return false; }; - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); // Close after a short period of time var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), closeCallback); @@ -97,7 +97,7 @@ public void Run_Generic () // Passing empty string will cause just a ctrl-q to be fired int stackSize = CreateInput (""); - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); int iterations = 0; Application.Iteration = () => { @@ -179,7 +179,7 @@ public void Run_All_Views_Tester_Scenario () List dimNames = new List { "Factor", "Fill", "Absolute" }; - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var Top = Application.Top; diff --git a/UnitTests/ScrollBarViewTests.cs b/UnitTests/ScrollBarViewTests.cs index 1826279781..8569273d83 100644 --- a/UnitTests/ScrollBarViewTests.cs +++ b/UnitTests/ScrollBarViewTests.cs @@ -20,19 +20,11 @@ public ScrollBarViewTests (ITestOutputHelper output) // This is necessary because a) Application is a singleton and Init/Shutdown must be called // as a pair, and b) all unit test functions should be atomic. [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public class InitShutdownAttribute : Xunit.Sdk.BeforeAfterTestAttribute { + public class ScrollBarAutoInitShutdownAttribute : AutoInitShutdownAttribute { public override void Before (MethodInfo methodUnderTest) { - Debug.WriteLine ($"Before: {methodUnderTest.Name}"); - - if (_hostView != null) { - throw new InvalidOperationException ("After did not run."); - } - - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); - - var top = Application.Top; + base.Before (methodUnderTest); ScrollBarViewTests._hostView = new HostView () { Width = Dim.Fill (), @@ -43,14 +35,13 @@ public override void Before (MethodInfo methodUnderTest) Cols = 100 }; - top.Add (ScrollBarViewTests._hostView); + Application.Top.Add (ScrollBarViewTests._hostView); } public override void After (MethodInfo methodUnderTest) { - Debug.WriteLine ($"After: {methodUnderTest.Name}"); ScrollBarViewTests._hostView = null; - Application.Shutdown (); + base.After (methodUnderTest); } } @@ -113,7 +104,7 @@ private void _scrollBar_OtherScrollBarView_ChangedPosition () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void Hosting_A_Null_View_To_A_ScrollBarView_Throws_ArgumentNullException () { Assert.Throws ("The host parameter can't be null.", @@ -123,7 +114,7 @@ public void Hosting_A_Null_View_To_A_ScrollBarView_Throws_ArgumentNullException } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void Hosting_A_Null_SuperView_View_To_A_ScrollBarView_Throws_ArgumentNullException () { Assert.Throws ("The host SuperView parameter can't be null.", @@ -133,7 +124,7 @@ public void Hosting_A_Null_SuperView_View_To_A_ScrollBarView_Throws_ArgumentNull } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void Hosting_Two_Vertical_ScrollBarView_Throws_ArgumentException () { var top = new Toplevel (); @@ -147,7 +138,7 @@ public void Hosting_Two_Vertical_ScrollBarView_Throws_ArgumentException () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void Hosting_Two_Horizontal_ScrollBarView_Throws_ArgumentException () { var top = new Toplevel (); @@ -161,7 +152,7 @@ public void Hosting_Two_Horizontal_ScrollBarView_Throws_ArgumentException () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void Scrolling_With_Default_Constructor_Do_Not_Scroll () { var sbv = new ScrollBarView { @@ -172,7 +163,7 @@ public void Scrolling_With_Default_Constructor_Do_Not_Scroll () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void Hosting_A_View_To_A_ScrollBarView () { RemoveHandlers (); @@ -198,7 +189,7 @@ public void Hosting_A_View_To_A_ScrollBarView () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void ChangedPosition_Update_The_Hosted_View () { Hosting_A_View_To_A_ScrollBarView (); @@ -213,7 +204,7 @@ public void ChangedPosition_Update_The_Hosted_View () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void ChangedPosition_Scrolling () { Hosting_A_View_To_A_ScrollBarView (); @@ -240,7 +231,7 @@ public void ChangedPosition_Scrolling () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void ChangedPosition_Negative_Value () { Hosting_A_View_To_A_ScrollBarView (); @@ -257,7 +248,7 @@ public void ChangedPosition_Negative_Value () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void DrawContent_Update_The_ScrollBarView_Position () { Hosting_A_View_To_A_ScrollBarView (); @@ -274,7 +265,7 @@ public void DrawContent_Update_The_ScrollBarView_Position () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void OtherScrollBarView_Not_Null () { Hosting_A_View_To_A_ScrollBarView (); @@ -287,7 +278,7 @@ public void OtherScrollBarView_Not_Null () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void ShowScrollIndicator_Check () { Hosting_A_View_To_A_ScrollBarView (); @@ -299,7 +290,7 @@ public void ShowScrollIndicator_Check () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void KeepContentAlwaysInViewport_True () { Hosting_A_View_To_A_ScrollBarView (); @@ -339,7 +330,7 @@ public void KeepContentAlwaysInViewport_True () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void KeepContentAlwaysInViewport_False () { Hosting_A_View_To_A_ScrollBarView (); @@ -361,7 +352,7 @@ public void KeepContentAlwaysInViewport_False () } [Fact] - [InitShutdown] + [ScrollBarAutoInitShutdown] public void AutoHideScrollBars_Check () { Hosting_A_View_To_A_ScrollBarView (); @@ -457,7 +448,7 @@ public void AutoHideScrollBars_Check () public void Constructor_ShowBothScrollIndicator_False_And_IsVertical_True_Refresh_Does_Not_Throws_An_Object_Null_Exception () { var exception = Record.Exception (() => { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; @@ -527,7 +518,7 @@ public void Constructor_ShowBothScrollIndicator_False_And_IsVertical_True_Refres public void Constructor_ShowBothScrollIndicator_False_And_IsVertical_False_Refresh_Does_Not_Throws_An_Object_Null_Exception () { var exception = Record.Exception (() => { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; diff --git a/UnitTests/StatusBarTests.cs b/UnitTests/StatusBarTests.cs index ee303d22df..e27a2910e9 100644 --- a/UnitTests/StatusBarTests.cs +++ b/UnitTests/StatusBarTests.cs @@ -36,7 +36,7 @@ public void StatusBar_Contructor_Default () Assert.Equal (1, sb.Height); var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); sb = new StatusBar (); diff --git a/UnitTests/TabViewTests.cs b/UnitTests/TabViewTests.cs index ab376fd3e0..a98f03566b 100644 --- a/UnitTests/TabViewTests.cs +++ b/UnitTests/TabViewTests.cs @@ -763,7 +763,7 @@ public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () private void InitFakeDriver () { var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); driver.Init (() => { }); } } diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index bdf99dc6d7..ac13d56c0f 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -10,37 +10,76 @@ using Attribute = Terminal.Gui.Attribute; using System.Text.RegularExpressions; using System.Reflection; +using System.Diagnostics; // This class enables test functions annotated with the [AutoInitShutdown] attribute to -// automatically call Application.Init before called and Application.Shutdown after +// automatically call Application.Init at start of the test and Application.Shutdown after the +// test exits. // // This is necessary because a) Application is a singleton and Init/Shutdown must be called -// as a pair, and b) all unit test functions should be atomic. +// as a pair, and b) all unit test functions should be atomic.. [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class AutoInitShutdownAttribute : Xunit.Sdk.BeforeAfterTestAttribute { + /// + /// Initializes a [AutoInitShutdown] attribute, which determines if/how Application.Init and + /// Application.Shutdown are automatically called Before/After a test runs. + /// + /// If true, Application.Init will be called Before the test runs. + /// If true, Application.Shutdown will be called After the test runs. + /// Determins which ConsoleDriver (FakeDriver, WindowsDriver, + /// CursesDriver, NetDriver) will be used when Appliation.Init is called. If null FakeDriver will be used. + /// Only valid if is true. + /// If true, will force the use of . + /// Only valid if == and is true. + /// Only valid if is true. + /// Only valid if == and is true. + /// Only valid if is true. + /// Only valid if == and is true. + public AutoInitShutdownAttribute (bool autoInit = true, bool autoShutdown = true, + Type consoleDriverType = null, + bool useFakeClipboard = false, + bool fakeClipboardAlwaysThrowsNotSupportedException = false, + bool fakeClipboardIsSupportedAlwaysTrue = false) + { + //Assert.True (autoInit == false && consoleDriverType == null); + + AutoInit = autoInit; + AutoShutdown = autoShutdown; + DriverType = consoleDriverType ?? typeof (FakeDriver); + FakeDriver.FakeBehaviors.UseFakeClipboard = useFakeClipboard; + FakeDriver.FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; + FakeDriver.FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; + } static bool _init = false; + bool AutoInit { get; } + bool AutoShutdown { get; } + Type DriverType; + public override void Before (MethodInfo methodUnderTest) { - if (_init) { - throw new InvalidOperationException ("After did not run."); + Debug.WriteLine ($"Before: {methodUnderTest.Name}"); + if (AutoShutdown && _init) { + throw new InvalidOperationException ("After did not run when AutoShutdown was specified."); + } + if (AutoInit) { + Application.Init ((ConsoleDriver)Activator.CreateInstance (DriverType)); + _init = true; } - - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); - _init = true; } public override void After (MethodInfo methodUnderTest) { - Application.Shutdown (); - _init = false; + Debug.WriteLine ($"After: {methodUnderTest.Name}"); + if (AutoShutdown) { + Application.Shutdown (); + _init = false; + } } } class TestHelpers { - - #pragma warning disable xUnit1013 // Public method should be marked as test public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelper output) { diff --git a/UnitTests/TextFieldTests.cs b/UnitTests/TextFieldTests.cs index a6d4e1ba03..cc35598486 100644 --- a/UnitTests/TextFieldTests.cs +++ b/UnitTests/TextFieldTests.cs @@ -11,11 +11,11 @@ public class TextFieldTests { // This is necessary because a) Application is a singleton and Init/Shutdown must be called // as a pair, and b) all unit test functions should be atomic. [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public class InitShutdown : Xunit.Sdk.BeforeAfterTestAttribute { + public class TextFieldTestsAutoInitShutdown : AutoInitShutdownAttribute { public override void Before (MethodInfo methodUnderTest) { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + base.Before (methodUnderTest); // 1 2 3 // 01234567890123456789012345678901=32 (Length) @@ -25,14 +25,14 @@ public override void Before (MethodInfo methodUnderTest) public override void After (MethodInfo methodUnderTest) { TextFieldTests._textField = null; - Application.Shutdown (); + base.After (methodUnderTest); } } private static TextField _textField; [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Changing_SelectedStart_Or_CursorPosition_Update_SelectedLength_And_SelectedText () { _textField.SelectedStart = 2; @@ -46,7 +46,7 @@ public void Changing_SelectedStart_Or_CursorPosition_Update_SelectedLength_And_S } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void SelectedStart_With_Value_Less_Than_Minus_One_Changes_To_Minus_One () { _textField.SelectedStart = -2; @@ -56,7 +56,7 @@ public void SelectedStart_With_Value_Less_Than_Minus_One_Changes_To_Minus_One () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void SelectedStart_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length () { _textField.CursorPosition = 2; @@ -67,7 +67,7 @@ public void SelectedStart_With_Value_Greater_Than_Text_Length_Changes_To_Text_Le } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void SelectedStart_And_CursorPosition_With_Value_Greater_Than_Text_Length_Changes_Both_To_Text_Length () { _textField.CursorPosition = 33; @@ -79,18 +79,18 @@ public void SelectedStart_And_CursorPosition_With_Value_Greater_Than_Text_Length } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void SelectedStart_Greater_Than_CursorPosition_All_Selection_Is_Overwritten_On_Typing () { _textField.SelectedStart = 19; _textField.CursorPosition = 12; - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x75, new KeyModifiers ())); // u - Assert.Equal ("TAB to jump u text fields.", _textField.Text); + Assert.Equal ("TAB to jump u text fields.", _textField.Text.ToString ()); } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void CursorPosition_With_Value_Less_Than_Zero_Changes_To_Zero () { _textField.CursorPosition = -1; @@ -100,7 +100,7 @@ public void CursorPosition_With_Value_Less_Than_Zero_Changes_To_Zero () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void CursorPosition_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length () { _textField.CursorPosition = 33; @@ -110,7 +110,7 @@ public void CursorPosition_With_Value_Greater_Than_Text_Length_Changes_To_Text_L } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void WordForward_With_No_Selection () { _textField.CursorPosition = 0; @@ -161,7 +161,7 @@ public void WordForward_With_No_Selection () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void WordBackward_With_No_Selection () { _textField.CursorPosition = _textField.Text.Length; @@ -212,7 +212,7 @@ public void WordBackward_With_No_Selection () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void WordForward_With_Selection () { _textField.CursorPosition = 0; @@ -264,7 +264,7 @@ public void WordForward_With_Selection () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void WordBackward_With_Selection () { _textField.CursorPosition = _textField.Text.Length; @@ -316,7 +316,7 @@ public void WordBackward_With_Selection () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void WordForward_With_The_Same_Values_For_SelectedStart_And_CursorPosition_And_Not_Starting_At_Beginning_Of_The_Text () { _textField.CursorPosition = 10; @@ -356,7 +356,7 @@ public void WordForward_With_The_Same_Values_For_SelectedStart_And_CursorPositio } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void WordBackward_With_The_Same_Values_For_SelectedStart_And_CursorPosition_And_Not_Starting_At_Beginning_Of_The_Text () { _textField.CursorPosition = 10; @@ -390,7 +390,7 @@ public void WordBackward_With_The_Same_Values_For_SelectedStart_And_CursorPositi } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void WordForward_With_No_Selection_And_With_More_Than_Only_One_Whitespace_And_With_Only_One_Letter () { // 1 2 3 4 5 @@ -474,7 +474,7 @@ public void WordForward_With_No_Selection_And_With_More_Than_Only_One_Whitespace } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void WordBackward_With_No_Selection_And_With_More_Than_Only_One_Whitespace_And_With_Only_One_Letter () { // 1 2 3 4 5 @@ -558,7 +558,7 @@ public void WordBackward_With_No_Selection_And_With_More_Than_Only_One_Whitespac } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Copy_Or_Cut_Null_If_No_Selection () { _textField.SelectedStart = -1; @@ -569,7 +569,7 @@ public void Copy_Or_Cut_Null_If_No_Selection () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Copy_Or_Cut_Not_Null_If_Has_Selection () { _textField.SelectedStart = 20; @@ -581,45 +581,45 @@ public void Copy_Or_Cut_Not_Null_If_Has_Selection () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Copy_Or_Cut_And_Paste_With_Selection () { _textField.SelectedStart = 20; _textField.CursorPosition = 24; _textField.Copy (); Assert.Equal ("text", _textField.SelectedText); - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); _textField.Paste (); - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); _textField.SelectedStart = 20; _textField.Cut (); _textField.Paste (); - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Copy_Or_Cut_And_Paste_With_No_Selection () { _textField.SelectedStart = 20; _textField.CursorPosition = 24; _textField.Copy (); Assert.Equal ("text", _textField.SelectedText); - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); _textField.SelectedStart = -1; _textField.Paste (); - Assert.Equal ("TAB to jump between texttext fields.", _textField.Text); + Assert.Equal ("TAB to jump between texttext fields.", _textField.Text.ToString ()); _textField.SelectedStart = 24; _textField.Cut (); Assert.Null (_textField.SelectedText); - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); _textField.SelectedStart = -1; _textField.Paste (); - Assert.Equal ("TAB to jump between texttext fields.", _textField.Text); + Assert.Equal ("TAB to jump between texttext fields.", _textField.Text.ToString ()); } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Copy_Or_Cut__Not_Allowed_If_Secret_Is_True () { _textField.Secret = true; @@ -637,7 +637,7 @@ public void Copy_Or_Cut__Not_Allowed_If_Secret_Is_True () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Paste_Always_Clear_The_SelectedText () { _textField.SelectedStart = 20; @@ -649,7 +649,7 @@ public void Paste_Always_Clear_The_SelectedText () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void TextChanging_Event () { bool cancel = true; @@ -662,14 +662,14 @@ public void TextChanging_Event () }; _textField.Text = "changing"; - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); cancel = false; _textField.Text = "changing"; - Assert.Equal ("changing", _textField.Text); + Assert.Equal ("changing", _textField.Text.ToString ()); } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void TextChanged_Event () { _textField.TextChanged += (e) => { @@ -677,40 +677,40 @@ public void TextChanged_Event () }; _textField.Text = "changed"; - Assert.Equal ("changed", _textField.Text); + Assert.Equal ("changed", _textField.Text.ToString ()); } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Used_Is_True_By_Default () { _textField.CursorPosition = 10; - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x75, new KeyModifiers ())); // u - Assert.Equal ("TAB to jumup between text fields.", _textField.Text); + Assert.Equal ("TAB to jumup between text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x73, new KeyModifiers ())); // s - Assert.Equal ("TAB to jumusp between text fields.", _textField.Text); + Assert.Equal ("TAB to jumusp between text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x65, new KeyModifiers ())); // e - Assert.Equal ("TAB to jumusep between text fields.", _textField.Text); + Assert.Equal ("TAB to jumusep between text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x64, new KeyModifiers ())); // d - Assert.Equal ("TAB to jumusedp between text fields.", _textField.Text); + Assert.Equal ("TAB to jumusedp between text fields.", _textField.Text.ToString ()); } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Used_Is_False () { _textField.Used = false; _textField.CursorPosition = 10; - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x75, new KeyModifiers ())); // u - Assert.Equal ("TAB to jumu between text fields.", _textField.Text); + Assert.Equal ("TAB to jumu between text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x73, new KeyModifiers ())); // s - Assert.Equal ("TAB to jumusbetween text fields.", _textField.Text); + Assert.Equal ("TAB to jumusbetween text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x65, new KeyModifiers ())); // e - Assert.Equal ("TAB to jumuseetween text fields.", _textField.Text); + Assert.Equal ("TAB to jumuseetween text fields.", _textField.Text.ToString ()); _textField.ProcessKey (new KeyEvent ((Key)0x64, new KeyModifiers ())); // d - Assert.Equal ("TAB to jumusedtween text fields.", _textField.Text); + Assert.Equal ("TAB to jumusedtween text fields.", _textField.Text.ToString ()); } [Fact] @@ -718,22 +718,22 @@ public void ProcessKey_Backspace_From_End () { var tf = new TextField ("ABC"); tf.EnsureFocus (); - Assert.Equal ("ABC", tf.Text); + Assert.Equal ("ABC", tf.Text.ToString ()); Assert.Equal (3, tf.CursorPosition); // now delete the C tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())); - Assert.Equal ("AB", tf.Text); + Assert.Equal ("AB", tf.Text.ToString ()); Assert.Equal (2, tf.CursorPosition); // then delete the B tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())); - Assert.Equal ("A", tf.Text); + Assert.Equal ("A", tf.Text.ToString ()); Assert.Equal (1, tf.CursorPosition); // then delete the A tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())); - Assert.Equal ("", tf.Text); + Assert.Equal ("", tf.Text.ToString ()); Assert.Equal (0, tf.CursorPosition); } @@ -743,24 +743,24 @@ public void ProcessKey_Backspace_From_Middle () var tf = new TextField ("ABC"); tf.EnsureFocus (); tf.CursorPosition = 2; - Assert.Equal ("ABC", tf.Text); + Assert.Equal ("ABC", tf.Text.ToString ()); // now delete the B tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())); - Assert.Equal ("AC", tf.Text); + Assert.Equal ("AC", tf.Text.ToString ()); // then delete the A tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())); - Assert.Equal ("C", tf.Text); + Assert.Equal ("C", tf.Text.ToString ()); // then delete nothing tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())); - Assert.Equal ("C", tf.Text); + Assert.Equal ("C", tf.Text.ToString ()); // now delete the C tf.CursorPosition = 1; tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())); - Assert.Equal ("", tf.Text); + Assert.Equal ("", tf.Text.ToString ()); } [Fact] @@ -769,35 +769,35 @@ public void Cancel_TextChanging_ThenBackspace () var tf = new TextField (); tf.EnsureFocus (); tf.ProcessKey (new KeyEvent (Key.A, new KeyModifiers ())); - Assert.Equal ("A", tf.Text); + Assert.Equal ("A", tf.Text.ToString ()); // cancel the next keystroke tf.TextChanging += (e) => e.Cancel = e.NewText == "AB"; tf.ProcessKey (new KeyEvent (Key.B, new KeyModifiers ())); // B was canceled so should just be A - Assert.Equal ("A", tf.Text); + Assert.Equal ("A", tf.Text.ToString ()); // now delete the A tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())); - Assert.Equal ("", tf.Text); + Assert.Equal ("", tf.Text.ToString ()); } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void Text_Replaces_Tabs_With_Empty_String () { _textField.Text = "\t\tTAB to jump between text fields."; - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); _textField.Text = ""; Clipboard.Contents = "\t\tTAB to jump between text fields."; _textField.Paste (); - Assert.Equal ("TAB to jump between text fields.", _textField.Text); + Assert.Equal ("TAB to jump between text fields.", _textField.Text.ToString ()); } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void TextField_SpaceHandling () { var tf = new TextField () { @@ -825,7 +825,7 @@ public void TextField_SpaceHandling () } [Fact] - [InitShutdown] + [TextFieldTestsAutoInitShutdown] public void CanFocus_False_Wont_Focus_With_Mouse () { var top = Application.Top; @@ -901,201 +901,201 @@ public void KeyBindings_Command () Assert.False (tf.ReadOnly); Assert.True (tf.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ()))); - Assert.Equal ("This is a test.", tf.Text); + Assert.Equal ("This is a test.", tf.Text.ToString ()); tf.CursorPosition = 0; Assert.True (tf.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ()))); - Assert.Equal ("his is a test.", tf.Text); + Assert.Equal ("his is a test.", tf.Text.ToString ()); tf.ReadOnly = true; Assert.True (tf.ProcessKey (new KeyEvent (Key.D | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("his is a test.", tf.Text); + Assert.Equal ("his is a test.", tf.Text.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.Delete, new KeyModifiers ()))); - Assert.Equal ("his is a test.", tf.Text); + Assert.Equal ("his is a test.", tf.Text.ToString ()); tf.ReadOnly = false; tf.CursorPosition = 1; Assert.True (tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); tf.CursorPosition = 5; Assert.True (tf.ProcessKey (new KeyEvent (Key.Home | Key.ShiftMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is is", tf.SelectedText); tf.CursorPosition = 5; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.Home | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is is", tf.SelectedText); tf.CursorPosition = 5; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.A | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is is", tf.SelectedText); tf.CursorPosition = 5; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.End | Key.ShiftMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (" a test.", tf.SelectedText); tf.CursorPosition = 5; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.End | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (" a test.", tf.SelectedText); tf.CursorPosition = 5; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.E | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (" a test.", tf.SelectedText); tf.CursorPosition = 5; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.Home, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (0, tf.CursorPosition); tf.CursorPosition = 5; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.Home | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (0, tf.CursorPosition); tf.CursorPosition = 5; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.A | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (0, tf.CursorPosition); tf.CursorPosition = 5; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft | Key.ShiftMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("s", tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorUp | Key.ShiftMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is", tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorRight | Key.ShiftMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("s", tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorDown | Key.ShiftMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Null (tf.SelectedText); tf.CursorPosition = 7; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("a", tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorUp | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is a", tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent ((Key)((int)'B' + Key.ShiftMask | Key.AltMask), new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is is a", tf.SelectedText); tf.CursorPosition = 3; tf.SelectedStart = -1; Assert.Null (tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorRight | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is ", tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorDown | Key.ShiftMask | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is a ", tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent ((Key)((int)'F' + Key.ShiftMask | Key.AltMask), new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal ("is a test.", tf.SelectedText); Assert.Equal (13, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Null (tf.SelectedText); Assert.Equal (12, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (11, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.End, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (13, tf.CursorPosition); tf.CursorPosition = 0; Assert.True (tf.ProcessKey (new KeyEvent (Key.End | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (13, tf.CursorPosition); tf.CursorPosition = 0; Assert.True (tf.ProcessKey (new KeyEvent (Key.E | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (13, tf.CursorPosition); tf.CursorPosition = 0; Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (1, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.F | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.Equal (2, tf.CursorPosition); tf.CursorPosition = 9; tf.ReadOnly = true; Assert.True (tf.ProcessKey (new KeyEvent (Key.K | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); tf.ReadOnly = false; Assert.True (tf.ProcessKey (new KeyEvent (Key.K | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); - Assert.Equal ("est.", Clipboard.Contents); + Assert.Equal ("is is a t", tf.Text.ToString ()); + Assert.Equal ("est.", Clipboard.Contents.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.Z | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.Y | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.Backspace | Key.AltMask, new KeyModifiers ()))); - Assert.Equal ("is is a test.", tf.Text); + Assert.Equal ("is is a test.", tf.Text.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.Y | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.Equal (8, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorUp | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.Equal (6, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent ((Key)((int)'B' + Key.AltMask), new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.Equal (3, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.Equal (6, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.CursorDown | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.Equal (8, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent ((Key)((int)'F' + Key.AltMask), new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.Equal (9, tf.CursorPosition); Assert.True (tf.Used); Assert.True (tf.ProcessKey (new KeyEvent (Key.InsertChar, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); + Assert.Equal ("is is a t", tf.Text.ToString ()); Assert.Equal (9, tf.CursorPosition); Assert.False (tf.Used); tf.SelectedStart = 3; tf.CursorPosition = 7; Assert.Equal ("is a", tf.SelectedText); - Assert.Equal ("est.", Clipboard.Contents); + Assert.Equal ("est.", Clipboard.Contents.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.C | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); - Assert.Equal ("is a", Clipboard.Contents); + Assert.Equal ("is is a t", tf.Text.ToString ()); + Assert.Equal ("is a", Clipboard.Contents.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.X | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is t", tf.Text); - Assert.Equal ("is a", Clipboard.Contents); + Assert.Equal ("is t", tf.Text.ToString ()); + Assert.Equal ("is a", Clipboard.Contents.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.V | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("is is a t", tf.Text); - Assert.Equal ("is a", Clipboard.Contents); + Assert.Equal ("is is a t", tf.Text.ToString ()); + Assert.Equal ("is a", Clipboard.Contents.ToString ()); Assert.Equal (7, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.K | Key.AltMask, new KeyModifiers ()))); - Assert.Equal (" t", tf.Text); - Assert.Equal ("is is a", Clipboard.Contents); + Assert.Equal (" t", tf.Text.ToString ()); + Assert.Equal ("is is a", Clipboard.Contents.ToString ()); tf.Text = "TAB to jump between text fields."; Assert.Equal (0, tf.CursorPosition); Assert.True (tf.ProcessKey (new KeyEvent (Key.DeleteChar | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("to jump between text fields.", tf.Text); + Assert.Equal ("to jump between text fields.", tf.Text.ToString ()); tf.CursorPosition = tf.Text.Length; Assert.True (tf.ProcessKey (new KeyEvent (Key.Backspace | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ("to jump between text ", tf.Text); + Assert.Equal ("to jump between text ", tf.Text.ToString ()); Assert.True (tf.ProcessKey (new KeyEvent (Key.T | Key.CtrlMask, new KeyModifiers ()))); Assert.Equal ("to jump between text ", tf.SelectedText); Assert.True (tf.ProcessKey (new KeyEvent (Key.D | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()))); - Assert.Equal ("", tf.Text); + Assert.Equal ("", tf.Text.ToString ()); } [Fact] @@ -1134,7 +1134,7 @@ public void DeleteSelectedText_InsertText_DeleteCharLeft_DeleteCharRight_Cut () Application.Top.Add (tf); Application.Begin (Application.Top); - Assert.Equal ("-1", tf.Text); + Assert.Equal ("-1", tf.Text.ToString ()); // InsertText tf.SelectedStart = 1; @@ -1144,7 +1144,7 @@ public void DeleteSelectedText_InsertText_DeleteCharLeft_DeleteCharRight_Cut () Assert.True (tf.ProcessKey (new KeyEvent (Key.D2, new KeyModifiers ()))); Assert.Equal ("-2", newText); Assert.Equal ("-1", oldText); - Assert.Equal ("-2", tf.Text); + Assert.Equal ("-2", tf.Text.ToString ()); // DeleteCharLeft tf.SelectedStart = 1; @@ -1154,7 +1154,7 @@ public void DeleteSelectedText_InsertText_DeleteCharLeft_DeleteCharRight_Cut () Assert.True (tf.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ()))); Assert.Equal ("-", newText); Assert.Equal ("-2", oldText); - Assert.Equal ("-", tf.Text); + Assert.Equal ("-", tf.Text.ToString ()); // DeleteCharRight tf.Text = "-1"; @@ -1165,7 +1165,7 @@ public void DeleteSelectedText_InsertText_DeleteCharLeft_DeleteCharRight_Cut () Assert.True (tf.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ()))); Assert.Equal ("-", newText); Assert.Equal ("-1", oldText); - Assert.Equal ("-", tf.Text); + Assert.Equal ("-", tf.Text.ToString ()); // Cut tf.Text = "-1"; @@ -1176,7 +1176,7 @@ public void DeleteSelectedText_InsertText_DeleteCharLeft_DeleteCharRight_Cut () Assert.True (tf.ProcessKey (new KeyEvent (Key.X | Key.CtrlMask, new KeyModifiers ()))); Assert.Equal ("-", newText); Assert.Equal ("-1", oldText); - Assert.Equal ("-", tf.Text); + Assert.Equal ("-", tf.Text.ToString ()); } [Fact] diff --git a/UnitTests/TextFormatterTests.cs b/UnitTests/TextFormatterTests.cs index 8fef1dff21..e2750439a4 100644 --- a/UnitTests/TextFormatterTests.cs +++ b/UnitTests/TextFormatterTests.cs @@ -2968,7 +2968,7 @@ public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Ver [Fact] public void Draw_Horizontal_Throws_IndexOutOfRangeException_With_Negative_Bounds () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; @@ -3008,7 +3008,7 @@ public void TestClipOrPad_LongWord () [Fact] public void Draw_Vertical_Throws_IndexOutOfRangeException_With_Negative_Bounds () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index e7d8813bd9..90debaafb9 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -22,16 +22,12 @@ public TextViewTests (ITestOutputHelper output) // This is necessary because a) Application is a singleton and Init/Shutdown must be called // as a pair, and b) all unit test functions should be atomic. [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public class InitShutdown : Xunit.Sdk.BeforeAfterTestAttribute { + public class TextViewTestsAutoInitShutdown : AutoInitShutdownAttribute { public static string txt = "TAB to jump between text fields."; public override void Before (MethodInfo methodUnderTest) { - if (_textView != null) { - throw new InvalidOperationException ("After did not run."); - } - - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + base.Before (methodUnderTest); // 1 2 3 // 01234567890123456789012345678901=32 (Length) @@ -47,12 +43,12 @@ public override void Before (MethodInfo methodUnderTest) public override void After (MethodInfo methodUnderTest) { _textView = null; - Application.Shutdown (); + base.After (methodUnderTest); } } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Changing_Selection_Or_CursorPosition_Update_SelectedLength_And_SelectedText () { _textView.SelectionStartColumn = 2; @@ -69,7 +65,7 @@ public void Changing_Selection_Or_CursorPosition_Update_SelectedLength_And_Selec } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Selection_With_Value_Less_Than_Zero_Changes_To_Zero () { _textView.SelectionStartColumn = -2; @@ -81,7 +77,7 @@ public void Selection_With_Value_Less_Than_Zero_Changes_To_Zero () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Selection_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length () { _textView.CursorPosition = new Point (2, 0); @@ -94,7 +90,7 @@ public void Selection_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Selection_With_Empty_Text () { _textView = new TextView (); @@ -108,7 +104,7 @@ public void Selection_With_Empty_Text () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Selection_And_CursorPosition_With_Value_Greater_Than_Text_Length_Changes_Both_To_Text_Length () { _textView.CursorPosition = new Point (33, 2); @@ -123,7 +119,7 @@ public void Selection_And_CursorPosition_With_Value_Greater_Than_Text_Length_Cha } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void CursorPosition_With_Value_Less_Than_Zero_Changes_To_Zero () { _textView.CursorPosition = new Point (-1, -1); @@ -134,7 +130,7 @@ public void CursorPosition_With_Value_Less_Than_Zero_Changes_To_Zero () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void CursorPosition_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length () { _textView.CursorPosition = new Point (33, 1); @@ -145,7 +141,7 @@ public void CursorPosition_With_Value_Greater_Than_Text_Length_Changes_To_Text_L } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordForward_With_No_Selection () { _textView.CursorPosition = new Point (0, 0); @@ -208,7 +204,7 @@ public void WordForward_With_No_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordBackward_With_No_Selection () { _textView.CursorPosition = new Point (_textView.Text.Length, 0); @@ -271,7 +267,7 @@ public void WordBackward_With_No_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordForward_With_Selection () { _textView.CursorPosition = new Point (0, 0); @@ -336,7 +332,7 @@ public void WordForward_With_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordBackward_With_Selection () { _textView.CursorPosition = new Point (_textView.Text.Length, 0); @@ -401,7 +397,7 @@ public void WordBackward_With_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordForward_With_The_Same_Values_For_SelectedStart_And_CursorPosition_And_Not_Starting_At_Beginning_Of_The_Text () { _textView.CursorPosition = new Point (10, 0); @@ -450,7 +446,7 @@ public void WordForward_With_The_Same_Values_For_SelectedStart_And_CursorPositio } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordBackward_With_The_Same_Values_For_SelectedStart_And_CursorPosition_And_Not_Starting_At_Beginning_Of_The_Text () { _textView.CursorPosition = new Point (10, 0); @@ -491,7 +487,7 @@ public void WordBackward_With_The_Same_Values_For_SelectedStart_And_CursorPositi } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordForward_With_No_Selection_And_With_More_Than_Only_One_Whitespace_And_With_Only_One_Letter () { // 1 2 3 4 5 @@ -597,7 +593,7 @@ public void WordForward_With_No_Selection_And_With_More_Than_Only_One_Whitespace } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordBackward_With_No_Selection_And_With_More_Than_Only_One_Whitespace_And_With_Only_One_Letter () { // 1 2 3 4 5 @@ -703,7 +699,7 @@ public void WordBackward_With_No_Selection_And_With_More_Than_Only_One_Whitespac } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordBackward_Multiline_With_Selection () { // 4 3 2 1 @@ -818,7 +814,7 @@ public void WordBackward_Multiline_With_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordForward_Multiline_With_Selection () { // 1 2 3 4 @@ -932,7 +928,7 @@ public void WordForward_Multiline_With_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Kill_To_End_Delete_Forwards_And_Copy_To_The_Clipboard () { _textView.Text = "This is the first line.\nThis is the second line."; @@ -945,26 +941,26 @@ public void Kill_To_End_Delete_Forwards_And_Copy_To_The_Clipboard () _textView.ProcessKey (new KeyEvent (Key.K | Key.CtrlMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ($"{Environment.NewLine}This is the second line.", _textView.Text); - Assert.Equal ("This is the first line.", Clipboard.Contents); + Assert.Equal ($"{Environment.NewLine}This is the second line.", _textView.Text.ToString ()); + Assert.Equal ("This is the first line.", Clipboard.Contents.ToString ()); break; case 1: _textView.ProcessKey (new KeyEvent (Key.DeleteChar | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ("This is the second line.", _textView.Text); - Assert.Equal ($"This is the first line.{Environment.NewLine}", Clipboard.Contents); + Assert.Equal ("This is the second line.", _textView.Text.ToString ()); + Assert.Equal ($"This is the first line.{Environment.NewLine}", Clipboard.Contents.ToString()); break; case 2: _textView.ProcessKey (new KeyEvent (Key.K | Key.CtrlMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ("", _textView.Text); - Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.", Clipboard.Contents); + Assert.Equal ("", _textView.Text.ToString ()); + Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.", Clipboard.Contents.ToString ()); // Paste _textView.ProcessKey (new KeyEvent (Key.Y | Key.CtrlMask, new KeyModifiers ())); - Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.", _textView.Text); + Assert.Equal ($"This is the first line.{Environment.NewLine}This is the second line.", _textView.Text.ToString ()); break; default: iterationsFinished = true; @@ -975,7 +971,7 @@ public void Kill_To_End_Delete_Forwards_And_Copy_To_The_Clipboard () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Kill_To_Start_Delete_Backwards_And_Copy_To_The_Clipboard () { _textView.Text = "This is the first line.\nThis is the second line."; @@ -989,26 +985,26 @@ public void Kill_To_Start_Delete_Backwards_And_Copy_To_The_Clipboard () _textView.ProcessKey (new KeyEvent (Key.K | Key.AltMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (1, _textView.CursorPosition.Y); - Assert.Equal ($"This is the first line.{Environment.NewLine}", _textView.Text); - Assert.Equal ($"This is the second line.", Clipboard.Contents); + Assert.Equal ($"This is the first line.{Environment.NewLine}", _textView.Text.ToString()); + Assert.Equal ($"This is the second line.", Clipboard.Contents.ToString ()); break; case 1: _textView.ProcessKey (new KeyEvent (Key.Backspace | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ())); Assert.Equal (23, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ("This is the first line.", _textView.Text); - Assert.Equal ($"This is the second line.{Environment.NewLine}", Clipboard.Contents); + Assert.Equal ("This is the first line.", _textView.Text.ToString ()); + Assert.Equal ($"This is the second line.{Environment.NewLine}", Clipboard.Contents.ToString ()); break; case 2: _textView.ProcessKey (new KeyEvent (Key.K | Key.AltMask, new KeyModifiers ())); Assert.Equal (0, _textView.CursorPosition.X); Assert.Equal (0, _textView.CursorPosition.Y); - Assert.Equal ("", _textView.Text); - Assert.Equal ($"This is the second line.{Environment.NewLine}This is the first line.", Clipboard.Contents); + Assert.Equal ("", _textView.Text.ToString ()); + Assert.Equal ($"This is the second line.{Environment.NewLine}This is the first line.", Clipboard.Contents.ToString ()); // Paste inverted _textView.ProcessKey (new KeyEvent (Key.Y | Key.CtrlMask, new KeyModifiers ())); - Assert.Equal ($"This is the second line.{Environment.NewLine}This is the first line.", _textView.Text); + Assert.Equal ($"This is the second line.{Environment.NewLine}This is the first line.", _textView.Text.ToString ()); break; default: iterationsFinished = true; @@ -1019,7 +1015,7 @@ public void Kill_To_Start_Delete_Backwards_And_Copy_To_The_Clipboard () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Kill_Delete_WordForward () { _textView.Text = "This is the first line."; @@ -1063,7 +1059,7 @@ public void Kill_Delete_WordForward () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Kill_Delete_WordBackward () { _textView.Text = "This is the first line."; @@ -1108,7 +1104,7 @@ public void Kill_Delete_WordBackward () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Kill_Delete_WordForward_Multiline () { _textView.Text = "This is the first line.\nThis is the second line."; @@ -1188,7 +1184,7 @@ public void Kill_Delete_WordForward_Multiline () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Kill_Delete_WordBackward_Multiline () { _textView.Text = "This is the first line.\nThis is the second line."; @@ -1268,7 +1264,7 @@ public void Kill_Delete_WordBackward_Multiline () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Copy_Or_Cut_Null_If_No_Selection () { _textView.SelectionStartColumn = 0; @@ -1280,7 +1276,7 @@ public void Copy_Or_Cut_Null_If_No_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Copy_Or_Cut_Not_Null_If_Has_Selection () { _textView.SelectionStartColumn = 20; @@ -1293,7 +1289,7 @@ public void Copy_Or_Cut_Not_Null_If_Has_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Copy_Or_Cut_And_Paste_With_Selection () { _textView.SelectionStartColumn = 20; @@ -1312,7 +1308,7 @@ public void Copy_Or_Cut_And_Paste_With_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Copy_Or_Cut_And_Paste_With_No_Selection () { _textView.SelectionStartColumn = 20; @@ -1347,7 +1343,7 @@ public void Copy_Or_Cut_And_Paste_With_No_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Cut_Not_Allowed_If_ReadOnly_Is_True () { _textView.ReadOnly = true; @@ -1368,7 +1364,7 @@ public void Cut_Not_Allowed_If_ReadOnly_Is_True () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Paste_Always_Clear_The_SelectedText () { _textView.SelectionStartColumn = 20; @@ -1381,7 +1377,7 @@ public void Paste_Always_Clear_The_SelectedText () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void TextChanged_Event () { _textView.TextChanged += () => { @@ -1396,7 +1392,7 @@ public void TextChanged_Event () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void TextChanged_Event_NoFires_OnTyping () { var eventcount = 0; @@ -1412,7 +1408,7 @@ public void TextChanged_Event_NoFires_OnTyping () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Used_Is_True_By_Default () { _textView.CursorPosition = new Point (10, 0); @@ -1428,7 +1424,7 @@ public void Used_Is_True_By_Default () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Used_Is_False () { _textView.Used = false; @@ -1445,7 +1441,7 @@ public void Used_Is_False () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Copy_Without_Selection () { _textView.Text = "This is the first line.\nThis is the second line.\n"; @@ -1464,7 +1460,7 @@ public void Copy_Without_Selection () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void TabWidth_Setting_To_Zero_Keeps_AllowsTab () { Assert.Equal (4, _textView.TabWidth); @@ -1483,7 +1479,7 @@ public void TabWidth_Setting_To_Zero_Keeps_AllowsTab () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void AllowsTab_Setting_To_True_Changes_TabWidth_To_Default_If_It_Is_Zero () { _textView.TabWidth = 0; @@ -1499,7 +1495,7 @@ public void AllowsTab_Setting_To_True_Changes_TabWidth_To_Default_If_It_Is_Zero } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void AllowsReturn_Setting_To_True_Changes_Multiline_To_True_If_It_Is_False () { Assert.True (_textView.AllowsReturn); @@ -1521,7 +1517,7 @@ public void AllowsReturn_Setting_To_True_Changes_Multiline_To_True_If_It_Is_Fals } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Multiline_Setting_Changes_AllowsReturn_AllowsTab_Height_WordWrap () { Assert.True (_textView.Multiline); @@ -1556,7 +1552,7 @@ public void Multiline_Setting_Changes_AllowsReturn_AllowsTab_Height_WordWrap () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Tab_Test_Follow_By_BackTab () { Application.Top.Add (_textView); @@ -1592,7 +1588,7 @@ public void Tab_Test_Follow_By_BackTab () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void BackTab_Test_Follow_By_Tab () { Application.Top.Add (_textView); @@ -1635,7 +1631,7 @@ public void BackTab_Test_Follow_By_Tab () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Tab_Test_Follow_By_CursorLeft_And_Then_Follow_By_CursorRight () { Application.Top.Add (_textView); @@ -1678,7 +1674,7 @@ public void Tab_Test_Follow_By_CursorLeft_And_Then_Follow_By_CursorRight () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Tab_Test_Follow_By_BackTab_With_Text () { Application.Top.Add (_textView); @@ -1714,7 +1710,7 @@ public void Tab_Test_Follow_By_BackTab_With_Text () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Tab_Test_Follow_By_Home_And_Then_Follow_By_End_And_Then_Follow_By_BackTab_With_Text () { Application.Top.Add (_textView); @@ -1772,7 +1768,7 @@ public void Tab_Test_Follow_By_Home_And_Then_Follow_By_End_And_Then_Follow_By_Ba } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Tab_Test_Follow_By_CursorLeft_And_Then_Follow_By_CursorRight_With_Text () { Application.Top.Add (_textView); @@ -2020,7 +2016,7 @@ public void WordWrap_True_Text_Always_Returns_Unwrapped () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordWrap_WrapModel_Output () { // 0123456789 @@ -2103,7 +2099,7 @@ public void WordWrap_Deleting_Backwards () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void WordWrap_ReadOnly_CursorPosition_SelectedText_Copy () { // 0123456789 @@ -2228,7 +2224,7 @@ public void Internal_Tests () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void BottomOffset_Sets_To_Zero_Adjust_TopRow () { string text = ""; @@ -2258,7 +2254,7 @@ public void BottomOffset_Sets_To_Zero_Adjust_TopRow () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void RightOffset_Sets_To_Zero_Adjust_leftColumn () { string text = ""; @@ -2288,7 +2284,7 @@ public void RightOffset_Sets_To_Zero_Adjust_leftColumn () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void TextView_SpaceHandling () { var tv = new TextView () { @@ -2316,7 +2312,7 @@ public void TextView_SpaceHandling () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void CanFocus_False_Wont_Focus_With_Mouse () { var top = Application.Top; @@ -2383,7 +2379,7 @@ public void CanFocus_False_Wont_Focus_With_Mouse () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void DesiredCursorVisibility_Vertical_Navigation () { string text = ""; @@ -2422,7 +2418,7 @@ public void DesiredCursorVisibility_Vertical_Navigation () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void DesiredCursorVisibility_Horizontal_Navigation () { string text = ""; @@ -5942,7 +5938,7 @@ public void ScrollTo_CursorPosition () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void Mouse_Button_Shift_Preserves_Selection () { Assert.Equal ("TAB to jump between text fields.", _textView.Text); @@ -6333,7 +6329,7 @@ This is the second line. } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void TextView_InsertText_Newline_LF () { var tv = new TextView { @@ -6402,7 +6398,7 @@ public void TextView_InsertText_Newline_LF () } [Fact] - [InitShutdown] + [TextViewTestsAutoInitShutdown] public void TextView_InsertText_Newline_CRLF () { var tv = new TextView { @@ -6621,7 +6617,7 @@ public void ContentsChanged_Event_Fires_On_Typing () Assert.Equal ("Yay", tv.Text.ToString ()); } - [Fact, InitShutdown] + [Fact, TextViewTestsAutoInitShutdown] public void ContentsChanged_Event_Fires_Using_Kill_Delete_Tests () { var eventcount = 0; @@ -6647,7 +6643,8 @@ public void ContentsChanged_Event_Fires_Using_Kill_Delete_Tests () Assert.Equal (expectedEventCount, eventcount); } - [Fact, InitShutdown] + + [Fact, TextViewTestsAutoInitShutdown] public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests () { var eventcount = 0; @@ -6659,7 +6656,7 @@ public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests () var expectedEventCount = 1; // reset - _textView.Text = InitShutdown.txt; + _textView.Text = TextViewTestsAutoInitShutdown.txt; Assert.Equal (expectedEventCount, eventcount); expectedEventCount += 3; @@ -6668,7 +6665,7 @@ public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests () // reset expectedEventCount += 1; - _textView.Text = InitShutdown.txt; + _textView.Text = TextViewTestsAutoInitShutdown.txt; Assert.Equal (expectedEventCount, eventcount); expectedEventCount += 3; @@ -6677,7 +6674,7 @@ public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests () // reset expectedEventCount += 1; - _textView.Text = InitShutdown.txt; + _textView.Text = TextViewTestsAutoInitShutdown.txt; Assert.Equal (expectedEventCount, eventcount); expectedEventCount += 1; @@ -6686,7 +6683,7 @@ public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests () // reset expectedEventCount += 1; - _textView.Text = InitShutdown.txt; + _textView.Text = TextViewTestsAutoInitShutdown.txt; Assert.Equal (expectedEventCount, eventcount); expectedEventCount += 1; @@ -6695,7 +6692,7 @@ public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests () // reset expectedEventCount += 1; - _textView.Text = InitShutdown.txt; + _textView.Text = TextViewTestsAutoInitShutdown.txt; Assert.Equal (expectedEventCount, eventcount); expectedEventCount += 4; @@ -6704,7 +6701,7 @@ public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests () // reset expectedEventCount += 1; - _textView.Text = InitShutdown.txt; + _textView.Text = TextViewTestsAutoInitShutdown.txt; Assert.Equal (expectedEventCount, eventcount); expectedEventCount += 4; @@ -6712,7 +6709,7 @@ public void ContentsChanged_Event_Fires_Using_Copy_Or_Cut_Tests () Assert.Equal (expectedEventCount, eventcount); } - [Fact, InitShutdown] + [Fact, TextViewTestsAutoInitShutdown] public void ContentsChanged_Event_Fires_On_Undo_Redo () { var eventcount = 0; diff --git a/UnitTests/ToplevelTests.cs b/UnitTests/ToplevelTests.cs index f96705ad64..a528a3a617 100644 --- a/UnitTests/ToplevelTests.cs +++ b/UnitTests/ToplevelTests.cs @@ -596,7 +596,7 @@ void View_Added (View obj) var win = new Window (); win.Add (view); - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var top = Application.Top; top.Add (win); @@ -663,7 +663,7 @@ void View_Initialized (object sender, EventArgs e) [AutoInitShutdown] public void FileDialog_FileSystemWatcher () { - for (int i = 0; i < 256; i++) { + for (int i = 0; i < 8; i++) { var fd = new FileDialog (); fd.Ready += () => Application.RequestStop (); Application.Run (fd); diff --git a/UnitTests/TreeViewTests.cs b/UnitTests/TreeViewTests.cs index 31c6bf5389..8cbb5825d7 100644 --- a/UnitTests/TreeViewTests.cs +++ b/UnitTests/TreeViewTests.cs @@ -953,7 +953,7 @@ public override bool Equals (object obj) private void InitFakeDriver () { var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); driver.Init (() => { }); } } diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 37132be115..68da4e1d00 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -1,6 +1,9 @@  - net6.0 + net7.0 + + + Preview false diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs index 8fc471f0f3..07931404c9 100644 --- a/UnitTests/ViewTests.cs +++ b/UnitTests/ViewTests.cs @@ -574,7 +574,7 @@ public void CanFocus_Set_Changes_TabIndex_And_TabStop () [Fact] public void Initialized_Event_Comparing_With_Added_Event () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = new Toplevel () { Id = "0", }; @@ -673,7 +673,7 @@ public void Initialized_Event_Comparing_With_Added_Event () [Fact] public void Initialized_Event_Will_Be_Invoked_When_Added_Dynamically () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = new Toplevel () { Id = "0", }; @@ -782,7 +782,7 @@ public void CanFocus_Faced_With_Container () [Fact] public void CanFocus_Faced_With_Container_Before_Run () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -819,7 +819,7 @@ public void CanFocus_Faced_With_Container_Before_Run () [Fact] public void CanFocus_Faced_With_Container_After_Run () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -862,7 +862,7 @@ public void CanFocus_Faced_With_Container_After_Run () [Fact] public void CanFocus_Container_ToFalse_Turns_All_Subviews_ToFalse_Too () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -897,7 +897,7 @@ public void CanFocus_Container_ToFalse_Turns_All_Subviews_ToFalse_Too () [Fact] public void CanFocus_Container_Toggling_All_Subviews_To_Old_Value_When_Is_True () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; @@ -941,7 +941,7 @@ public void Navigation_With_Null_Focused_View () { // Non-regression test for #882 (NullReferenceException during keyboard navigation when Focused is null) - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); Application.Top.Ready += () => { Assert.Null (Application.Top.Focused); @@ -959,7 +959,7 @@ public void Navigation_With_Null_Focused_View () [Fact] public void Multi_Thread_Toplevels () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); var t = Application.Top; var w = new Window (); @@ -1148,7 +1148,7 @@ public void FocusNearestView_Ensure_Focus_Ordered () [Fact] public void KeyPress_Handled_To_True_Prevents_Changes () { - Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver ()); Console.MockKeyPresses.Push (new ConsoleKeyInfo ('N', ConsoleKey.N, false, false, false)); @@ -1584,7 +1584,7 @@ H X public void LabelChangeText_RendersCorrectly_Constructors (int choice) { var driver = new FakeDriver (); - Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver); try { // Create a label with a short text