2026-10-19 17:56:36 +08:00
import assert from 'node:assert/strict'
import { spawn } from 'node:child_process'
import { mkdtemp , readFile , rm , writeFile } from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
const launcher = fileURLToPath ( new URL ( './local-check.ps1' , import . meta . url ))
const quote = value => `' ${ String ( value ). replaceAll ( "'" , "''" ) } '`
const delay = ms => new Promise ( resolve => setTimeout ( resolve , ms ))
function run ( state , args , { cwd = os . tmpdir (), timeout = 15 , wait = 15 } = {}) {
const script = `& ${ quote ( launcher ) } -WorkingDirectory ${ quote ( cwd ) } -TimeoutSeconds ${ timeout } -WaitSeconds ${ wait } -Command @( ${ args . map ( quote ). join ( ',' ) } ); exit $LASTEXITCODE`
const env = { ... process . env , LOCALAPPDATA : state }
delete env . CHONGCHONG_LOCAL_CHECK_OWNER
const child = spawn ( 'pwsh' , [ '-NoProfile' , '-NonInteractive' , '-OutputFormat' , 'Text' , '-EncodedCommand' , Buffer . from ( script , 'utf16le' ). toString ( 'base64' )], { env })
let output = ''
child . stdout . on ( 'data' , chunk => { output += chunk })
child . stderr . on ( 'data' , chunk => { output += chunk })
const done = new Promise (( resolve , reject ) => {
child . on ( 'error' , reject )
child . on ( 'close' , code => resolve ({ code , output }))
})
return { child , done , output : () => output }
}
async function until ( check , message ) {
const deadline = Date . now () + 12000
while ( Date . now () < deadline ) {
if ( await check ()) return
await delay ( 100 )
}
assert . fail ( message )
}
2026-10-20 15:48:38 +08:00
test ( 'local-check bounds different directories to two slots, handles failures and recovers process trees' , { skip : process . platform !== 'win32' , timeout : 120000 }, async t => {
2026-10-19 17:56:36 +08:00
const state = await mkdtemp ( path . join ( os . tmpdir (), 'local-check-test-' ))
const processes = []
const start = (... args ) => { const result = run ( state , ... args ); processes . push ( result ); return result }
try {
await t . test ( 'literal arguments, environment caps and exit codes are preserved' , async () => {
const result = await start ([ process . execPath , '-e' , 'console.log(JSON.stringify([process.argv[1],process.env.VITEST_MAX_WORKERS,process.env.NODE_OPTIONS,process.env.JAVA_TOOL_OPTIONS]));process.exit(7)' , 'space quote \' $() ` ; 中文' ]). done
assert . equal ( result . code , 7 , result . output )
assert . ok ( result . output . includes ( JSON . stringify ( 'space quote \' $() ` ; 中文' )))
assert . match ( result . output , /"2","--max-old-space-size=2048"/ )
assert . match ( result . output , /ActiveProcessorCount=2 -Xmx1536m/ )
})
2026-10-20 15:48:38 +08:00
await t . test ( 'two directories run together; a third times out or waits until one lease is released' , async () => {
const releaseFirst = path . join ( state , 'release-first' ), releaseSecond = path . join ( state , 'release-second' )
const hold = ( file , label ) => `const fs=require('fs');console.log( ${ JSON . stringify ( label ) } );const timer=setInterval(()=>{if(fs.existsSync( ${ JSON . stringify ( file ) } ))clearInterval(timer)},50)`
const first = start ([ process . execPath , '-e' , hold ( releaseFirst , 'FIRST_READY' )], { timeout : 30 })
await until (() => first . output (). includes ( 'FIRST_READY' ), first . output ())
const second = start ([ process . execPath , '-e' , hold ( releaseSecond , 'SECOND_READY' )], { cwd : path . dirname ( launcher ), timeout : 30 })
await until (() => second . output (). includes ( 'SECOND_READY' ), second . output ())
assert . equal ( first . child . exitCode , null )
const owners = await Promise . all ([ 'owner.json' , 'owner-2.json' ]. map ( name => readFile ( path . join ( state , 'Chongchong' , 'LocalChecks' , name ), 'utf8' ). then ( JSON . parse )))
assert . deepEqual ( owners . map ( owner => owner . slot ). sort (), [ 1 , 2 ])
assert . equal ( new Set ( owners . map ( owner => owner . directory )). size , 2 )
2026-10-19 17:56:36 +08:00
const timedOut = await start ([ process . execPath , '-e' , 'console.log("SHOULD_NOT_RUN")' ], { wait : 0 , cwd : path . dirname ( launcher ) }). done
assert . equal ( timedOut . code , 75 , timedOut . output )
assert . doesNotMatch ( timedOut . output , /SHOULD_NOT_RUN/ )
2026-10-20 15:48:38 +08:00
const third = start ([ process . execPath , '-e' , 'console.log("THIRD_READY")' ], { cwd : state })
await until (() => third . output (). includes ( 'WAITING' ), third . output ())
assert . doesNotMatch ( third . output (), /THIRD_READY/ )
await writeFile ( releaseFirst , '' )
2026-10-19 17:56:36 +08:00
assert . equal (( await first . done ). code , 0 )
2026-10-20 15:48:38 +08:00
const result = await third . done
2026-10-19 17:56:36 +08:00
assert . equal ( result . code , 0 , result . output )
2026-10-20 15:48:38 +08:00
assert . match ( result . output , /THIRD_READY/ )
assert . equal ( second . child . exitCode , null )
await writeFile ( releaseSecond , '' )
assert . equal (( await second . done ). code , 0 )
2026-10-19 17:56:36 +08:00
})
await t . test ( 'execution timeout kills descendants and frees the slot' , async () => {
const pidFile = path . join ( state , 'descendant.pid' )
const code = `const {spawn}=require('child_process');const fs=require('fs');const c=spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{stdio:'ignore'});fs.writeFileSync( ${ JSON . stringify ( pidFile ) } ,String(c.pid));setInterval(()=>{},1000)`
const result = await start ([ process . execPath , '-e' , code ], { timeout : 3 }). done
assert . equal ( result . code , 124 , result . output )
const descendant = Number ( await readFile ( pidFile , 'utf8' ))
await until (() => { try { process . kill ( descendant , 0 ); return false } catch { return true } }, 'descendant survived timeout' )
assert . equal (( await start ([ process . execPath , '-e' , 'process.exit(0)' ]). done ). code , 0 )
})
await t . test ( 'abrupt wrapper termination kills its descendants and releases the file lock' , async () => {
const pidFile = path . join ( state , 'abrupt.pid' )
const code = `require('fs').writeFileSync( ${ JSON . stringify ( pidFile ) } ,String(process.pid));console.log('ABRUPT_READY');setInterval(()=>{},1000)`
const active = start ([ process . execPath , '-e' , code ])
await until (() => active . output (). includes ( 'ABRUPT_READY' ), active . output ())
const descendant = Number ( await readFile ( pidFile , 'utf8' ))
active . child . kill ()
await active . done
await until (() => { try { process . kill ( descendant , 0 ); return false } catch { return true } }, 'descendant survived wrapper termination' )
assert . equal (( await start ([ process . execPath , '-e' , 'process.exit(0)' ]). done ). code , 0 )
})
await t . test ( 'missing commands fail and do not leave the slot occupied' , async () => {
const result = await start ([ 'chongchong-missing-command-for-test' ]). done
assert . notEqual ( result . code , 0 , result . output )
assert . equal (( await start ([ process . execPath , '-e' , 'process.exit(0)' ]). done ). code , 0 )
})
await t . test ( 'Maven arguments are limited without launching Maven' , async () => {
const fakeMaven = path . join ( state , 'mvn.ps1' )
await writeFile ( fakeMaven , 'ConvertTo-Json -InputObject @($args) -Compress\n' )
const result = await start ([ fakeMaven , '-pl' , 'example' , 'test' ]). done
assert . equal ( result . code , 0 , result . output )
assert . match ( result . output , /\["-T","1","-DforkCount=1","-DreuseForks=true","-pl","example","test"\]/ )
const override = await start ([ fakeMaven , '-T4' , 'test' ]). done
assert . notEqual ( override . code , 0 , override . output )
assert . match ( override . output , /Do not override Maven concurrency/ )
})
await t . test ( 'direct Node test concurrency is supplied and nested locks are rejected' , async () => {
const nodeTest = path . join ( state , 'probe.test.mjs' )
await writeFile ( nodeTest , `import assert from 'node:assert/strict'; assert.ok(process.execArgv.includes('--test-concurrency=2'));\n` )
const result = await start ([ process . execPath , '--test' , nodeTest ]). done
assert . equal ( result . code , 0 , result . output )
const nested = await start ([ 'pwsh' , '-NoProfile' , '-File' , launcher , '-Command' , 'node' ]). done
assert . notEqual ( nested . code , 0 , nested . output )
assert . match ( nested . output , /Already inside local-check/ )
})
} finally {
for ( const item of processes ) if ( item . child . exitCode === null ) item . child . kill ()
await Promise . all ( processes . map ( item => item . done ))
await rm ( state , { recursive : true , force : true })
}
})