88 * binary, lets it run for a few seconds, then kills it and asserts the TUI
99 * actually rendered a known boot screen.
1010 *
11- * The positive check matters more than the negative one: a "did the boot
12- * screen appear" assertion catches *any* startup failure — known fatals,
11+ * Before the long-running UI check, this also invokes explicit compiled
12+ * runtime smoke flags in the binary. Those cover startup-sensitive assets
13+ * (tree-sitter) and non-UI runtime integrations (network, subprocesses,
14+ * vendored native tools, filesystem IO).
15+ *
16+ * The positive boot check matters more than the negative one: a "did the
17+ * boot screen appear" assertion catches *any* startup failure — known fatals,
1318 * novel error messages, silent crashes, hangs, segfaults that produce no
1419 * output. Negative pattern matches are kept only for clearer diagnostics
1520 * when a known regression recurs.
@@ -81,9 +86,21 @@ const FATAL_PATTERNS = [
8186// the renderer is up).
8287const DEFAULT_RUN_SECONDS = 10
8388
84- function runTreeSitterSmoke ( binary : string ) : Promise < void > {
89+ function runFlagSmoke ( {
90+ binary,
91+ flag,
92+ label,
93+ okPattern,
94+ timeoutMs,
95+ } : {
96+ binary : string
97+ flag : string
98+ label : string
99+ okPattern : RegExp
100+ timeoutMs : number
101+ } ) : Promise < void > {
85102 return new Promise ( ( resolve , reject ) => {
86- const proc = spawn ( binary , [ '--smoke-tree-sitter' ] , {
103+ const proc = spawn ( binary , [ flag ] , {
87104 stdio : [ 'ignore' , 'pipe' , 'pipe' ] ,
88105 env : { ...process . env , NO_COLOR : '1' , TERM : 'dumb' } ,
89106 } )
@@ -95,16 +112,36 @@ function runTreeSitterSmoke(binary: string): Promise<void> {
95112 proc . stdout ?. on ( 'data' , append )
96113 proc . stderr ?. on ( 'data' , append )
97114
115+ let timedOut = false
116+ const timeout = setTimeout ( ( ) => {
117+ timedOut = true
118+ proc . kill ( 'SIGKILL' )
119+ } , timeoutMs )
120+
98121 proc . once ( 'error' , reject )
99122 proc . once ( 'exit' , ( code ) => {
100- if ( code === 0 && / t r e e - s i t t e r s m o k e o k / . test ( captured ) ) {
123+ clearTimeout ( timeout )
124+
125+ if ( timedOut ) {
126+ reject (
127+ new Error (
128+ `${ label } smoke timed out after ${ timeoutMs } ms\n${ captured . slice (
129+ 0 ,
130+ 8 * 1024 ,
131+ ) } `,
132+ ) ,
133+ )
134+ return
135+ }
136+
137+ if ( code === 0 && okPattern . test ( captured ) ) {
101138 resolve ( )
102139 return
103140 }
104141
105142 reject (
106143 new Error (
107- `tree-sitter smoke failed with exit code ${ code } \n${ captured . slice (
144+ `${ label } smoke failed with exit code ${ code } \n${ captured . slice (
108145 0 ,
109146 8 * 1024 ,
110147 ) } `,
@@ -133,9 +170,24 @@ async function main(): Promise<void> {
133170
134171 console . log ( `smoke-binary: spawning ${ binary } for ${ runSeconds } s…` )
135172
136- await runTreeSitterSmoke ( binary )
173+ await runFlagSmoke ( {
174+ binary,
175+ flag : '--smoke-tree-sitter' ,
176+ label : 'tree-sitter' ,
177+ okPattern : / t r e e - s i t t e r s m o k e o k / ,
178+ timeoutMs : 30_000 ,
179+ } )
137180 console . log ( 'smoke-binary: tree-sitter init OK.' )
138181
182+ await runFlagSmoke ( {
183+ binary,
184+ flag : '--smoke-runtime-primitives' ,
185+ label : 'runtime primitives' ,
186+ okPattern : / r u n t i m e p r i m i t i v e s s m o k e o k / ,
187+ timeoutMs : 90_000 ,
188+ } )
189+ console . log ( 'smoke-binary: runtime primitives OK.' )
190+
139191 const proc = spawn ( binary , [ ] , {
140192 stdio : [ 'ignore' , 'pipe' , 'pipe' ] ,
141193 env : { ...process . env , NO_COLOR : '1' , TERM : 'dumb' } ,
0 commit comments