Skip to content

Commit

Permalink
Allow DI of input/output
Browse files Browse the repository at this point in the history
  • Loading branch information
tznind committed Dec 15, 2024
1 parent 55fb132 commit 8216597
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 31 deletions.
15 changes: 10 additions & 5 deletions Terminal.Gui/Application/TimedEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,21 @@ private long NudgeToUniqueKey (long k)
// INTENT: It looks like the general architecture here is trying to be a form of publisher/consumer pattern.
private void RunIdle ()
{
List<Func<bool>> iterate;

iterate = _idleHandlers;
_idleHandlers = new List<Func<bool>> ();
Func<bool> [] iterate;
lock (_idleHandlers)
{
iterate = _idleHandlers.ToArray ();
_idleHandlers = new List<Func<bool>> ();
}

foreach (Func<bool> idle in iterate)
{
if (idle ())
{
_idleHandlers.Add (idle);
lock (_idleHandlers)
{
_idleHandlers.Add (idle);
}
}
}
}
Expand Down
39 changes: 28 additions & 11 deletions Terminal.Gui/ConsoleDrivers/V2/ApplicationV2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,31 @@ namespace Terminal.Gui.ConsoleDrivers.V2;

public class ApplicationV2 : ApplicationImpl
{
private readonly Func<INetInput> _netInputFactory;
private readonly Func<IConsoleOutput> _netOutputFactory;
private readonly Func<IWindowsInput> _winInputFactory;
private readonly Func<IConsoleOutput> _winOutputFactory;
private IMainLoopCoordinator _coordinator;
public ITimedEvents TimedEvents { get; } = new TimedEvents ();
public ApplicationV2 ()
public ApplicationV2 () : this (
()=>new NetInput (),
()=>new NetOutput (),
()=>new WindowsInput (),
()=>new WindowsOutput ()
)
{
}
internal ApplicationV2 (
Func<INetInput> netInputFactory,
Func<IConsoleOutput> netOutputFactory,
Func<IWindowsInput> winInputFactory,
Func<IConsoleOutput> winOutputFactory
)
{
_netInputFactory = netInputFactory;
_netOutputFactory = netOutputFactory;
_winInputFactory = winInputFactory;
_winOutputFactory = winOutputFactory;
IsLegacy = false;
}

Expand Down Expand Up @@ -53,12 +74,7 @@ private void CreateDriver (string driverName)
CreateNetSubcomponents ();
}

_coordinator.StartAsync ();

if (!_coordinator.StartupSemaphore.WaitAsync (TimeSpan.FromSeconds (3)).Result)
{
throw new Exception ("Failed to boot MainLoopCoordinator in sensible timeframe");
}
_coordinator.StartAsync().Wait();

if (Application.Driver == null)
{
Expand All @@ -72,21 +88,21 @@ private void CreateWindowsSubcomponents ()
var inputBuffer = new ConcurrentQueue<WindowsConsole.InputRecord> ();
var loop = new MainLoop<WindowsConsole.InputRecord> ();
_coordinator = new MainLoopCoordinator<WindowsConsole.InputRecord> (TimedEvents,
() => new WindowsInput (),
_winInputFactory,
inputBuffer,
new WindowsInputProcessor (inputBuffer),
() => new WindowsOutput (),
_winOutputFactory,
loop);
}
private void CreateNetSubcomponents ()
{
var inputBuffer = new ConcurrentQueue<ConsoleKeyInfo> ();
var loop = new MainLoop<ConsoleKeyInfo> ();
_coordinator = new MainLoopCoordinator<ConsoleKeyInfo> (TimedEvents,
() => new NetInput (),
_netInputFactory,
inputBuffer,
new NetInputProcessor (inputBuffer),
() => new NetOutput (),
_netOutputFactory,
loop);
}

Expand Down Expand Up @@ -126,6 +142,7 @@ public override void Shutdown ()
{
_coordinator.Stop ();
base.Shutdown ();
Application.Driver = null;
}

/// <inheritdoc />
Expand Down
8 changes: 1 addition & 7 deletions Terminal.Gui/ConsoleDrivers/V2/IMainLoopCoordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@

public interface IMainLoopCoordinator
{
/// <summary>
/// Can be waited on after calling <see cref="StartAsync"/> to know when
/// boot up threads are running. Do not wait unless you constructed the coordinator.
/// Do not wait multiple times on the same coordinator.
/// </summary>
SemaphoreSlim StartupSemaphore { get; }
public void StartAsync ();
public Task StartAsync ();

public void Stop ();
}
4 changes: 4 additions & 0 deletions Terminal.Gui/ConsoleDrivers/V2/INetInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Terminal.Gui;

