import { spawnSync } from 'node:child_process' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { loadGoldenManifest, compareGoldenResult } from './freecad-golden-contract.mjs' const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') const args = new Set(process.argv.slice(2)) const scenarioArgument = process.argv.slice(2).find((argument) => argument.startsWith('--scenario=')) const manifestArgument = process.argv.slice(2).find((argument) => argument.startsWith('--manifest=')) const requestedScenario = scenarioArgument?.slice('--scenario='.length) const jsonOutput = args.has('--json') const allowMissing = args.has('--allow-missing') const manifest = await loadGoldenManifest(resolve(root, manifestArgument?.slice('--manifest='.length) || 'fixtures/freecad-golden/manifest.json')) const driver = resolve(root, manifest.driver) const localOracle = resolve(root, '.cache/freecad/install-native/bin/FreeCADCmd') const candidates = process.env.FREECAD_CMD ? [process.env.FREECAD_CMD] : [localOracle, 'FreeCADCmd', 'freecadcmd'] let command = null for (const candidate of candidates) { const probe = spawnSync(candidate, ['--version'], { encoding: 'utf8', timeout: 15000 }) if (!probe.error || probe.error.code !== 'ENOENT') { command = candidate; break } } if (!command) { const message = `FreeCADCmd 1.1.1 is unavailable. Set FREECAD_CMD to the locked desktop oracle executable.` if (allowMissing) { console.log(message); process.exit(0) } console.error(message) process.exit(2) } const scenarios = requestedScenario ? manifest.scenarios.filter((entry) => entry.id === requestedScenario) : manifest.scenarios if (scenarios.length === 0) { console.error(`Unknown golden scenario: ${requestedScenario}`); process.exit(2) } const reports = [] for (const entry of scenarios) { const execution = spawnSync(command, [driver], { cwd: root, encoding: 'utf8', timeout: 120000, maxBuffer: 10 * 1024 * 1024, env: { ...process.env, FREECAD_GOLDEN_SCENARIO: entry.file }, }) const output = `${execution.stdout || ''}\n${execution.stderr || ''}` const resultLine = output.split(/\r?\n/).find((line) => line.startsWith('FREECAD_GOLDEN_RESULT=')) if (execution.error || execution.status !== 0 || !resultLine) { reports.push({ id: entry.id, passed: false, differences: [`FreeCADCmd execution failed with status ${execution.status}: ${execution.error?.message || output.trim()}`] }) continue } let actual try { actual = JSON.parse(resultLine.slice('FREECAD_GOLDEN_RESULT='.length)) } catch (error) { reports.push({ id: entry.id, passed: false, differences: [`Invalid result JSON: ${error.message}`] }); continue } const differences = [] if (actual.fixtureId !== entry.id) differences.push(`fixtureId: expected ${entry.id}, received ${actual.fixtureId}`) if (actual.freecadVersion !== '1.1.1') differences.push(`freecadVersion: expected 1.1.1, received ${actual.freecadVersion}`) differences.push(...compareGoldenResult(entry.scenario, actual)) reports.push({ id: entry.id, passed: differences.length === 0, differences, actual }) } if (jsonOutput) console.log(JSON.stringify({ schemaVersion: 1, baselineId: manifest.baselineId, command, reports }, null, 2)) else for (const report of reports) { console.log(`${report.passed ? 'PASS' : 'FAIL'} ${report.id}`) report.differences.forEach((difference) => console.log(` ${difference}`)) } if (reports.some((report) => !report.passed)) process.exit(1)