68 lines
3.9 KiB
JavaScript
68 lines
3.9 KiB
JavaScript
import test from 'node:test'
|
|||
|
|
import assert from 'node:assert/strict'
|
||
|
|
import { readFileSync, mkdtempSync, writeFileSync, rmSync, existsSync } from 'node:fs'
|
||
|
|
import { tmpdir } from 'node:os'
|
||
|
|
import { join, resolve } from 'node:path'
|
||
|
|
import { fileURLToPath } from 'node:url'
|
||
|
|
import { spawnSync } from 'node:child_process'
|
||
|
|
|
||
|
|
const root = fileURLToPath(new URL('..', import.meta.url))
|
||
|
|
const wrapper = resolve(root, 'deploy/scripts/run-ci-maven.sh')
|
||
|
|
const bash = process.platform === 'win32' ? 'C:/Program Files/Git/bin/bash.exe' : 'bash'
|
||
|
|
const shellAvailable = process.platform !== 'win32' || existsSync(bash)
|
||
|
|
|
||
|
|
test('both Jenkins Maven paths share one cross-job lock and bounded host entry', () => {
|
||
|
|
for (const file of ['Jenkinsfile', 'Jenkinsfile.service']) {
|
||
|
|
const source = readFileSync(resolve(root, file), 'utf8')
|
||
|
|
assert.match(source, /lock\(resource: 'firstleg-test-maven'\)\s*\{\s*timeout\(time: 50, unit: 'MINUTES'\)/)
|
||
|
|
assert.match(source, /sh deploy\/scripts\/run-ci-maven\.sh --settings/)
|
||
|
|
assert.doesNotMatch(source, /(?:^|\n)\s*mvn\s|sh "mvn /)
|
||
|
|
}
|
||
|
|
const source = readFileSync(wrapper, 'utf8')
|
||
|
|
assert.match(source, /exec 9>/)
|
||
|
|
assert.match(source, /flock -n 9/)
|
||
|
|
assert.match(source, /exec timeout --signal=TERM --kill-after=30s 45m mvn -Pci-bounded/)
|
||
|
|
assert.doesNotMatch(source, /exec timeout[^\n]*--foreground/)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('test JVM gets a separate heap, JaCoCo late binding, and parent-loss cleanup', () => {
|
||
|
|
const pom = readFileSync(resolve(root, 'pom.xml'), 'utf8')
|
||
|
|
const profile = pom.match(/<profile>\s*<id>ci-bounded<\/id>([\s\S]*?)<\/profile>/)?.[1]
|
||
|
|
assert.ok(profile)
|
||
|
|
assert.match(profile, /<argLine>@\{argLine\}[^<]*-Xmx1024m[^<]*-XX:\+ExitOnOutOfMemoryError<\/argLine>/)
|
||
|
|
for (const [name, value] of Object.entries({ forkCount: '1', enableProcessChecker: 'ping', shutdown: 'kill',
|
||
|
|
forkedProcessTimeoutInSeconds: '1200', forkedProcessExitTimeoutInSeconds: '30' }))
|
||
|
|
assert.ok(profile.includes(`<${name}>${value}</${name}>`))
|
||
|
|
})
|
||
|
|
|
||
|
|
test('shell rejects parallel reactors and held host lock before invoking Maven; propagates failure', { skip: !shellAvailable }, () => {
|
||
|
|
const directory = mkdtempSync(join(tmpdir(), 'ci-maven-command-test-'))
|
||
|
|
try {
|
||
|
|
// Stand-ins exercise command wiring, not Linux flock or process-group signalling.
|
||
|
|
writeFileSync(join(directory, 'flock'), '#!/bin/sh\nexit "${TEST_LOCK_STATUS:-0}"\n', { mode: 0o755 })
|
||
|
|
writeFileSync(join(directory, 'mvn'), '#!/bin/sh\nexit 99\n', { mode: 0o755 })
|
||
|
|
writeFileSync(join(directory, 'timeout'), '#!/bin/sh\nprintf "%s\\n" "$MAVEN_OPTS" "$@"\nexit "${TEST_CHILD_STATUS:-0}"\n', { mode: 0o755 })
|
||
|
|
const run = (args, extra = {}) => spawnSync(bash, ['-c',
|
||
|
|
'testbin=$(cd "$1" && pwd); shift; PATH="$testbin:$PATH"; export PATH; exec sh "$@"',
|
||
|
|
'fixture', directory.replaceAll('\\', '/'), wrapper.replaceAll('\\', '/'), ...args],
|
||
|
|
{ encoding: 'utf8', timeout: 10000, env: { ...process.env, ...extra } })
|
||
|
|
for (const args of [['-T', '3', 'test'], ['-T3', 'test'], ['--threads=3', 'test']]) {
|
||
|
|
const result = run(args)
|
||
|
|
assert.equal(result.status, 64, result.stderr)
|
||
|
|
assert.equal(result.stdout, '')
|
||
|
|
}
|
||
|
|
const held = run(['test'], { TEST_LOCK_STATUS: '1' })
|
||
|
|
assert.equal(held.status, 75, held.stderr)
|
||
|
|
assert.equal(held.stdout, '')
|
||
|
|
const normal = run(['-pl', 'file', '-am', 'test'], { TEST_CHILD_STATUS: '23', MAVEN_OPTS: '-Xmx8g' })
|
||
|
|
assert.equal(normal.status, 23, normal.stderr)
|
||
|
|
assert.match(normal.stdout, /-Xmx768m/)
|
||
|
|
assert.doesNotMatch(normal.stdout, /-Xmx8g/)
|
||
|
|
assert.match(normal.stdout, /--signal=TERM\r?\n--kill-after=30s\r?\n45m\r?\nmvn\r?\n-Pci-bounded\r?\n-pl\r?\nfile/)
|
||
|
|
} finally {
|
||
|
|
// Only this newly-created fixture directory; no project copies or shared caches removed.
|
||
|
|
assert.equal(resolve(directory).startsWith(resolve(tmpdir()) + (process.platform === 'win32' ? '\\' : '/')), true)
|
||
|
|
rmSync(directory, { recursive: true })
|
||
|
|
}
|
||
|
|
})
|