Files
Print_server/Inspectron.Epson.Tests/RecordingEpsonPrinter.cs
EugeneTes a4e96f3041 Fix three pre-merge review findings on POS895 branch
1. Strengthen POS895 IsCut test: add RecordingEpsonPrinter.CallOrder so
   the test can assert feeds happen BEFORE the cut, plus a LineFeedBytes
   count assertion. The prior test could not detect the FeedAsync call
   being deleted from the IsCut branch (verified: test now fails when
   that line is removed, passes when restored).

2. CompositeDiscoveryService: distinguish "some children failed" from
   "every child failed". Per-child failures are still absorbed, but if
   every discovery service throws, the composite now rethrows instead
   of silently returning an empty list. Previously a total discovery
   failure (e.g. a routeless interface) looked like "discovery
   succeeded, found nothing" to PrinterDiscoveryBackgroundTask, which
   would expire and unregister every known printer after the retention
   window, discarding their queued jobs. Also corrected a doc comment
   that overstated Task.WhenAll's timeout guarantee.

3. Update CLAUDE.md to describe the POS895/POS896 work as shipped
   rather than future work: added the Inspectron.Epson.Tests and
   Pos895Test projects, a dotnet test command, SprtDiscoveryService/
   CompositeDiscoveryService/POS895 in the architecture sections, the
   0x20 printer-ID note, and the established SPRT hardware facts.

Claude-Session: https://claude.ai/code/session_01E6N3P4nSZLtp4BC2LXVref
2026-09-03 12:46:14 +02:00

105 lines
4.6 KiB
C#

using Inspectron.Epson;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Inspectron.Epson.Tests;
/// <summary>
/// Records what an IPrinter adapter sends. Only the calls the POS895 adapter
/// makes are recorded; the rest satisfy the interface and do nothing.
/// </summary>
public sealed class RecordingEpsonPrinter : IEpsonPrinter
{
public List<byte[]> RawCommands { get; } = new();
public List<int> FeedLineCalls { get; } = new();
public List<string> Text { get; } = new();
public List<(string Path, EpsonCommands.BitImageMode Mode, int MaxWidth)> BitModeImages { get; } = new();
public List<(string Path, int MaxWidth)> RasterImages { get; } = new();
public List<int> AbsolutePositions { get; } = new();
public int CutCount { get; private set; }
/// <summary>
/// Ordered log of the calls that affect receipt layout, so a test can assert
/// that feeds happen BEFORE a cut rather than merely that both occurred.
/// </summary>
public List<string> CallOrder { get; } = new();
/// <summary>Total LF bytes across every raw command sent.</summary>
public int LineFeedBytes => RawCommands.Sum(c => c.Count(b => b == 0x0A));
public bool IsConnected => true;
public Task SendRawCommandAsync(byte[] command)
{
RawCommands.Add(command);
CallOrder.Add($"raw:{command.Length}");
return Task.CompletedTask;
}
public Task FeedLinesAsync(int lines)
{
FeedLineCalls.Add(lines);
return Task.CompletedTask;
}
public Task PrintTextAsync(string text)
{
Text.Add(text);
return Task.CompletedTask;
}
public Task PrintImageBitModeAsync(string imagePath, EpsonCommands.BitImageMode mode = EpsonCommands.BitImageMode.SingleDensity8Dot, int maxWidth = 10)
{
BitModeImages.Add((imagePath, mode, maxWidth));
return Task.CompletedTask;
}
public Task LoadImageAsync(string imagePath, int maxWidth = 384)
{
RasterImages.Add((imagePath, maxWidth));
return Task.CompletedTask;
}
public Task SetAbsolutePrintPosition(int x)
{
AbsolutePositions.Add(x);
return Task.CompletedTask;
}
public Task CutAsync(bool fullCut = true)
{
CutCount++;
CallOrder.Add("cut");
return Task.CompletedTask;
}
// Remaining interface members: no-ops.
public Task ConnectAsync(string ip, int port = 9100, int timeoutSeconds = 10) => Task.CompletedTask;
public void Disconnect() { }
// 0x16 / 0x12 are what the POS895 actually returns when idle and ready.
public Task<PrinterStatus> GetPrinterStatusAsync() => Task.FromResult(PrinterStatus.Parse(0x16));
public Task<OfflineStatus> GetOfflineStatusAsync() => Task.FromResult(OfflineStatus.Parse(0x12));
public Task<ErrorStatus> GetErrorStatusAsync() => Task.FromResult(ErrorStatus.Parse(0x12));
public Task<PaperSensorStatus> GetPaperSensorStatusAsync() => Task.FromResult(PaperSensorStatus.Parse(0x12));
public Task<byte> GetTM220StatusAsync() => Task.FromResult((byte)0);
public Task<byte?> GetPrinterIdAsync() => Task.FromResult<byte?>(0x20);
public Task<OverallStatus> GetOverallStatusAsync() => Task.FromResult(new OverallStatus());
public Task InitAsync() => Task.CompletedTask;
public Task SetQuadrupleMode(bool enable) => Task.CompletedTask;
public Task SetRedColor(bool enable) => Task.CompletedTask;
public Task SetEmphasized(bool enable) => Task.CompletedTask;
public Task SelectFont(EpsonCommands.PrinterFont font) => Task.CompletedTask;
public Task SetBiggerFontTM220(bool biggerWidth, bool biggerHeight = false, bool secondaryFont = true) => Task.CompletedTask;
public Task SetFontSizeAsync(int width, int height) => Task.CompletedTask;
public Task PrintTextAndCutAsync(string text, int feedLines = 5) => Task.CompletedTask;
public Task PrintBuffer() => Task.CompletedTask;
public Task LoadImageAsync(Stream imageStream, int maxWidth = 384) => Task.CompletedTask;
public Task LoadImageAsync(Image<Rgba32> image, int maxWidth = 384) => Task.CompletedTask;
public Task PrintLoadedImage() => Task.CompletedTask;
public Task PrintImageBitModeAsync(Stream imageStream, EpsonCommands.BitImageMode mode = EpsonCommands.BitImageMode.DoubleDensity24Dot, int maxWidth = 384) => Task.CompletedTask;
public Task PrintImageBitModeAsync(Image<Rgba32> image, EpsonCommands.BitImageMode mode, int maxWidth) => Task.CompletedTask;
public Task SetDefaultLineSpacing() => Task.CompletedTask;
public Task SetCustomLineSpacing(int spacing = 24) => Task.CompletedTask;
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}