Skip to content

Commit 9326e76

Browse files
committed
feat: add --expect-text, --fail-text and --timeout
1 parent 676cc97 commit 9326e76

File tree

3 files changed

+98
-3
lines changed

3 files changed

+98
-3
lines changed

src/ExpectEngine.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { EventEmitter } from 'events';
2+
3+
export class ExpectEngine extends EventEmitter {
4+
readonly expectTexts: string[] = [];
5+
readonly failTexts: string[] = [];
6+
7+
private currentLine = '';
8+
9+
onMatch(type: 'match' | 'fail', text: string) {
10+
this.emit(type, text);
11+
}
12+
13+
feed(bytes: number[]) {
14+
if (this.expectTexts.length === 0 && this.failTexts.length === 0) {
15+
return;
16+
}
17+
for (const byte of bytes) {
18+
const char = String.fromCharCode(byte);
19+
if (char === '\n') {
20+
this.testMatches();
21+
this.currentLine = '';
22+
} else {
23+
if (this.currentLine.length < 10_000) {
24+
this.currentLine += char;
25+
}
26+
}
27+
}
28+
}
29+
30+
private testMatches() {
31+
for (const candidate of this.expectTexts) {
32+
if (this.currentLine.includes(candidate)) {
33+
this.onMatch('match', candidate);
34+
}
35+
}
36+
for (const candidate of this.failTexts) {
37+
if (this.currentLine.includes(candidate)) {
38+
this.onMatch('fail', candidate);
39+
}
40+
}
41+
}
42+
}

src/help.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,15 @@ export function cliHelp() {
99
{bold OPTIONS}
1010
{green --help}, {green -h} Shows this help message and exit
1111
{green --quiet}, {green -q} Quiet: do not print version or status messages
12+
{green --expect-text} <string> Expect the given text in the output
13+
{green --fail-text} <string> Fail if the given text is found in the output
14+
{green --timeout} <number> Timeout in simulation milliseconds (default: 0 = none)
15+
16+
{bold EXAMPLES}
17+
18+
Running a simulation for 5 seconds, and expecting "Hello World" in the output (assuming wokwi.toml in the current directory):
19+
20+
{dim $} {bold wokwi-cli} {green --timeout} 5000 {green --expect-text} "Hello World"
21+
1222
`);
1323
}

src/main.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,35 @@
11
import arg from 'arg';
2+
import chalk from 'chalk';
23
import { existsSync, readFileSync } from 'fs';
34
import path, { join } from 'path';
45
import { APIClient } from './APIClient';
56
import type { APIEvent, SerialMonitorDataPayload } from './APITypes';
67
import { parseConfig } from './config';
7-
import { readVersion } from './readVersion';
88
import { cliHelp } from './help';
9+
import { readVersion } from './readVersion';
10+
import { ExpectEngine } from './ExpectEngine';
911

1012
async function main() {
1113
const args = arg(
1214
{
1315
'--help': Boolean,
1416
'--quiet': Boolean,
1517
'--version': Boolean,
18+
'--expect-text': String,
19+
'--fail-text': String,
20+
'--timeout': Number,
1621
'-h': '--help',
1722
'-q': '--quiet',
1823
},
1924
{ argv: process.argv.slice(2) }
2025
);
2126

2227
const quiet = args['--quiet'];
28+
const expectText = args['--expect-text'];
29+
const failText = args['--fail-text'];
30+
const timeout = args['--timeout'] ?? 0;
31+
const timeoutNanos = timeout * 1_000_000;
32+
2333
if (!quiet) {
2434
const { sha, version } = readVersion();
2535
console.log(`Wokwi CLI v${version} (${sha})`);
@@ -69,6 +79,28 @@ async function main() {
6979
process.exit(1);
7080
}
7181

82+
const expectEngine = new ExpectEngine();
83+
84+
if (expectText) {
85+
expectEngine.expectTexts.push(expectText);
86+
expectEngine.on('match', (text) => {
87+
if (!quiet) {
88+
console.log(chalk`\n\nExpected text found: {green "${expectText}"}`);
89+
console.log('TEST PASSED.');
90+
}
91+
process.exit(0);
92+
});
93+
}
94+
95+
if (failText) {
96+
expectEngine.failTexts.push(failText);
97+
expectEngine.on('fail', (text) => {
98+
console.error(chalk`\n\n{red Error:} Unexpected text found: {yellow "${text}"}`);
99+
console.error('TEST FAILED.');
100+
process.exit(1);
101+
});
102+
}
103+
72104
const client = new APIClient(token);
73105
client.onConnected = (hello) => {
74106
if (!quiet) {
@@ -85,13 +117,24 @@ async function main() {
85117
}
86118

87119
await client.serialMonitorListen();
88-
await client.simStart({ elf: 'test.elf', firmware: 'firmware' });
120+
await client.simStart({ elf: 'test.elf', firmware: 'firmware', pause: timeoutNanos > 0 });
121+
if (timeout > 0) {
122+
await client.simResume(timeoutNanos);
123+
}
89124

90125
client.onEvent = (event) => {
126+
if (event.event === 'sim:pause') {
127+
if (timeoutNanos && event.nanos >= timeoutNanos) {
128+
console.error(`Timeout: simulation did not finish in ${timeout}ms`);
129+
process.exit(42);
130+
}
131+
}
91132
if (event.event === 'serial-monitor:data') {
92-
for (const byte of (event as APIEvent<SerialMonitorDataPayload>).payload.bytes) {
133+
const { bytes } = (event as APIEvent<SerialMonitorDataPayload>).payload;
134+
for (const byte of bytes) {
93135
process.stdout.write(String.fromCharCode(byte));
94136
}
137+
expectEngine.feed(bytes);
95138
}
96139
};
97140
}

0 commit comments

Comments
 (0)