From 9ec2c8b6eb2d5bb6efc19fd2bbfeba6c29403d7e Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Mon, 19 Jan 2026 20:32:56 +0000 Subject: [PATCH] fix(logging): handle mid-line streaming output in test logger and add streaming tests --- changelog.md | 8 ++++++++ test/tstest/test.gap-debug.ts | 12 ++++++++++++ test/tstest/test.gap-debug2.ts | 14 ++++++++++++++ ts/00_commitinfo_data.ts | 2 +- ts/tstest.logging.ts | 34 ++++++++++++++++++++++++++-------- 5 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 test/tstest/test.gap-debug.ts create mode 100644 test/tstest/test.gap-debug2.ts diff --git a/changelog.md b/changelog.md index b5750a5..9374aed 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2026-01-19 - 3.1.6 - fix(logging) +handle mid-line streaming output in test logger and add streaming tests + +- Introduce isOutputMidLine flag to track when streaming output does not end with a newline +- Only prepend the visual prefix at the start of a line and append segments to the last buffered entry when mid-line +- Write consistent output to log files for both complete lines and raw streaming segments +- Add tests to exercise streaming behavior: test/tstest/test.gap-debug.ts and test/tstest/test.gap-debug2.ts + ## 2026-01-19 - 3.1.5 - fix(tstest) preserve streaming console output and correctly buffer incomplete TAP lines diff --git a/test/tstest/test.gap-debug.ts b/test/tstest/test.gap-debug.ts new file mode 100644 index 0000000..28d9ce4 --- /dev/null +++ b/test/tstest/test.gap-debug.ts @@ -0,0 +1,12 @@ +import { tap, expect } from '../../ts_tapbundle/index.js'; + +tap.test('check for gaps in streaming', async () => { + // This should print "ABCD" with no gaps + process.stdout.write("A"); + process.stdout.write("B"); + process.stdout.write("C"); + process.stdout.write("D\n"); + expect(true).toEqual(true); +}); + +export default tap.start(); diff --git a/test/tstest/test.gap-debug2.ts b/test/tstest/test.gap-debug2.ts new file mode 100644 index 0000000..b1eb719 --- /dev/null +++ b/test/tstest/test.gap-debug2.ts @@ -0,0 +1,14 @@ +import { tap, expect } from '../../ts_tapbundle/index.js'; + +tap.test('streaming with delays', async (tools) => { + // Simulate real streaming with delays + process.stdout.write("Progress: ["); + for (let i = 0; i < 10; i++) { + await tools.delayFor(50); + process.stdout.write("="); + } + process.stdout.write("]\n"); + expect(true).toEqual(true); +}); + +export default tap.start(); diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index f3b9cc6..386b426 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@git.zone/tstest', - version: '3.1.5', + version: '3.1.6', description: 'a test utility to run tests that match test/**/*.ts' } diff --git a/ts/tstest.logging.ts b/ts/tstest.logging.ts index a9cfd13..875a843 100644 --- a/ts/tstest.logging.ts +++ b/ts/tstest.logging.ts @@ -44,6 +44,7 @@ export class TsTestLogger { private currentTestLogFile: string | null = null; private currentTestLogs: string[] = []; // Buffer for current test logs private currentTestFailed: boolean = false; + private isOutputMidLine: boolean = false; // Track whether we're mid-line for streaming output constructor(options: LogOptions = {}) { this.options = options; @@ -189,6 +190,7 @@ export class TsTestLogger { // Reset test-specific state this.currentTestLogs = []; this.currentTestFailed = false; + this.isOutputMidLine = false; // Only set up test log file if --logfile option is specified if (this.options.logFile) { @@ -352,17 +354,28 @@ export class TsTestLogger { testConsoleOutput(message: string) { if (this.options.json) return; + const prefix = ' '; // In verbose mode, show console output immediately if (this.options.verbose) { - this.log(this.format(` ${message}`, 'dim')); + // Only add prefix if we're starting a new line + const output = this.isOutputMidLine ? message : prefix + message; + this.log(this.format(output, 'dim')); } else { // In non-verbose mode, buffer the logs - this.currentTestLogs.push(message); + if (this.isOutputMidLine && this.currentTestLogs.length > 0) { + // Append to the last buffered entry since we're mid-line + this.currentTestLogs[this.currentTestLogs.length - 1] += message; + } else { + this.currentTestLogs.push(message); + } } + // Reset mid-line state since we just output a complete line + this.isOutputMidLine = false; + // Always log to test file if --logfile is specified if (this.currentTestLogFile) { - this.logToTestFile(` ${message}`); + this.logToTestFile(`${prefix}${message}`); } } @@ -371,13 +384,15 @@ export class TsTestLogger { if (this.options.json) return; const prefix = ' '; + // Only add prefix if we're starting a new line (not mid-line) + const output = this.isOutputMidLine ? message : prefix + message; + if (this.options.verbose) { // Use process.stdout.write to preserve streaming without adding newlines - process.stdout.write(this.format(prefix + message, 'dim')); + process.stdout.write(this.format(output, 'dim')); } else { - // Buffer without trailing newline - append to last entry if exists and incomplete - if (this.currentTestLogs.length > 0) { - // Append to the last buffered entry (for streaming segments) + // Buffer mode: append to last entry if mid-line + if (this.isOutputMidLine && this.currentTestLogs.length > 0) { this.currentTestLogs[this.currentTestLogs.length - 1] += message; } else { this.currentTestLogs.push(message); @@ -386,8 +401,11 @@ export class TsTestLogger { // Log to test file without adding newline if (this.currentTestLogFile) { - this.logToTestFileRaw(prefix + message); + this.logToTestFileRaw(output); } + + // We're now mid-line (no newline was written) + this.isOutputMidLine = true; } private logToTestFileRaw(message: string) {