Skip to content

Commit

Permalink
Output log messages on failure
Browse files Browse the repository at this point in the history
  • Loading branch information
thoukydides authored Sep 17, 2024
1 parent f2144a8 commit 6b01f32
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 23 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ jobs:

strategy:
matrix:
# the Node.js versions to build on
node-version: [18.x, 20.x, 22.x]
homebridge-version: ['^1.8.0', '^2.0.0-beta.0']

Expand All @@ -29,5 +28,5 @@ jobs:
- name: Build the project
run: npm run build

- name: Tune the tests
- name: Run the tests
run: npm run test
66 changes: 45 additions & 21 deletions bin/test-startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { spawn } from 'child_process';
import assert from 'node:assert';
import { setTimeout as setTimeoutP } from 'node:timers/promises';

// Command to use to launch Homebridge
const SPAWN_COMMAND = 'homebridge';
Expand All @@ -14,32 +15,55 @@ const SUCCESS_OUTPUT_REGEX = /\[Homebridge AEG Robot Vacuum\] (Starting new auth
// Length of time to wait for the message
const TIMEOUT_MS = 15 * 1000; // (15 seconds)

// Collect stdout and stderr, checking for success message(s)
let homebridgeOutput = '';
async function checkHomebridgeOutput(name: string, stream: NodeJS.ReadableStream): Promise<void> {
stream.setEncoding('utf8');
for await (const chunk of stream) {
assert(typeof chunk === 'string');
homebridgeOutput += chunk.toString();

// Check for the expected log messages
if (SUCCESS_OUTPUT_REGEX.test(homebridgeOutput)) return;
}

// Stream should only terminate if the process is killed
throw new Error(`Unexpected ${name} termination`);
};

// Timeout
async function timeout(ms: number): Promise<never> {
await setTimeoutP(ms);
throw new Error('Timeout waiting for expected output');
};

// Run the test
void (async (): Promise<void> => {
// Attempt to launch Homebridge
const homebridge = spawn(SPAWN_COMMAND, SPAWN_ARGS, { stdio: 'pipe', timeout: TIMEOUT_MS });
const homebridge = spawn(SPAWN_COMMAND, SPAWN_ARGS, { stdio: 'pipe' });
try {
// Collect stdout and stderr, checking for success message(s)
await Promise.race([
checkHomebridgeOutput('stdout', homebridge.stdout),
checkHomebridgeOutput('stderr', homebridge.stderr),
timeout(TIMEOUT_MS)
]);

// Log any error output
//homebridge.stderr.on('data', (data) => { console.error(`stderr: ${data.trim()}`); });
// The expected log messages were seen
console.log('Test successful');

// Collect stdout and check for success message(s)
let output = '';
homebridge.stdout.setEncoding('utf8');
for await (const chunk of homebridge.stdout) {
assert(typeof chunk === 'string');
output += chunk.toString();
if (SUCCESS_OUTPUT_REGEX.test(output)) {
// Test completed successfully
homebridge.kill('SIGTERM');
console.log('Success');
return;
}
}
} catch (err) {

// Test finished without seeing the expected log messages
const errs = err instanceof AggregateError ? err.errors : [err];
const messages = errs.map(e => e instanceof Error ? e.message : String(e));
if (homebridge.exitCode !== null) messages.unshift(`Homebridge exited with code ${homebridge.exitCode}`);
console.error('Test failed:\n' + messages.map(m => ` ${m}\n`).join(''));
console.log(homebridgeOutput);
process.exitCode = 1;

// Homebridge did not start successfully
switch (homebridge.exitCode) {
case null: throw new Error('Unexpected stdout termination');
case 0: throw new Error('Unexpected successful process termination');
default: throw new Error(`Homebridge exited with code ${homebridge.exitCode}`);
} finally {
// Terminate the homebridge process at the end of the test
homebridge.kill('SIGTERM');
}
})();

0 comments on commit 6b01f32

Please sign in to comment.