mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-27 00:07:58 +01:00
233 lines
7.9 KiB
C#
233 lines
7.9 KiB
C#
using Moq;
|
|
|
|
namespace UnitTests_Parallelizable.DriverTests;
|
|
|
|
public class AnsiRequestSchedulerTests
|
|
{
|
|
private readonly Mock<IAnsiResponseParser> _parserMock;
|
|
private readonly AnsiRequestScheduler _scheduler;
|
|
|
|
private static DateTime _staticNow; // Static value to hold the current time
|
|
|
|
public AnsiRequestSchedulerTests ()
|
|
{
|
|
_parserMock = new (MockBehavior.Strict);
|
|
_staticNow = DateTime.UtcNow; // Initialize static time
|
|
_scheduler = new (_parserMock.Object, () => _staticNow);
|
|
}
|
|
|
|
[Fact]
|
|
public void SendOrSchedule_SendsDeviceAttributeRequest_WhenNoOutstandingRequests ()
|
|
{
|
|
// Arrange
|
|
var request = new AnsiEscapeSequenceRequest
|
|
{
|
|
Request = "\u001b[0c", // ESC [ c
|
|
Terminator = "c",
|
|
ResponseReceived = r => { }
|
|
};
|
|
|
|
// we have no outstanding for c already
|
|
_parserMock.Setup (p => p.IsExpecting ("c")).Returns (false).Verifiable (Times.Once);
|
|
|
|
// then we should execute our request
|
|
_parserMock.Setup (p => p.ExpectResponse ("c", It.IsAny<Action<string>> (), null, false)).Verifiable (Times.Once);
|
|
|
|
// Act
|
|
bool result = _scheduler.SendOrSchedule (request);
|
|
|
|
// Assert
|
|
Assert.Empty (_scheduler.QueuedRequests); // We sent it i.e. we did not queue it for later
|
|
Assert.True (result); // Should send immediately
|
|
_parserMock.Verify ();
|
|
}
|
|
|
|
[Fact]
|
|
public void SendOrSchedule_QueuesRequest_WhenOutstandingRequestExists ()
|
|
{
|
|
// Arrange
|
|
var request1 = new AnsiEscapeSequenceRequest
|
|
{
|
|
Request = "\u001b[0c", // ESC [ 0 c
|
|
Terminator = "c",
|
|
ResponseReceived = r => { }
|
|
};
|
|
|
|
// Parser already has an ongoing request for "c"
|
|
_parserMock.Setup (p => p.IsExpecting ("c")).Returns (true).Verifiable (Times.Once);
|
|
|
|
// Act
|
|
bool result = _scheduler.SendOrSchedule (request1);
|
|
|
|
// Assert
|
|
Assert.Single (_scheduler.QueuedRequests); // Ensure only one request is in the queue
|
|
Assert.False (result); // Should be queued
|
|
_parserMock.Verify ();
|
|
}
|
|
|
|
[Fact]
|
|
public void RunSchedule_ThrottleNotExceeded_AllowSend ()
|
|
{
|
|
// Arrange
|
|
var request = new AnsiEscapeSequenceRequest
|
|
{
|
|
Request = "\u001b[0c", // ESC [ 0 c
|
|
Terminator = "c",
|
|
ResponseReceived = r => { }
|
|
};
|
|
|
|
// Set up to expect no outstanding request for "c" i.e. parser instantly gets response and resolves it
|
|
_parserMock.Setup (p => p.IsExpecting ("c")).Returns (false).Verifiable (Times.Exactly (2));
|
|
_parserMock.Setup (p => p.ExpectResponse ("c", It.IsAny<Action<string>> (), null, false)).Verifiable (Times.Exactly (2));
|
|
|
|
_scheduler.SendOrSchedule (request);
|
|
|
|
// Simulate time passing beyond throttle
|
|
SetTime (101); // Exceed throttle limit
|
|
|
|
// Act
|
|
|
|
// Send another request after the throttled time limit
|
|
bool result = _scheduler.SendOrSchedule (request);
|
|
|
|
// Assert
|
|
Assert.Empty (_scheduler.QueuedRequests); // Should send and clear the request
|
|
Assert.True (result); // Should have found and sent the request
|
|
_parserMock.Verify ();
|
|
}
|
|
|
|
[Fact]
|
|
public void RunSchedule_ThrottleExceeded_QueueRequest ()
|
|
{
|
|
// Arrange
|
|
var request = new AnsiEscapeSequenceRequest
|
|
{
|
|
Request = "\u001b[0c", // ESC [ 0 c
|
|
Terminator = "c",
|
|
ResponseReceived = r => { }
|
|
};
|
|
|
|
// Set up to expect no outstanding request for "c" i.e. parser instantly gets response and resolves it
|
|
_parserMock.Setup (p => p.IsExpecting ("c")).Returns (false).Verifiable (Times.Exactly (2));
|
|
_parserMock.Setup (p => p.ExpectResponse ("c", It.IsAny<Action<string>> (), null, false)).Verifiable (Times.Exactly (2));
|
|
|
|
_scheduler.SendOrSchedule (request);
|
|
|
|
// Simulate time passing
|
|
SetTime (55); // Does not exceed throttle limit
|
|
|
|
// Act
|
|
|
|
// Send another request after the throttled time limit
|
|
bool result = _scheduler.SendOrSchedule (request);
|
|
|
|
// Assert
|
|
Assert.Single (_scheduler.QueuedRequests); // Should have been queued
|
|
Assert.False (result); // Should have been queued
|
|
|
|
// Throttle still not exceeded
|
|
Assert.False (_scheduler.RunSchedule ());
|
|
|
|
SetTime (90);
|
|
|
|
// Throttle still not exceeded
|
|
Assert.False (_scheduler.RunSchedule ());
|
|
|
|
SetTime (105);
|
|
|
|
// Throttle exceeded - so send the request
|
|
Assert.True (_scheduler.RunSchedule ());
|
|
|
|
_parserMock.Verify ();
|
|
}
|
|
|
|
[Fact]
|
|
public void EvictStaleRequests_RemovesStaleRequest_AfterTimeout ()
|
|
{
|
|
// Arrange
|
|
var request1 = new AnsiEscapeSequenceRequest
|
|
{
|
|
Request = "\u001b[0c",
|
|
Terminator = "c",
|
|
ResponseReceived = r => { }
|
|
};
|
|
|
|
// Send
|
|
_parserMock.Setup (p => p.IsExpecting ("c")).Returns (false).Verifiable (Times.Once);
|
|
_parserMock.Setup (p => p.ExpectResponse ("c", It.IsAny<Action<string>> (), null, false)).Verifiable (Times.Exactly (2));
|
|
|
|
Assert.True (_scheduler.SendOrSchedule (request1));
|
|
|
|
// Parser already has an ongoing request for "c"
|
|
_parserMock.Setup (p => p.IsExpecting ("c")).Returns (true).Verifiable (Times.Exactly (2));
|
|
|
|
// Cannot send because there is already outstanding request
|
|
Assert.False (_scheduler.SendOrSchedule (request1));
|
|
Assert.Single (_scheduler.QueuedRequests);
|
|
|
|
// Simulate request going stale
|
|
SetTime (5001); // Exceeds stale timeout
|
|
|
|
// Parser should be told to give up on this one (evicted)
|
|
_parserMock.Setup (p => p.StopExpecting ("c", false))
|
|
.Callback (() =>
|
|
{
|
|
// When we tell parser to evict - it should now tell us it is no longer expecting
|
|
_parserMock.Setup (p => p.IsExpecting ("c")).Returns (false).Verifiable (Times.Once);
|
|
})
|
|
.Verifiable ();
|
|
|
|
// When we send again the evicted one should be
|
|
bool evicted = _scheduler.RunSchedule ();
|
|
|
|
Assert.True (evicted); // Stale request should be evicted
|
|
Assert.Empty (_scheduler.QueuedRequests);
|
|
|
|
// Assert
|
|
_parserMock.Verify ();
|
|
}
|
|
|
|
[Fact]
|
|
public void RunSchedule_DoesNothing_WhenQueueIsEmpty ()
|
|
{
|
|
// Act
|
|
bool result = _scheduler.RunSchedule ();
|
|
|
|
// Assert
|
|
Assert.False (result); // No requests to process
|
|
Assert.Empty (_scheduler.QueuedRequests);
|
|
}
|
|
|
|
[Fact]
|
|
public void SendOrSchedule_ManagesIndependentTerminatorsCorrectly ()
|
|
{
|
|
// Arrange
|
|
var request1 = new AnsiEscapeSequenceRequest { Request = "\u001b[0c", Terminator = "c", ResponseReceived = r => { } };
|
|
var request2 = new AnsiEscapeSequenceRequest { Request = "\u001b[0x", Terminator = "x", ResponseReceived = r => { } };
|
|
|
|
// Already have a 'c' ongoing
|
|
_parserMock.Setup (p => p.IsExpecting ("c")).Returns (true).Verifiable (Times.Once);
|
|
|
|
// 'x' is free
|
|
_parserMock.Setup (p => p.IsExpecting ("x")).Returns (false).Verifiable (Times.Once);
|
|
_parserMock.Setup (p => p.ExpectResponse ("x", It.IsAny<Action<string>> (), null, false)).Verifiable (Times.Once);
|
|
|
|
// Act
|
|
bool a = _scheduler.SendOrSchedule (request1);
|
|
bool b = _scheduler.SendOrSchedule (request2);
|
|
|
|
// Assert
|
|
Assert.False (a);
|
|
Assert.True (b);
|
|
Assert.Equal (request1, Assert.Single (_scheduler.QueuedRequests));
|
|
_parserMock.Verify ();
|
|
}
|
|
|
|
private void SetTime (int milliseconds)
|
|
{
|
|
// This simulates the passing of time by setting the Now function to return a specific time.
|
|
DateTime newNow = _staticNow.AddMilliseconds (milliseconds);
|
|
_scheduler.Now = () => newNow;
|
|
}
|
|
}
|