Fixed all modelusage bugs?

Replaced static `Application` references with instance-based `App`
context across the codebase. Updated calls to `Application.RequestStop()`
and `Application.Screen` to use `App?.RequestStop()` and `App?.Screen`
for better encapsulation and flexibility.

Refactored test infrastructure to align with the new context, including
reintroducing `FakeApplicationFactory` and `FakeApplicationLifecycle`
for testing purposes. Improved logging, error handling, and test
clarity by adding `logWriter` support and simplifying test setup.

Removed redundant or obsolete code, such as `NetSequences` and the old
`FakeApplicationFactory` implementation. Updated documentation to
reflect the new `IApplication.RequestStop()` usage.
This commit is contained in:
Tig
2025-11-23 07:13:47 -07:00
parent ad6925a13a
commit bc0634cf44
18 changed files with 75 additions and 119 deletions

View File

@@ -266,7 +266,7 @@ public class Dialogs : Scenario
{ {
Title = titleEdit.Text, Title = titleEdit.Text,
Text = "Dialog Text", Text = "Dialog Text",
ButtonAlignment = (Alignment)Enum.Parse (typeof (Alignment), alignmentGroup.Labels! [(int)alignmentGroup.Value!.Value] [1..]), ButtonAlignment = (Alignment)Enum.Parse (typeof (Alignment), alignmentGroup.Labels! [(int)alignmentGroup.Value!.Value] [0..]),
Buttons = buttons.ToArray () Buttons = buttons.ToArray ()
}; };

View File

@@ -200,7 +200,7 @@ public class DynamicStatusBar : Scenario
TextTitle.Text = string.Empty; TextTitle.Text = string.Empty;
Application.RequestStop (); Application.RequestStop ();
}; };
var dialog = new Dialog { Title = "Enter the menu details.", Buttons = [btnOk, btnCancel], Height = Dim.Auto (DimAutoStyle.Content, 17, Application.Screen.Height) }; var dialog = new Dialog { Title = "Enter the menu details.", Buttons = [btnOk, btnCancel], Height = Dim.Auto (DimAutoStyle.Content, 17, App?.Screen.Height) };
Width = Dim.Fill (); Width = Dim.Fill ();
Height = Dim.Fill () - 2; Height = Dim.Fill () - 2;

View File

@@ -37,7 +37,7 @@ public partial class ColorPicker
{ {
accept = true; accept = true;
e.Handled = true; e.Handled = true;
Application.RequestStop (); (s as View)?.App?.RequestStop ();
}; };
var btnCancel = new Button var btnCancel = new Button
@@ -51,7 +51,7 @@ public partial class ColorPicker
btnCancel.Accepting += (s, e) => btnCancel.Accepting += (s, e) =>
{ {
e.Handled = true; e.Handled = true;
Application.RequestStop (); (s as View)?.App ?.RequestStop ();
}; };
d.Add (btnOk); d.Add (btnOk);

View File

