PlasmaCommands.GraphicsWrite/GraphicsRow port the display firmware graphics command (ESC P s y x w h, MSB-left); PlasmaDisplay.RowAsync writes a locked whole-row update; the line protocol gains `plasma row <y> <hex32>`. The router plasma slot becomes a bounded FIFO queue: rows stream in order (a frame must not tear), texts still coalesce to the newest, clear flushes, cap 128 with counted drops. Docs updated; 442 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
93 lines
2.9 KiB
C#
93 lines
2.9 KiB
C#
using RioJoy.Core.Plasma;
|
|
using RioJoy.Core.Tests.Serial;
|
|
using Xunit;
|
|
|
|
namespace RioJoy.Core.Tests.Plasma;
|
|
|
|
public class PlasmaDisplayTests
|
|
{
|
|
private static byte[][] PosTextChunks(string text, byte x = 0, byte y = 0, byte attr = 0, byte font = 0)
|
|
{
|
|
(byte rx, byte ry, byte rfont, int len) = PlasmaCommands.ResolvePosText(text, x, y, font);
|
|
return new[]
|
|
{
|
|
PlasmaCommands.CursorX(rx),
|
|
PlasmaCommands.CursorY(ry),
|
|
PlasmaCommands.FontAttr(attr),
|
|
PlasmaCommands.Font(rfont),
|
|
PlasmaCommands.Text(text[..len]),
|
|
};
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PosTextAsync_EmitsThePosTextSequenceInOrder()
|
|
{
|
|
var transport = new FakeTransport();
|
|
var display = new PlasmaDisplay(transport);
|
|
|
|
await display.PosTextAsync("VIPER 1-1").WithTimeout();
|
|
|
|
foreach (byte[] expected in PosTextChunks("VIPER 1-1"))
|
|
Assert.Equal(expected, await transport.NextWriteAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PosTextAsync_EmptyText_WritesNothing()
|
|
{
|
|
var transport = new FakeTransport();
|
|
var display = new PlasmaDisplay(transport);
|
|
|
|
await display.PosTextAsync("").WithTimeout();
|
|
|
|
Assert.False(transport.Writes.TryRead(out _));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ClearAsync_WritesTheClearCommand()
|
|
{
|
|
var transport = new FakeTransport();
|
|
var display = new PlasmaDisplay(transport);
|
|
|
|
await display.ClearAsync().WithTimeout();
|
|
|
|
Assert.Equal(PlasmaCommands.Clear(), await transport.NextWriteAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RowAsync_WritesOneGraphicsRowCommand()
|
|
{
|
|
var transport = new FakeTransport();
|
|
var display = new PlasmaDisplay(transport);
|
|
var row = new byte[16];
|
|
row[3] = 0xF0;
|
|
|
|
await display.RowAsync(12, row).WithTimeout();
|
|
|
|
Assert.Equal(PlasmaCommands.GraphicsRow(12, row), await transport.NextWriteAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PosTextAsync_ConcurrentCalls_DoNotInterleave()
|
|
{
|
|
// Without the write lock, B's cursor/font fragments land between A's five
|
|
// writes and corrupt the ESC stream. Gate A's first write so B has every
|
|
// chance to sneak in, then assert the ten writes arrive as A's five
|
|
// followed by B's five.
|
|
var transport = new GatedTransport();
|
|
var display = new PlasmaDisplay(transport);
|
|
|
|
Task a = display.PosTextAsync("AAAA");
|
|
Task b = display.PosTextAsync("BBBB");
|
|
transport.Open();
|
|
await Task.WhenAll(a, b).WithTimeout();
|
|
|
|
var writes = new List<byte[]>();
|
|
for (int i = 0; i < 10; i++)
|
|
writes.Add(await transport.NextWriteAsync());
|
|
|
|
byte[][] expected = PosTextChunks("AAAA").Concat(PosTextChunks("BBBB")).ToArray();
|
|
for (int i = 0; i < expected.Length; i++)
|
|
Assert.Equal(expected[i], writes[i]);
|
|
}
|
|
}
|