Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions Sharprompt.Tests/ConsoleDriverFactoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System;

using Sharprompt.Drivers;

using Xunit;

namespace Sharprompt.Tests;

public class ConsoleDriverFactoryTests
{
[Fact]
public void ConsoleDriverFactory_DefaultFactory_IsNotNull()
{
Assert.NotNull(Prompt.ConsoleDriverFactory);
}

[Fact]
public void ConsoleDriverFactory_SetCustomFactory_ReturnsCustomFactory()
{
var originalFactory = Prompt.ConsoleDriverFactory;

try
{
var customDriver = new StubConsoleDriver();
Prompt.ConsoleDriverFactory = () => customDriver;

var driver = Prompt.ConsoleDriverFactory();

Assert.Same(customDriver, driver);
}
finally
{
Prompt.ConsoleDriverFactory = originalFactory;
}
}

[Fact]
public void ConsoleDriverFactory_SetNull_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() => Prompt.ConsoleDriverFactory = null!);
}

[Fact]
public void ConsoleDriverFactory_AfterReset_ReturnsPreviousFactory()
{
var originalFactory = Prompt.ConsoleDriverFactory;

try
{
Prompt.ConsoleDriverFactory = () => new StubConsoleDriver();

Prompt.ConsoleDriverFactory = originalFactory;

Assert.Same(originalFactory, Prompt.ConsoleDriverFactory);
}
finally
{
Prompt.ConsoleDriverFactory = originalFactory;
}
}

[Fact]
public void IConsoleDriver_CustomImplementation_CanBeAssigned()
{
var originalFactory = Prompt.ConsoleDriverFactory;

try
{
IConsoleDriver driver = new StubConsoleDriver();
Prompt.ConsoleDriverFactory = () => driver;

var result = Prompt.ConsoleDriverFactory();

Assert.IsAssignableFrom<IConsoleDriver>(result);
Assert.Same(driver, result);
}
finally
{
Prompt.ConsoleDriverFactory = originalFactory;
}
}

private sealed class StubConsoleDriver : IConsoleDriver
{
public void Dispose() { }
public void Beep() { }
public void Reset() { }
public void ClearLine(int top) { }
public ConsoleKeyInfo ReadKey() => new('\0', ConsoleKey.Enter, false, false, false);
public void Write(string value, ConsoleColor color) { }
public void WriteLine() { }
public void SetCursorPosition(int left, int top) { }
public bool KeyAvailable => false;
public bool CursorVisible { set { } }
public int CursorLeft => 0;
public int CursorTop => 0;
public int BufferWidth => 80;
public int BufferHeight => 24;
public int WindowWidth => 80;
public int WindowHeight => 24;
public Action CancellationCallback { get; set; } = () => { };
}
}
226 changes: 226 additions & 0 deletions Sharprompt.Tests/IConsoleDriverTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
using System;

using Sharprompt.Drivers;

using Xunit;

namespace Sharprompt.Tests;