@@ -11,7 +11,7 @@ namespace Terminal.Gui.Views;
/// <see cref="IApplication.Run(Toplevel, Func{Exception, bool})"/>. This will execute the dialog until /// <see cref="IApplication.Run(Toplevel, Func{Exception, bool})"/>. This will execute the dialog until
/// it terminates via the <see cref="Application.QuitKey"/> (`Esc` by default), /// it terminates via the <see cref="Application.QuitKey"/> (`Esc` by default),
/// or when one of the views or buttons added to the dialog calls /// or when one of the views or buttons added to the dialog calls
/// <see cref="Application.RequestStop"/>. /// <see cref="IApplication.RequestStop()"/>.
/// </remarks> /// </remarks>
public class Dialog : Window public class Dialog : Window
{ {

View File

@@ -138,7 +138,7 @@ public class DefaultFileOperations : IFileOperations
btnOk.Accepting += (s, e) => btnOk.Accepting += (s, e) =>
{ {
confirm = true; confirm = true;
Application.RequestStop (); (s as View)?.App?.RequestStop ();
// When Accepting is handled, set e.Handled to true to prevent further processing. // When Accepting is handled, set e.Handled to true to prevent further processing.
e.Handled = true; e.Handled = true;
}; };
@@ -147,7 +147,7 @@ public class DefaultFileOperations : IFileOperations
btnCancel.Accepting += (s, e) => btnCancel.Accepting += (s, e) =>
{ {
confirm = false; confirm = false;
Application.RequestStop (); (s as View)?.App?.RequestStop ();
// When Accepting is handled, set e.Handled to true to prevent further processing. // When Accepting is handled, set e.Handled to true to prevent further processing.
e.Handled = true; e.Handled = true;
}; };

View File

@@ -108,7 +108,7 @@ public class FileDialog : Dialog, IDesignable
if (Modal) if (Modal)
{ {
Application.RequestStop (); (s as View)?.App?.RequestStop ();
} }
}; };
@@ -468,7 +468,6 @@ public class FileDialog : Dialog, IDesignable
Style.IconProvider.IsOpenGetter = _treeView.IsExpanded; Style.IconProvider.IsOpenGetter = _treeView.IsExpanded;
_treeView.AddObjects (_treeRoots.Keys); _treeView.AddObjects (_treeRoots.Keys);
#if MENU_V1
// if filtering on file type is configured then create the ComboBox and establish // if filtering on file type is configured then create the ComboBox and establish
// initial filtering by extension(s) // initial filtering by extension(s)
@@ -479,6 +478,7 @@ public class FileDialog : Dialog, IDesignable
// Fiddle factor // Fiddle factor
int width = AllowedTypes.Max (a => a.ToString ()!.Length) + 6; int width = AllowedTypes.Max (a => a.ToString ()!.Length) + 6;
#if MENU_V1
_allowedTypeMenu = new ( _allowedTypeMenu = new (
"<placeholder>", "<placeholder>",
_allowedTypeMenuItems = AllowedTypes.Select ( _allowedTypeMenuItems = AllowedTypes.Select (
@@ -512,8 +512,8 @@ public class FileDialog : Dialog, IDesignable
}; };
Add (_allowedTypeMenuBar); Add (_allowedTypeMenuBar);
}
#endif #endif
}
// if no path has been provided // if no path has been provided
if (_tbPath.Text.Length <= 0) if (_tbPath.Text.Length <= 0)
@@ -879,7 +879,7 @@ public class FileDialog : Dialog, IDesignable
if (Modal) if (Modal)
{ {
Application.RequestStop (); App?.RequestStop ();
} }
} }

View File

@@ -360,7 +360,7 @@ public static class MessageBox
if (count == defaultButton) if (count == defaultButton)
{ {
b.IsDefault = true; b.IsDefault = true;
b.Accepting += (_, e) => b.Accepting += (s, e) =>
{ {
if (e?.Context?.Source is Button button) if (e?.Context?.Source is Button button)
{ {
@@ -376,7 +376,7 @@ public static class MessageBox
e.Handled = true; e.Handled = true;
} }
Application.RequestStop (); (s as View)?.App?.RequestStop ();
}; };
} }

View File

@@ -1534,7 +1534,7 @@ public class TableView : View, IDesignable
/// <param name="width"></param> /// <param name="width"></param>
private void ClearLine (int row, int width) private void ClearLine (int row, int width)
{ {
if (Application.Screen.Height == 0) if (App?.Screen.Height == 0)
{ {
return; return;
} }
@@ -1810,7 +1810,7 @@ public class TableView : View, IDesignable
} }
} }
if (Application.Screen.Height > 0) if (App?.Screen.Height > 0)
{ {
AddRuneAt (c, row, rune); AddRuneAt (c, row, rune);
} }

View File

@@ -458,7 +458,7 @@ public class Wizard : Dialog
if (IsCurrentTop) if (IsCurrentTop)
{ {
Application.RequestStop (this); (sender as View)?.App?.RequestStop (this);
e.Handled = true; e.Handled = true;
} }

View File

