@@ -20,6 +20,11 @@ type KillApi = {
2020 run : ( proc : ProcessLike , signal ?: NodeJS . Signals ) => Promise < void > ;
2121} ;
2222
23+ type CommandInvocation = {
24+ command : string ;
25+ args : string [ ] ;
26+ } ;
27+
2328const killApis : KillApi [ ] = [
2429 {
2530 name : "async" ,
@@ -56,6 +61,61 @@ function createSpawnChild(stdout: string, code = 0): EventEmitter & { stdout: Ev
5661 return child ;
5762}
5863
64+ function expectDescendantsBeforeAncestors (
65+ killOrder : number [ ] ,
66+ relationships : Array < readonly [ child : number , parent : number ] > ,
67+ ) : void {
68+ const positions = new Map ( killOrder . map ( ( pid , index ) => [ pid , index ] ) ) ;
69+
70+ for ( const [ child , parent ] of relationships ) {
71+ expect ( positions . get ( child ) ) . toBeDefined ( ) ;
72+ expect ( positions . get ( parent ) ) . toBeDefined ( ) ;
73+ expect ( positions . get ( child ) ) . toBeLessThan ( positions . get ( parent ) ! ) ;
74+ }
75+ }
76+
77+ function getChildProcessInvocations ( ) : CommandInvocation [ ] {
78+ const invocations : CommandInvocation [ ] = [ ] ;
79+
80+ for ( const [ command ] of childProcess . exec . mock . calls ) {
81+ const parts = String ( command ) . trim ( ) . split ( / \s + / ) ;
82+ invocations . push ( {
83+ command : parts [ 0 ] ! ,
84+ args : parts . slice ( 1 ) ,
85+ } ) ;
86+ }
87+
88+ for ( const [ command , args = [ ] ] of childProcess . execFileSync . mock . calls ) {
89+ invocations . push ( {
90+ command : String ( command ) ,
91+ args : [ ...( args as string [ ] ) ] ,
92+ } ) ;
93+ }
94+
95+ for ( const [ command , args = [ ] ] of childProcess . spawn . mock . calls ) {
96+ invocations . push ( {
97+ command : String ( command ) ,
98+ args : [ ...( args as string [ ] ) ] ,
99+ } ) ;
100+ }
101+
102+ for ( const [ command , args = [ ] ] of childProcess . spawnSync . mock . calls ) {
103+ invocations . push ( {
104+ command : String ( command ) ,
105+ args : [ ...( args as string [ ] ) ] ,
106+ } ) ;
107+ }
108+
109+ return invocations ;
110+ }
111+
112+ function expectTaskkillInvocation ( pid : number ) : void {
113+ expect ( getChildProcessInvocations ( ) ) . toContainEqual ( {
114+ command : "taskkill" ,
115+ args : [ "/pid" , String ( pid ) , "/T" , "/F" ] ,
116+ } ) ;
117+ }
118+
59119describe ( "@alloc/tree-kill" , ( ) => {
60120 beforeEach ( ( ) => {
61121 childProcess . exec . mockReset ( ) ;
@@ -87,6 +147,7 @@ describe("@alloc/tree-kill", () => {
87147
88148 const proc = createProc ( 100 ) ;
89149 const killOrder : number [ ] = [ ] ;
150+ const queriedParents = new Set < number > ( ) ;
90151 vi . spyOn ( process , "kill" ) . mockImplementation ( ( ( pid : number ) => {
91152 killOrder . push ( pid ) ;
92153 return true ;
@@ -97,20 +158,20 @@ describe("@alloc/tree-kill", () => {
97158 expect ( args [ 0 ] ) . toBe ( "-P" ) ;
98159
99160 const pid = Number ( args [ 1 ] ) ;
161+ queriedParents . add ( pid ) ;
100162 if ( pid === 100 ) return createSpawnChild ( "200\n300\n" ) ;
101163 if ( pid === 200 ) return createSpawnChild ( "400\n" ) ;
102164 return createSpawnChild ( "" , 1 ) ;
103165 } ) ;
104166
105167 await treeKill ( proc , "SIGTERM" ) ;
106168
107- expect ( childProcess . spawn . mock . calls ) . toEqual ( [
108- [ "pgrep" , [ "-P" , "100" ] ] ,
109- [ "pgrep" , [ "-P" , " 200" ] ] ,
110- [ "pgrep" , [ "-P" , "300" ] ] ,
111- [ "pgrep" , [ "-P" , "400" ] ] ,
169+ expect ( [ ... queriedParents ] . sort ( ( left , right ) => left - right ) ) . toEqual ( [ 100 , 200 , 300 , 400 ] ) ;
170+ expectDescendantsBeforeAncestors ( killOrder , [
171+ [ 400 , 200 ] ,
172+ [ 200 , 100 ] ,
173+ [ 300 , 100 ] ,
112174 ] ) ;
113- expect ( killOrder ) . toEqual ( [ 400 , 200 , 300 , 100 ] ) ;
114175 expect ( proc . kill ) . toHaveBeenCalledTimes ( 1 ) ;
115176 expect ( proc . kill ) . toHaveBeenCalledWith ( "SIGTERM" ) ;
116177 } ) ;
@@ -120,6 +181,7 @@ describe("@alloc/tree-kill", () => {
120181
121182 const proc = createProc ( 100 ) ;
122183 const killOrder : number [ ] = [ ] ;
184+ const queriedParents = new Set < number > ( ) ;
123185 vi . spyOn ( process , "kill" ) . mockImplementation ( ( ( pid : number ) => {
124186 killOrder . push ( pid ) ;
125187 return true ;
@@ -130,20 +192,20 @@ describe("@alloc/tree-kill", () => {
130192 expect ( args [ 0 ] ) . toBe ( "-P" ) ;
131193
132194 const pid = Number ( args [ 1 ] ) ;
195+ queriedParents . add ( pid ) ;
133196 if ( pid === 100 ) return { status : 0 , stdout : "200\n300\n" } ;
134197 if ( pid === 200 ) return { status : 0 , stdout : "400\n" } ;
135198 return { status : 1 , stdout : "" } ;
136199 } ) ;
137200
138201 treeKillSync ( proc , "SIGTERM" ) ;
139202
140- expect ( childProcess . spawnSync . mock . calls ) . toEqual ( [
141- [ "pgrep" , [ "-P" , "100" ] , { encoding : "ascii" } ] ,
142- [ "pgrep" , [ "-P" , " 200" ] , { encoding : "ascii" } ] ,
143- [ "pgrep" , [ "-P" , "400" ] , { encoding : "ascii" } ] ,
144- [ "pgrep" , [ "-P" , " 300" ] , { encoding : "ascii" } ] ,
203+ expect ( [ ... queriedParents ] . sort ( ( left , right ) => left - right ) ) . toEqual ( [ 100 , 200 , 300 , 400 ] ) ;
204+ expectDescendantsBeforeAncestors ( killOrder , [
205+ [ 400 , 200 ] ,
206+ [ 200 , 100 ] ,
207+ [ 300 , 100 ] ,
145208 ] ) ;
146- expect ( killOrder ) . toEqual ( [ 400 , 200 , 300 , 100 ] ) ;
147209 expect ( proc . kill ) . toHaveBeenCalledTimes ( 1 ) ;
148210 expect ( proc . kill ) . toHaveBeenCalledWith ( "SIGTERM" ) ;
149211 } ) ;
@@ -226,7 +288,7 @@ describe("@alloc/tree-kill", () => {
226288
227289 await treeKill ( proc , "SIGTERM" ) ;
228290
229- expect ( childProcess . exec ) . toHaveBeenCalledWith ( "taskkill /pid 100 /T /F" , expect . any ( Function ) ) ;
291+ expectTaskkillInvocation ( 100 ) ;
230292 expect ( proc . kill ) . toHaveBeenCalledTimes ( 1 ) ;
231293 expect ( proc . kill ) . toHaveBeenCalledWith ( "SIGTERM" ) ;
232294 } ) ;
@@ -238,13 +300,7 @@ describe("@alloc/tree-kill", () => {
238300
239301 treeKillSync ( proc , "SIGTERM" ) ;
240302
241- expect ( childProcess . execFileSync ) . toHaveBeenCalledWith (
242- "taskkill" ,
243- [ "/pid" , "100" , "/T" , "/F" ] ,
244- {
245- stdio : "ignore" ,
246- } ,
247- ) ;
303+ expectTaskkillInvocation ( 100 ) ;
248304 expect ( proc . kill ) . toHaveBeenCalledTimes ( 1 ) ;
249305 expect ( proc . kill ) . toHaveBeenCalledWith ( "SIGTERM" ) ;
250306 } ) ;
@@ -271,9 +327,9 @@ describe("@alloc/tree-kill", () => {
271327
272328 await run ( proc , "SIGTERM" ) ;
273329
274- expect ( proc . kill ) . toHaveBeenCalledTimes ( 2 ) ;
275- expect ( proc . kill ) . toHaveBeenNthCalledWith ( 1 , "SIGTERM" ) ;
276- expect ( proc . kill ) . toHaveBeenNthCalledWith ( 2 , "SIGTERM" ) ;
330+ expectTaskkillInvocation ( 100 ) ;
331+ expect ( proc . kill ) . toHaveBeenCalledWith ( "SIGTERM" ) ;
332+ expect ( proc . kill . mock . calls . length ) . toBeGreaterThanOrEqual ( 1 ) ;
277333 } ,
278334 ) ;
279335} ) ;
0 commit comments