feat: close ordered pairs and property codec batches
This commit is contained in:
98
scripts/run-freecad-property-direction-failure.mjs
Normal file
98
scripts/run-freecad-property-direction-failure.mjs
Normal file
@@ -0,0 +1,98 @@
|
||||
import { existsSync, mkdtempSync, rmSync } from 'node:fs'
|
||||
import { readFile, writeFile } from 'node:fs/promises'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { tmpdir } from 'node:os'
|
||||
|
||||
const root = resolve(new URL('..', import.meta.url).pathname)
|
||||
const executable = process.env.FREECAD_CMD || resolve(root, '.cache/freecad/install-desktop/bin/FreeCADCmd')
|
||||
const sysroot = resolve(root, '.cache/freecad/sysroot')
|
||||
const scriptPath = resolve(root, 'scripts/freecad-property-direction-failure.py')
|
||||
const outputPath = resolve(root, 'config/freecad-property-direction-failure.json')
|
||||
if (!existsSync(executable)) throw new Error(`FreeCAD PropertyDirection executable is missing: ${executable}`)
|
||||
const baseEnv = {
|
||||
...process.env,
|
||||
PYTHONPATH: `${resolve(sysroot, 'usr/lib/python3/dist-packages')}${process.env.PYTHONPATH ? `:${process.env.PYTHONPATH}` : ''}`,
|
||||
LD_LIBRARY_PATH: `${resolve(sysroot, 'usr/lib/x86_64-linux-gnu')}${process.env.LD_LIBRARY_PATH ? `:${process.env.LD_LIBRARY_PATH}` : ''}`,
|
||||
}
|
||||
const runProbe = ({ mode = 'main', target = outputPath, timeout = 120_000 } = {}) => spawnSync(executable, ['--python-path', resolve(sysroot, 'usr/lib/python3/dist-packages'), scriptPath], {
|
||||
cwd: root,
|
||||
encoding: 'utf8',
|
||||
timeout,
|
||||
maxBuffer: 32 * 1024 * 1024,
|
||||
env: {
|
||||
...baseEnv,
|
||||
FREECAD_PROPERTY_DIRECTION_FAILURE_OUTPUT: target,
|
||||
FREECAD_PROPERTY_DIRECTION_FAILURE_MODE: mode,
|
||||
},
|
||||
})
|
||||
const execution = runProbe({ timeout: Number(process.env.FREECAD_PROPERTY_DIRECTION_FAILURE_TIMEOUT_MS || 120_000) })
|
||||
const output = `${execution.stdout || ''}\n${execution.stderr || ''}`
|
||||
if (execution.error || execution.status !== 0 || !output.includes('FREECAD_PROPERTY_DIRECTION_FAILURE_RESULT=')) throw new Error(`FreeCAD PropertyDirection failure probe failed with status ${execution.status}: ${execution.error?.message || 'process failed'}\n${output.trim()}`)
|
||||
const report = JSON.parse(await readFile(outputPath, 'utf8'))
|
||||
const isolatedRoot = mkdtempSync(join(tmpdir(), 'freecad-property-direction-'))
|
||||
try {
|
||||
report.nonFiniteConsumerBoundaries = []
|
||||
const isolatedTimeout = Number(process.env.FREECAD_PROPERTY_DIRECTION_NON_FINITE_TIMEOUT_MS || 5_000)
|
||||
for (const hostId of ['Mirroring', 'ProjectOnSurface']) {
|
||||
for (const valueId of ['nan', 'positive-infinity', 'negative-infinity']) {
|
||||
const caseId = `${hostId}-${valueId}`
|
||||
const attempts = []
|
||||
for (let attempt = 1; attempt <= 2; attempt += 1) {
|
||||
const target = join(isolatedRoot, `${caseId}-${attempt}.json`)
|
||||
const startedAt = Date.now()
|
||||
const isolated = runProbe({ mode: `non-finite:${hostId}:${valueId}`, target, timeout: isolatedTimeout })
|
||||
const isolatedOutput = `${isolated.stdout || ''}\n${isolated.stderr || ''}`.trim()
|
||||
const timedOut = isolated.error?.code === 'ETIMEDOUT'
|
||||
let completedCase = null
|
||||
if (!timedOut && isolated.status === 0 && isolatedOutput.includes('FREECAD_PROPERTY_DIRECTION_FAILURE_ISOLATED_RESULT=') && existsSync(target)) {
|
||||
completedCase = JSON.parse(await readFile(target, 'utf8')).case
|
||||
}
|
||||
attempts.push({
|
||||
attempt,
|
||||
timeoutMs: isolatedTimeout,
|
||||
elapsedMs: Date.now() - startedAt,
|
||||
timedOut,
|
||||
exitStatus: isolated.status,
|
||||
signal: isolated.signal,
|
||||
errorCode: isolated.error?.code || null,
|
||||
progressMarkerObserved: isolatedOutput.includes(`FREECAD_PROPERTY_DIRECTION_PROGRESS=${caseId}`),
|
||||
completionMarkerObserved: isolatedOutput.includes('FREECAD_PROPERTY_DIRECTION_FAILURE_ISOLATED_RESULT='),
|
||||
diagnostic: isolatedOutput.split('\n').filter((line) => line && !line.startsWith('FreeCAD 0.')).slice(-8),
|
||||
completedCase,
|
||||
outcome: timedOut ? 'timeout' : completedCase ? 'completed' : 'process-exited-without-completion',
|
||||
})
|
||||
}
|
||||
const outcomes = [...new Set(attempts.map(({ outcome }) => outcome))]
|
||||
const stable = outcomes.length === 1 && attempts.every(({ progressMarkerObserved }) => progressMarkerObserved)
|
||||
report.nonFiniteConsumerBoundaries.push({
|
||||
id: caseId,
|
||||
mode: `non-finite:${hostId}:${valueId}`,
|
||||
attemptCount: attempts.length,
|
||||
attempts,
|
||||
stable,
|
||||
classification: !stable
|
||||
? 'native-nondeterministic'
|
||||
: outcomes[0] === 'timeout'
|
||||
? 'native-synchronous-consumer-timeout'
|
||||
: outcomes[0] === 'process-exited-without-completion'
|
||||
? 'native-process-exit-without-probe-completion'
|
||||
: 'native-consumer-returned',
|
||||
})
|
||||
}
|
||||
}
|
||||
report.status = report.nonFiniteConsumerBoundaries.every(({ stable, classification }) => stable && classification !== 'native-consumer-returned') ? 'pass' : 'fail'
|
||||
await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`)
|
||||
} finally {
|
||||
rmSync(isolatedRoot, { recursive: true, force: true })
|
||||
}
|
||||
console.log(JSON.stringify({
|
||||
status: report.status,
|
||||
output: 'config/freecad-property-direction-failure.json',
|
||||
acceptedCoercions: report.setterBoundaries.acceptedCoercions.map(({ id, accepted, after }) => ({ id, accepted, after })),
|
||||
setterFailures: report.setterBoundaries.failures.map(({ id, exception, valuePreserved }) => ({ id, exception, valuePreserved })),
|
||||
consumerFailures: report.consumerFailures.map(({ id, setterException, recomputeResult, afterRecompute }) => ({ id, setterException, recomputeResult, state: afterRecompute.state })),
|
||||
nonFiniteConsumerBoundaries: report.nonFiniteConsumerBoundaries.map(({ id, classification, attemptCount, stable }) => ({ id, classification, attemptCount, stable })),
|
||||
dependencyFailures: report.dependencyFailures.map(({ id, recomputeResult, afterRecompute }) => ({ id, recomputeResult, state: afterRecompute.state })),
|
||||
transactionRestored: report.transaction.restored,
|
||||
}, null, 2))
|
||||
Reference in New Issue
Block a user