@@ -60,7 +60,7 @@ public class FileDialogFluentTests
public void CancelFileDialog_QuitKey_Quits (TestDriver d) public void CancelFileDialog_QuitKey_Quits (TestDriver d)
{ {
SaveDialog? sd = null; SaveDialog? sd = null;
using var c = With.A (() => NewSaveDialog (out sd), 100, 20, d) using GuiTestContext c = With.A (() => NewSaveDialog (out sd), 100, 20, d, logWriter: _out)
.ScreenShot ("Save dialog", _out) .ScreenShot ("Save dialog", _out)
.EnqueueKeyEvent (Application.QuitKey) .EnqueueKeyEvent (Application.QuitKey)
.AssertTrue (sd!.Canceled); .AssertTrue (sd!.Canceled);
@@ -93,7 +93,7 @@ public class FileDialogFluentTests
public void CancelFileDialog_UsingCancelButton_AltC (TestDriver d) public void CancelFileDialog_UsingCancelButton_AltC (TestDriver d)
{ {
SaveDialog? sd = null; SaveDialog? sd = null;
using var c = With.A (() => NewSaveDialog (out sd), 100, 20, d) using var c = With.A (() => NewSaveDialog (out sd), 100, 20, d, _out)
.ScreenShot ("Save dialog", _out) .ScreenShot ("Save dialog", _out)
.EnqueueKeyEvent (Key.C.WithAlt) .EnqueueKeyEvent (Key.C.WithAlt)
.AssertTrue (sd!.Canceled); .AssertTrue (sd!.Canceled);
@@ -132,12 +132,13 @@ public class FileDialogFluentTests
{ {
SaveDialog? sd = null; SaveDialog? sd = null;
MockFileSystem? fs = null; MockFileSystem? fs = null;
using var c = With.A (() => NewSaveDialog (out sd, out fs, modal: false), 100, 20, d) using GuiTestContext c = With.A (() => NewSaveDialog (out sd, out fs, modal: false), 100, 20, d)
.ScreenShot ("Save dialog", _out) .ScreenShot ("Save dialog", _out)
.Focus<Button> (b => b.Text == "_Save") .Focus<Button> (b => b.Text == "_Save")
.EnqueueKeyEvent (Key.Enter) .EnqueueKeyEvent (Key.Enter)
.AssertFalse (sd!.Canceled) .AssertFalse (sd!.Canceled)
.AssertEqual (GetFileSystemRoot (fs!), sd!.FileName); .AssertEqual (GetFileSystemRoot (fs!), sd!.FileName)
;
} }
private string GetFileSystemRoot (IFileSystem fs) private string GetFileSystemRoot (IFileSystem fs)

View File

@@ -18,7 +18,7 @@ public class GuiTestContextTests (ITestOutputHelper outputHelper)
{ {
using var context = new GuiTestContext (d, _out, TimeSpan.FromSeconds (10)); using var context = new GuiTestContext (d, _out, TimeSpan.FromSeconds (10));
Assert.NotEqual (Rectangle.Empty, Application.Screen); Assert.NotEqual (Rectangle.Empty, context.App?.Screen);
} }
[Theory] [Theory]

View File

@@ -205,11 +205,6 @@ public partial class GuiTestContext
App.Driver.EnqueueKeyEvent (key); App.Driver.EnqueueKeyEvent (key);
WaitUntil (() => keyReceived); WaitUntil (() => keyReceived);
} }
else
{
Fail ("Expected Application.Driver to be non-null.");
}
return this; return this;

View File

@@ -68,7 +68,7 @@ public partial class GuiTestContext : IDisposable
try try
{ {
InitializeApplication (); App?.Init (GetDriverName ());
_booting.Release (); _booting.Release ();
// After Init, Application.Screen should be set by the driver // After Init, Application.Screen should be set by the driver
@@ -119,10 +119,22 @@ public partial class GuiTestContext : IDisposable
{ {
try try
{ {
InitializeApplication (); try
{
App?.Init (GetDriverName ());
}
catch (Exception e)
{
Logging.Error(e.Message);
_runCancellationTokenSource.Cancel ();
}
finally
{
_booting.Release (); _booting.Release ();
}
if (App is { Initialized: true })
{
Toplevel t = topLevelBuilder (); Toplevel t = topLevelBuilder ();
t.Closed += (s, e) => { Finished = true; }; t.Closed += (s, e) => { Finished = true; };
App?.Run (t); // This will block, but it's on a background thread now App?.Run (t); // This will block, but it's on a background thread now
@@ -132,8 +144,11 @@ public partial class GuiTestContext : IDisposable
App?.Shutdown (); App?.Shutdown ();
_runCancellationTokenSource.Cancel (); _runCancellationTokenSource.Cancel ();
} }
}
catch (OperationCanceledException) catch (OperationCanceledException)
{ } {
Logging.Trace ("OperationCanceledException");
}
catch (Exception ex) catch (Exception ex)
{ {
_backgroundException = ex; _backgroundException = ex;
@@ -142,7 +157,6 @@ public partial class GuiTestContext : IDisposable
finally finally
{ {
CleanupApplication (); CleanupApplication ();
if (_logWriter != null) if (_logWriter != null)
{ {
WriteOutLogs (_logWriter); WriteOutLogs (_logWriter);
@@ -165,11 +179,6 @@ public partial class GuiTestContext : IDisposable
} }
} }
private void InitializeApplication ()
{
App?.Init (GetDriverName ());
}
/// <summary> /// <summary>
/// Common initialization for both constructors. /// Common initialization for both constructors.
@@ -316,7 +325,7 @@ public partial class GuiTestContext : IDisposable
throw new NotSupportedException ("Cannot WaitIteration during Invoke"); throw new NotSupportedException ("Cannot WaitIteration during Invoke");
} }
Logging.Trace ($"WaitIteration started"); //Logging.Trace ($"WaitIteration started");
if (action is null) if (action is null)
{ {
action = (app) => { }; action = (app) => { };
@@ -358,8 +367,9 @@ public partial class GuiTestContext : IDisposable
GuiTestContext? c = null; GuiTestContext? c = null;
var sw = Stopwatch.StartNew (); var sw = Stopwatch.StartNew ();
//Logging.Trace ($"WaitUntil started with timeout {_timeout}"); Logging.Trace ($"WaitUntil started with timeout {_timeout}");
int count = 0;
while (!condition ()) while (!condition ())
{ {
if (sw.Elapsed > _timeout) if (sw.Elapsed > _timeout)
@@ -368,8 +378,10 @@ public partial class GuiTestContext : IDisposable
} }
c = WaitIteration (); c = WaitIteration ();
count++;
} }
Logging.Trace ($"WaitUntil completed after {sw.ElapsedMilliseconds}ms and {count} iterations");
return c ?? this; return c ?? this;
} }

View File

@@ -1,53 +0,0 @@
namespace TerminalGuiFluentTesting;
class NetSequences
{
public static ConsoleKeyInfo [] Down = new []
{
new ConsoleKeyInfo('\x1B', ConsoleKey.Enter, false, false, false),
new ConsoleKeyInfo('[', ConsoleKey.None, false, false, false),
new ConsoleKeyInfo('B', ConsoleKey.None, false, false, false),
};
public static ConsoleKeyInfo [] Up = new []
{
new ConsoleKeyInfo('\x1B', ConsoleKey.Enter, false, false, false),
new ConsoleKeyInfo('[', ConsoleKey.None, false, false, false),
new ConsoleKeyInfo('A', ConsoleKey.None, false, false, false),
};
public static ConsoleKeyInfo [] Left = new []
{
new ConsoleKeyInfo('\x1B', ConsoleKey.Enter, false, false, false),
new ConsoleKeyInfo('[', ConsoleKey.None, false, false, false),
new ConsoleKeyInfo('D', ConsoleKey.None, false, false, false),
};
public static ConsoleKeyInfo [] Right = new []
{
new ConsoleKeyInfo('\x1B', ConsoleKey.Enter, false, false, false),
new ConsoleKeyInfo('[', ConsoleKey.None, false, false, false),
new ConsoleKeyInfo('C', ConsoleKey.None, false, false, false),
};
public static IEnumerable<ConsoleKeyInfo> Click (int button, int screenX, int screenY)
{
// Adjust for 1-based coordinates
int adjustedX = screenX + 1;
int adjustedY = screenY + 1;
// Mouse press sequence
var sequence = $"\x1B[<{button};{adjustedX};{adjustedY}M";
foreach (char c in sequence)
{
yield return new ConsoleKeyInfo (c, ConsoleKey.None, false, false, false);
}
// Mouse release sequence
sequence = $"\x1B[<{button};{adjustedX};{adjustedY}m";
foreach (char c in sequence)
{
yield return new ConsoleKeyInfo (c, ConsoleKey.None, false, false, false);
}
}
}

View File

@@ -29,10 +29,11 @@ public static class With
/// <param name="width"></param> /// <param name="width"></param>
/// <param name="height"></param> /// <param name="height"></param>
/// <param name="testDriver"></param> /// <param name="testDriver"></param>
/// <param name="logWriter"></param>
/// <returns></returns> /// <returns></returns>
public static GuiTestContext A (Func<Toplevel> toplevelFactory, int width, int height, TestDriver testDriver) public static GuiTestContext A (Func<Toplevel> toplevelFactory, int width, int height, TestDriver testDriver, TextWriter? logWriter = null)
{ {
return new (toplevelFactory, width, height, testDriver, null, Timeout); return new (toplevelFactory, width, height, testDriver, logWriter, Timeout);
} }
/// <summary> /// <summary>
/// The global timeout to allow for any given application to run for before shutting down. /// The global timeout to allow for any given application to run for before shutting down.

View File

@@ -59,7 +59,7 @@ public class TableViewTests (ITestOutputHelper output)
{ {
var tv = new TableView var tv = new TableView
{ {
Driver = ApplicationImpl.Instance.Driver, App = ApplicationImpl.Instance,
Width = 20, Height = 4 Width = 20, Height = 4
}; };
@@ -683,7 +683,7 @@ public class TableViewTests (ITestOutputHelper output)
{ {
var tableView = new TableView () var tableView = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
tableView.BeginInit (); tableView.BeginInit ();
tableView.EndInit (); tableView.EndInit ();
@@ -765,7 +765,7 @@ public class TableViewTests (ITestOutputHelper output)
{ {
var tableView = new TableView () var tableView = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
tableView.BeginInit (); tableView.BeginInit ();
tableView.EndInit (); tableView.EndInit ();
@@ -830,7 +830,7 @@ public class TableViewTests (ITestOutputHelper output)
{ {
var tableView = new TableView () var tableView = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
tableView.BeginInit (); tableView.BeginInit ();
@@ -1579,7 +1579,7 @@ public class TableViewTests (ITestOutputHelper output)
{ {
var tv = new TableView () var tv = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
tv.SchemeName = "TopLevel"; tv.SchemeName = "TopLevel";
tv.Viewport = new (0, 0, 50, 7); tv.Viewport = new (0, 0, 50, 7);
@@ -2226,7 +2226,7 @@ public class TableViewTests (ITestOutputHelper output)
ApplicationImpl.Instance.Driver!.SetScreenSize (100, 100); ApplicationImpl.Instance.Driver!.SetScreenSize (100, 100);
var tv = new TableView () var tv = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
tv.SchemeName = "TopLevel"; tv.SchemeName = "TopLevel";
tv.Viewport = new (0, 0, 50, 6); tv.Viewport = new (0, 0, 50, 6);
@@ -2433,7 +2433,7 @@ A B C
var tv = new TableView () var tv = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
//tv.BeginInit (); tv.EndInit (); //tv.BeginInit (); tv.EndInit ();
@@ -3441,7 +3441,7 @@ A B C
{ {
var tableView = new TableView () var tableView = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
tableView.BeginInit (); tableView.BeginInit ();
tableView.EndInit (); tableView.EndInit ();
@@ -3473,7 +3473,7 @@ A B C
{ {
var tv = new TableView () var tv = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver, App = ApplicationImpl.Instance,
SchemeName = "TopLevel", SchemeName = "TopLevel",
Viewport = new (0, 0, 25, 6) Viewport = new (0, 0, 25, 6)
}; };
@@ -3504,7 +3504,7 @@ A B C
{ {
var tableView = new TableView () var tableView = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
tableView.SchemeName = "TopLevel"; tableView.SchemeName = "TopLevel";
@@ -3537,7 +3537,7 @@ A B C
{ {
var tv = new TableView () var tv = new TableView ()
{ {
Driver = ApplicationImpl.Instance.Driver App = ApplicationImpl.Instance
}; };
tv.BeginInit (); tv.BeginInit ();
tv.EndInit (); tv.EndInit ();