internal interface INetInput :IConsoleInput<ConsoleKeyInfo>
{ }
4 changes: 4 additions & 0 deletions Terminal.Gui/ConsoleDrivers/V2/IWindowsInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Terminal.Gui;

internal interface IWindowsInput : IConsoleInput<WindowsConsole.InputRecord>
{ }
6 changes: 5 additions & 1 deletion Terminal.Gui/ConsoleDrivers/V2/MainLoopCoordinator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ public MainLoopCoordinator (ITimedEvents timedEvents, Func<IConsoleInput<T>> inp
/// <summary>
/// Starts the main and input loop threads in separate tasks (returning immediately).
/// </summary>
public void StartAsync ()
public async Task StartAsync ()
{
// TODO: if crash on boot then semaphore never finishes
_inputTask = Task.Run (RunInput);
_loopTask = Task.Run (RunLoop);

// Use asynchronous semaphore waiting.
await StartupSemaphore.WaitAsync ().ConfigureAwait (false);
}

private void RunInput ()
Expand Down
2 changes: 1 addition & 1 deletion Terminal.Gui/ConsoleDrivers/V2/NetInput.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Terminal.Gui;

public class NetInput : ConsoleInput<ConsoleKeyInfo>
public class NetInput : ConsoleInput<ConsoleKeyInfo>, INetInput
{
private NetWinVTConsole _adjustConsole;

Expand Down
4 changes: 2 additions & 2 deletions Terminal.Gui/ConsoleDrivers/V2/WindowsInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Terminal.Gui;

internal class WindowsInput : ConsoleInput<InputRecord>
internal class WindowsInput : ConsoleInput<InputRecord>, IWindowsInput
{
private readonly nint _inputHandle;

Expand Down Expand Up @@ -111,4 +111,4 @@ public void Dispose ()
{
SetConsoleMode (_inputHandle, _originalConsoleMode);
}
}
}
1 change: 1 addition & 0 deletions Terminal.Gui/Terminal.Gui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<ItemGroup>
<InternalsVisibleTo Include="UnitTests" />
<InternalsVisibleTo Include="TerminalGuiDesigner" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<!-- =================================================================== -->
<!-- API Documentation -->
Expand Down
2 changes: 0 additions & 2 deletions UnitTests/Application/MainLoopTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -784,8 +784,6 @@ private static void Launch (Random r, TextField tf, int target)
Task.Run (
() =>
{
Thread.Sleep (r.Next (2, 4));

Application.Invoke (
() =>
{
Expand Down
66 changes: 64 additions & 2 deletions UnitTests/ConsoleDrivers/V2/ApplicationV2Tests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
using Terminal.Gui.ConsoleDrivers.V2;
using Moq;
using Terminal.Gui.ConsoleDrivers.V2;

namespace UnitTests.ConsoleDrivers.V2;
public class ApplicationV2Tests
{

private ApplicationV2 NewApplicationV2 ()
{
return new (
Mock.Of<INetInput>,
Mock.Of<IConsoleOutput>,
Mock.Of<IWindowsInput>,
Mock.Of<IConsoleOutput>);
}

[Fact]
public void TestInit_CreatesKeybindings ()
{
var v2 = new ApplicationV2 ();
var v2 = NewApplicationV2();

Application.KeyBindings.Clear();

Expand All @@ -18,4 +29,55 @@ public void TestInit_CreatesKeybindings ()

v2.Shutdown ();
}

[Fact]
public void TestInit_DriverIsFacade ()
{
var v2 = NewApplicationV2();

Assert.Null (Application.Driver);
v2.Init ();
Assert.NotNull (Application.Driver);

var type = Application.Driver.GetType ();
Assert.True(type.IsGenericType);
Assert.True (type.GetGenericTypeDefinition () == typeof (ConsoleDriverFacade<>));
v2.Shutdown ();

Assert.Null (Application.Driver);
}

[Fact]
public void Test_NoInitThrowOnRun ()
{
var app = NewApplicationV2();

var ex = Assert.Throws<Exception> (() => app.Run (new Window ()));
Assert.Equal ("App not Initialized",ex.Message);
}
/* TODO : Infinite loops
[Fact]
public void Test_InitRunShutdown ()
{
var v2 = NewApplicationV2();
v2.Init ();
v2.AddTimeout (TimeSpan.FromMilliseconds (150),
() =>
{
if (Application.Top != null)
{
Application.RequestStop ();
return true;
}
return true;
}
);
Assert.Null (Application.Top);
v2.Run (new Window ());
Assert.Null (Application.Top);
v2.Shutdown ();
}*/
}

0 comments on commit 8216597

Please sign in to comment.