Skip to content

Latest commit

 

History

History
131 lines (92 loc) · 3.73 KB

LogSinkMemory.md

File metadata and controls

131 lines (92 loc) · 3.73 KB

LogSinkMemory

In-memory ring buffer sink for testing. Captures formatted log lines so tests can assert on log output without any I/O. When the buffer fills, the oldest entry is overwritten.

Why use a memory sink?

Unit tests that verify logging behavior need a way to capture output and inspect it. Writing to a file and reading it back is fragile and slow. The memory sink stores up to 64 formatted lines in a fixed-size ring buffer that you can query directly -- check how many lines were captured, retrieve specific lines by index, or search for a substring across all stored lines.

Types

MemorySink

TYPE MemorySink = RECORD
  lines:     ARRAY [0..MaxLines-1] OF StoredLine;
  count:     INTEGER;
  nextSlot:  INTEGER;
  totalSeen: INTEGER;
END;

Declare this as a module-level variable so it outlives the Sink handle. The ring buffer holds up to 64 lines of up to 512 characters each.

Constant Value Purpose
MaxLines 64 Maximum lines stored before wrapping.
LineLen 512 Maximum characters per stored line.

Procedures

Create

PROCEDURE Create(VAR mem: MemorySink; VAR out: Sink);

Initialize a MemorySink and return a Sink handle suitable for passing to Log.AddSink. The mem variable must outlive the sink -- typically a module-level variable or a variable in an outer scope that persists for the test's duration.

VAR mem: MemorySink; ms: Sink; lg: Logger;
Create(mem, ms);
Init(lg);
IF AddSink(lg, ms) THEN END;

GetCount

PROCEDURE GetCount(VAR mem: MemorySink): INTEGER;

Number of lines currently stored (0 to MaxLines). After the ring buffer wraps, this stays at MaxLines -- older entries are silently overwritten.

GetTotal

PROCEDURE GetTotal(VAR mem: MemorySink): INTEGER;

Total number of log calls received since creation or the last Clear. May exceed MaxLines if the buffer has wrapped. Useful for asserting that the expected number of log calls occurred, even if the buffer can't hold them all.

GetLine

PROCEDURE GetLine(VAR mem: MemorySink; index: INTEGER;
                  VAR buf: ARRAY OF CHAR): BOOLEAN;

Copy the stored line at index into buf. Index 0 is the oldest stored line, index GetCount(mem) - 1 is the newest. Returns FALSE if index is out of range.

Clear

PROCEDURE Clear(VAR mem: MemorySink);

Reset all counters and discard stored lines. Useful between test cases when reusing the same sink.

Contains

PROCEDURE Contains(VAR mem: MemorySink; sub: ARRAY OF CHAR): BOOLEAN;

Search all stored lines for the substring sub. Returns TRUE if any line contains it. This is the workhorse for test assertions -- rather than extracting a full line and doing string comparison, you can just check that the expected content appeared somewhere in the log output.

Info(lg, "connection established");
IF NOT Contains(mem, "established") THEN
  WriteString("FAIL: expected log message"); WriteLn
END;

Example

MODULE LogTest;

FROM InOut IMPORT WriteString, WriteLn;
FROM Log IMPORT Logger, Init, AddSink, Sink, Info, Warn;
FROM LogSinkMemory IMPORT MemorySink, Create, GetCount, GetLine, Contains, Clear;

VAR
  lg: Logger;
  ms: Sink;
  mem: MemorySink;
  line: ARRAY [0..511] OF CHAR;

BEGIN
  Init(lg);
  Create(mem, ms);
  IF AddSink(lg, ms) THEN END;

  Info(lg, "first");
  Warn(lg, "second");

  IF GetCount(mem) = 2 THEN
    WriteString("PASS: 2 lines captured"); WriteLn
  END;

  IF Contains(mem, "first") THEN
    WriteString("PASS: found 'first'"); WriteLn
  END;

  GetLine(mem, 1, line);
  (* line contains: WARN msg="second" *)

  Clear(mem);
  (* GetCount(mem) = 0, ready for next test *)
END LogTest.