public class IConsoleDriverTests
{
[Fact]
public void Beep_CanBeCalled()
{
var driver = new RecordingConsoleDriver();

((IConsoleDriver)driver).Beep();

Assert.True(driver.BeepCalled);
}

[Fact]
public void Reset_CanBeCalled()
{
var driver = new RecordingConsoleDriver();

((IConsoleDriver)driver).Reset();

Assert.True(driver.ResetCalled);
}

[Fact]
public void ClearLine_PassesCorrectArgument()
{
var driver = new RecordingConsoleDriver();

((IConsoleDriver)driver).ClearLine(5);

Assert.Equal(5, driver.ClearLineTop);
}

[Fact]
public void ReadKey_ReturnsConfiguredKeyInfo()
{
var expected = new ConsoleKeyInfo('A', ConsoleKey.A, false, false, false);
var driver = new RecordingConsoleDriver { ReadKeyResult = expected };

var result = ((IConsoleDriver)driver).ReadKey();

Assert.Equal(expected, result);
}

[Fact]
public void Write_PassesCorrectArguments()
{
var driver = new RecordingConsoleDriver();

((IConsoleDriver)driver).Write("hello", ConsoleColor.Cyan);

Assert.Equal("hello", driver.LastWriteValue);
Assert.Equal(ConsoleColor.Cyan, driver.LastWriteColor);
}

[Fact]
public void WriteLine_CanBeCalled()
{
var driver = new RecordingConsoleDriver();

((IConsoleDriver)driver).WriteLine();

Assert.True(driver.WriteLineCalled);
}

[Fact]
public void SetCursorPosition_PassesCorrectCoordinates()
{
var driver = new RecordingConsoleDriver();

((IConsoleDriver)driver).SetCursorPosition(10, 20);

Assert.Equal(10, driver.SetCursorLeft);
Assert.Equal(20, driver.SetCursorTop);
}

[Fact]
public void KeyAvailable_ReturnsConfiguredValue()
{
var driver = new RecordingConsoleDriver { KeyAvailableValue = true };

var result = ((IConsoleDriver)driver).KeyAvailable;

Assert.True(result);
}

[Fact]
public void CursorVisible_CanBeSet()
{
var driver = new RecordingConsoleDriver();

((IConsoleDriver)driver).CursorVisible = false;

Assert.False(driver.CursorVisibleValue);
}

[Fact]
public void CursorLeft_ReturnsConfiguredValue()
{
var driver = new RecordingConsoleDriver { CursorLeftValue = 42 };

var result = ((IConsoleDriver)driver).CursorLeft;

Assert.Equal(42, result);
}

[Fact]
public void CursorTop_ReturnsConfiguredValue()
{
var driver = new RecordingConsoleDriver { CursorTopValue = 7 };

var result = ((IConsoleDriver)driver).CursorTop;

Assert.Equal(7, result);
}

[Fact]
public void BufferWidth_ReturnsConfiguredValue()
{
var driver = new RecordingConsoleDriver { BufferWidthValue = 120 };

var result = ((IConsoleDriver)driver).BufferWidth;

Assert.Equal(120, result);
}

[Fact]
public void BufferHeight_ReturnsConfiguredValue()
{
var driver = new RecordingConsoleDriver { BufferHeightValue = 50 };

var result = ((IConsoleDriver)driver).BufferHeight;

Assert.Equal(50, result);
}

[Fact]
public void WindowWidth_ReturnsConfiguredValue()
{
var driver = new RecordingConsoleDriver { WindowWidthValue = 200 };

var result = ((IConsoleDriver)driver).WindowWidth;

Assert.Equal(200, result);
}

[Fact]
public void WindowHeight_ReturnsConfiguredValue()
{
var driver = new RecordingConsoleDriver { WindowHeightValue = 60 };

var result = ((IConsoleDriver)driver).WindowHeight;

Assert.Equal(60, result);
}

[Fact]
public void CancellationCallback_CanBeSetAndInvoked()
{
var driver = new RecordingConsoleDriver();
var invoked = false;
Action callback = () => invoked = true;

((IConsoleDriver)driver).CancellationCallback = callback;
((IConsoleDriver)driver).CancellationCallback.Invoke();

Assert.True(invoked);
}

[Fact]
public void Dispose_CanBeCalled()
{
var driver = new RecordingConsoleDriver();

((IDisposable)driver).Dispose();

Assert.True(driver.DisposeCalled);
}

private sealed class RecordingConsoleDriver : IConsoleDriver
{
public bool BeepCalled { get; private set; }
public bool ResetCalled { get; private set; }
public int ClearLineTop { get; private set; } = -1;
public ConsoleKeyInfo ReadKeyResult { get; set; } = new('\0', ConsoleKey.Enter, false, false, false);
public string? LastWriteValue { get; private set; }
public ConsoleColor LastWriteColor { get; private set; }
public bool WriteLineCalled { get; private set; }
public int SetCursorLeft { get; private set; } = -1;
public int SetCursorTop { get; private set; } = -1;
public bool KeyAvailableValue { get; set; }
public bool? CursorVisibleValue { get; private set; }
public int CursorLeftValue { get; set; }
public int CursorTopValue { get; set; }
public int BufferWidthValue { get; set; } = 80;
public int BufferHeightValue { get; set; } = 24;
public int WindowWidthValue { get; set; } = 80;
public int WindowHeightValue { get; set; } = 24;
public bool DisposeCalled { get; private set; }

public void Dispose() => DisposeCalled = true;
public void Beep() => BeepCalled = true;
public void Reset() => ResetCalled = true;
public void ClearLine(int top) => ClearLineTop = top;
public ConsoleKeyInfo ReadKey() => ReadKeyResult;
public void Write(string value, ConsoleColor color) { LastWriteValue = value; LastWriteColor = color; }
public void WriteLine() => WriteLineCalled = true;
public void SetCursorPosition(int left, int top) { SetCursorLeft = left; SetCursorTop = top; }
public bool KeyAvailable => KeyAvailableValue;
public bool CursorVisible { set => CursorVisibleValue = value; }
public int CursorLeft => CursorLeftValue;
public int CursorTop => CursorTopValue;
public int BufferWidth => BufferWidthValue;
public int BufferHeight => BufferHeightValue;
public int WindowWidth => WindowWidthValue;
public int WindowHeight => WindowHeightValue;
public Action CancellationCallback { get; set; } = () => { };
}
}
2 changes: 1 addition & 1 deletion Sharprompt/Drivers/DefaultConsoleDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Sharprompt.Drivers;

internal sealed class DefaultConsoleDriver : IConsoleDriver
public sealed class DefaultConsoleDriver : IConsoleDriver
{
static DefaultConsoleDriver()
{
Expand Down
2 changes: 1 addition & 1 deletion Sharprompt/Drivers/IConsoleDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Sharprompt.Drivers;

internal interface IConsoleDriver : IDisposable
public interface IConsoleDriver : IDisposable
{
void Beep();
void Reset();
Expand Down
7 changes: 3 additions & 4 deletions Sharprompt/Forms/FormBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ internal abstract class FormBase<T> : IDisposable
{
protected FormBase()
{
_consoleDriver = new DefaultConsoleDriver
{
CancellationCallback = CancellationHandler
};
_consoleDriver = Prompt.ConsoleDriverFactory() ?? throw new InvalidOperationException("ConsoleDriverFactory must return a non-null IConsoleDriver instance.");

_consoleDriver.CancellationCallback = CancellationHandler;

_formRenderer = new FormRenderer(_consoleDriver);
}
Expand Down
13 changes: 13 additions & 0 deletions Sharprompt/Prompt.Configuration.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Globalization;

using Sharprompt.Drivers;
using Sharprompt.Strings;

namespace Sharprompt;
Expand All @@ -9,6 +10,18 @@ public static partial class Prompt
{
public static bool ThrowExceptionOnCancel { get; set; } = false;

private static Func<IConsoleDriver> s_consoleDriverFactory = () => new DefaultConsoleDriver();

public static Func<IConsoleDriver> ConsoleDriverFactory
{
get => s_consoleDriverFactory;
set
{
ArgumentNullException.ThrowIfNull(value);
s_consoleDriverFactory = value;
}
}

public static CultureInfo Culture
{
get => Resource.Culture;
Expand Down
Loading