@@ -8,27 +8,39 @@ const spawn = require('child_process').spawn;
88const EventEmitter = require ( 'events' ) . EventEmitter ;
99
1010exports . fixturesDir = path . join ( __dirname , 'fixtures' ) ;
11- exports . buildDir = path . join ( __dirname , '..' , 'out' , 'Release ') ;
11+ exports . projectDir = path . join ( __dirname , '..' ) ;
1212
1313exports . core = path . join ( os . tmpdir ( ) , 'core' ) ;
14- exports . ranges = exports . core + '.ranges' ;
14+ exports . promptDelay = 200 ;
15+
16+ function llnodeDebug ( ) {
17+ // Node v4.x does not support rest
18+ const args = Array . prototype . slice . call ( arguments ) ;
19+ console . error . apply ( console , [ `[TEST][${ process . pid } ]` ] . concat ( args ) ) ;
20+ }
21+
22+ const debug = exports . debug =
23+ process . env . TEST_LLNODE_DEBUG ? llnodeDebug : ( ) => { } ;
1524
1625let pluginName ;
1726if ( process . platform === 'darwin' )
1827 pluginName = 'llnode.dylib' ;
1928else if ( process . platform === 'windows' )
2029 pluginName = 'llnode.dll' ;
2130else
22- pluginName = path . join ( 'lib.target' , ' llnode.so') ;
31+ pluginName = ' llnode.so';
2332
24- exports . llnodePath = path . join ( exports . buildDir , pluginName ) ;
33+ exports . llnodePath = path . join ( exports . projectDir , pluginName ) ;
34+ exports . saveCoreTimeout = 180 * 1000 ;
35+ exports . loadCoreTimeout = 20 * 1000 ;
2536
26- function SessionOutput ( session , stream ) {
37+ function SessionOutput ( session , stream , timeout ) {
2738 EventEmitter . call ( this ) ;
2839 this . waiting = false ;
2940 this . waitQueue = [ ] ;
30-
3141 let buf = '' ;
42+ this . timeout = timeout || 10000 ;
43+ this . session = session ;
3244
3345 stream . on ( 'data' , ( data ) => {
3446 buf += data ;
@@ -44,10 +56,8 @@ function SessionOutput(session, stream) {
4456
4557 if ( / p r o c e s s \d + e x i t e d / i. test ( line ) )
4658 session . kill ( ) ;
47- else if ( session . initialized )
59+ else
4860 this . emit ( 'line' , line ) ;
49- else if ( / p r o c e s s \d + l a u n c h e d / i. test ( line ) )
50- session . initialized = true ;
5161 }
5262 } ) ;
5363
@@ -72,109 +82,173 @@ SessionOutput.prototype._unqueueWait = function _unqueueWait() {
7282 this . waitQueue . shift ( ) ( ) ;
7383} ;
7484
75- SessionOutput . prototype . wait = function wait ( regexp , callback ) {
76- if ( ! this . _queueWait ( ( ) => { this . wait ( regexp , callback ) ; } ) )
77- return ;
78-
79- const self = this ;
80- this . on ( 'line' , function onLine ( line ) {
81- if ( ! regexp . test ( line ) )
82- return ;
83-
84- self . removeListener ( 'line' , onLine ) ;
85- self . _unqueueWait ( ) ;
86-
87- callback ( line ) ;
88- } ) ;
89- } ;
90-
91- SessionOutput . prototype . waitBreak = function waitBreak ( callback ) {
92- this . wait ( / P r o c e s s \d + s t o p p e d / i, callback ) ;
85+ SessionOutput . prototype . timeoutAfter = function timeoutAfter ( timeout ) {
86+ this . timeout = timeout ;
9387} ;
9488
95- SessionOutput . prototype . linesUntil = function linesUntil ( regexp , callback ) {
96- if ( ! this . _queueWait ( ( ) => { this . linesUntil ( regexp , callback ) ; } ) )
89+ SessionOutput . prototype . wait = function wait ( regexp , callback , allLines ) {
90+ if ( ! this . _queueWait ( ( ) => { this . wait ( regexp , callback , allLines ) ; } ) )
9791 return ;
9892
99- const lines = [ ] ;
10093 const self = this ;
101- this . on ( 'line' , function onLine ( line ) {
94+ const lines = [ ] ;
95+
96+ function onLine ( line ) {
10297 lines . push ( line ) ;
98+ debug ( '[LINE]' , line ) ;
10399
104100 if ( ! regexp . test ( line ) )
105101 return ;
106102
107103 self . removeListener ( 'line' , onLine ) ;
108104 self . _unqueueWait ( ) ;
105+ done = true ;
109106
110- callback ( lines ) ;
107+ callback ( allLines ? lines : line ) ;
108+ }
109+
110+ let done = false ;
111+ let timePassed = 0 ;
112+ const interval = 100 ;
113+ const check = setInterval ( ( ) => {
114+ timePassed += interval ;
115+ if ( done )
116+ clearInterval ( check ) ;
117+
118+ if ( timePassed > self . timeout ) {
119+ self . removeListener ( 'line' , onLine ) ;
120+ self . _unqueueWait ( ) ;
121+ const message = `Test timeout in ${ this . timeout } ` +
122+ `waiting for ${ regexp } \n` +
123+ `\n${ '=' . repeat ( 10 ) } lldb output ${ '=' . repeat ( 10 ) } \n` +
124+ `\n${ lines . join ( '\n' ) } ` +
125+ `\n${ '=' . repeat ( 30 ) } \n` ;
126+ throw new Error ( message ) ;
127+ }
128+ } , interval ) ;
129+
130+ this . on ( 'line' , onLine ) ;
131+ } ;
132+
133+ SessionOutput . prototype . waitBreak = function waitBreak ( callback ) {
134+ this . wait ( / P r o c e s s \d + s t o p p e d / i, ( ) => {
135+ // Do not resume immediately since the process would print
136+ // the instructions out and input sent before the stdout finish
137+ // could be lost
138+ setTimeout ( callback , exports . promptDelay ) ;
111139 } ) ;
112140} ;
113141
142+ SessionOutput . prototype . linesUntil = function linesUntil ( regexp , callback ) {
143+ this . wait ( regexp , callback , true ) ;
144+ } ;
114145
115- function Session ( scenario ) {
146+ function Session ( options ) {
116147 EventEmitter . call ( this ) ;
148+ const timeout = parseInt ( process . env . TEST_TIMEOUT ) || 10000 ;
149+ const lldbBin = process . env . TEST_LLDB_BINARY || 'lldb' ;
150+ const env = Object . assign ( { } , process . env ) ;
151+
152+ if ( options . ranges )
153+ env . LLNODE_RANGESFILE = options . ranges ;
154+
155+ debug ( 'lldb binary:' , lldbBin ) ;
156+ if ( options . scenario ) {
157+ this . needToKill = true ;
158+ // lldb -- node scenario.js
159+ const args = [
160+ '--' ,
161+ process . execPath ,
162+ '--abort_on_uncaught_exception' ,
163+ '--expose_externalize_string' ,
164+ path . join ( exports . fixturesDir , options . scenario )
165+ ] ;
166+
167+ debug ( 'lldb args:' , args ) ;
168+ this . lldb = spawn ( lldbBin , args , {
169+ stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
170+ env : env
171+ } ) ;
172+ this . lldb . stdin . write ( `plugin load "${ exports . llnodePath } "\n` ) ;
173+ this . lldb . stdin . write ( 'run\n' ) ;
174+ } else if ( options . core ) {
175+ this . needToKill = false ;
176+ debug ( 'loading core' , options . core ) ;
177+ // lldb node -c core
178+ this . lldb = spawn ( lldbBin , [ ] , {
179+ stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
180+ env : env
181+ } ) ;
182+ this . lldb . stdin . write ( `plugin load "${ exports . llnodePath } "\n` ) ;
183+ this . lldb . stdin . write ( `target create "${ options . executable } "` +
184+ ` --core "${ options . core } "\n` ) ;
185+ }
186+ this . stdout = new SessionOutput ( this , this . lldb . stdout , timeout ) ;
187+ this . stderr = new SessionOutput ( this , this . lldb . stderr , timeout ) ;
117188
118- // lldb -- node scenario.js
119- this . lldb = spawn ( process . env . TEST_LLDB_BINARY || 'lldb' , [
120- '--' ,
121- process . execPath ,
122- '--abort_on_uncaught_exception' ,
123- '--expose_externalize_string' ,
124- path . join ( exports . fixturesDir , scenario )
125- ] , {
126- stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
127- env : util . _extend ( util . _extend ( { } , process . env ) , {
128- LLNODE_RANGESFILE : exports . ranges
129- } )
189+ this . stderr . on ( 'line' , ( line ) => {
190+ debug ( '[stderr]' , line ) ;
130191 } ) ;
131192
132- this . lldb . stdin . write ( `plugin load "${ exports . llnodePath } "\n` ) ;
133- this . lldb . stdin . write ( 'run\n' ) ;
134-
135- this . initialized = false ;
136- this . stdout = new SessionOutput ( this , this . lldb . stdout ) ;
137- this . stderr = new SessionOutput ( this , this . lldb . stderr ) ;
138-
139193 // Map these methods to stdout for compatibility with legacy tests.
140194 this . wait = SessionOutput . prototype . wait . bind ( this . stdout ) ;
141195 this . waitBreak = SessionOutput . prototype . waitBreak . bind ( this . stdout ) ;
142196 this . linesUntil = SessionOutput . prototype . linesUntil . bind ( this . stdout ) ;
197+ this . timeoutAfter = SessionOutput . prototype . timeoutAfter . bind ( this . stdout ) ;
143198}
144199util . inherits ( Session , EventEmitter ) ;
145200exports . Session = Session ;
146201
147202Session . create = function create ( scenario ) {
148- return new Session ( scenario ) ;
203+ return new Session ( { scenario : scenario } ) ;
204+ } ;
205+
206+ Session . loadCore = function loadCore ( executable , core , ranges ) {
207+ return new Session ( {
208+ executable : executable ,
209+ core : core ,
210+ ranges : ranges
211+ } ) ;
212+ } ;
213+
214+ Session . prototype . waitCoreLoad = function waitCoreLoad ( callback ) {
215+ this . wait ( / C o r e f i l e [ ^ \n ] + w a s l o a d e d / , callback ) ;
149216} ;
150217
151218Session . prototype . kill = function kill ( ) {
152- this . lldb . kill ( ) ;
153- this . lldb = null ;
219+ // if a 'quit' has been sent to lldb, killing it could result in ECONNRESET
220+ if ( this . lldb . channel ) {
221+ debug ( 'kill lldb' ) ;
222+ this . lldb . kill ( ) ;
223+ this . lldb = null ;
224+ }
154225} ;
155226
156227Session . prototype . quit = function quit ( ) {
157- this . send ( 'kill' ) ;
228+ if ( this . needToKill )
229+ this . send ( 'kill' ) ; // kill the process launched in lldb
230+
158231 this . send ( 'quit' ) ;
159232} ;
160233
161234Session . prototype . send = function send ( line , callback ) {
235+ debug ( '[SEND]' , line ) ;
162236 this . lldb . stdin . write ( line + '\n' , callback ) ;
163237} ;
164238
165-
166- exports . generateRanges = function generateRanges ( cb ) {
239+ exports . generateRanges = function generateRanges ( core , dest , cb ) {
167240 let script ;
168241 if ( process . platform === 'darwin' )
169242 script = path . join ( __dirname , '..' , 'scripts' , 'otool2segments.py' ) ;
170243 else
171244 script = path . join ( __dirname , '..' , 'scripts' , 'readelf2segments.py' ) ;
172245
173- const proc = spawn ( script , [ exports . core ] , {
174- stdio : [ null , 'pipe' , 'inherit' ]
246+ debug ( '[RANGES]' , `${ script } , ${ core } , ${ dest } ` ) ;
247+ const proc = spawn ( script , [ core ] , {
248+ stdio : [ null , 'pipe' , 'inherit' ]
175249 } ) ;
176250
177- proc . stdout . pipe ( fs . createWriteStream ( exports . ranges ) ) ;
251+ proc . stdout . pipe ( fs . createWriteStream ( dest ) ) ;
178252
179253 proc . on ( 'exit' , ( status ) => {
180254 cb ( status === 0 ? null : new Error ( 'Failed to generate ranges' ) ) ;
0 commit comments