Ctrl K
ComunidadGuías…

scripts/mutation-sweep.mjs

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import { appendFileSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';

export function selectSpecs(root, names = [], shard = '0/1') {
  const all = readdirSync(join(root, 'test/mutations')).filter(n => n.endsWith('.json'))
    .sort().map(n => `test/mutations/${n}`);
  if (!all.length) throw new Error('zero discovered mutation specs');
  const requested = names.length ? names.map(n => n.includes('/') ? n : `test/mutations/${n}`) : all;
  if (new Set(requested).size !== requested.length) throw new Error('duplicate spec selection');
  for (const name of requested) if (!all.includes(name)) throw new Error(`unknown spec: ${name}`);
  const match = /^(\d+)\/(\d+)$/.exec(shard);
  if (!match) throw new Error('shard must be zero-based i/N');
  const [index, total] = match.slice(1).map(Number);
  if (!Number.isSafeInteger(index) || !Number.isSafeInteger(total) || total < 1 || index >= total) {
    throw new Error('invalid shard index/total');
  }
  const selected = [...requested].sort().filter((_, i) => i % total === index);
  if (!selected.length) throw new Error('zero selected specs (including empty shards)');
  return { discovered: all.length, selected, shard };
}

export function verifyReport(directory) {
  const plan = JSON.parse(readFileSync(join(directory, 'manifest.json'), 'utf8'));
  const text = readFileSync(join(directory, 'verdicts.log'), 'utf8');
  if (!Array.isArray(plan.selected) || !plan.selected.length
      || new Set(plan.selected).size !== plan.selected.length) throw new Error('empty or duplicate manifest');
  const lines = text.split(/\r?\n/).filter(Boolean);
  const rows = lines.map(line => /^VERDICT (\S+) (OK|FAIL|UNTRUSTWORTHY) exit=(\d+)$/.exec(line));
  if (rows.some(row => !row)) throw new Error('malformed VERDICT record');
  const seen = rows.map(row => row[1]);
  if (rows.length !== plan.selected.length || new Set(seen).size !== plan.selected.length
      || seen.some(name => !plan.selected.includes(name))) {
    throw new Error(`VERDICT coverage mismatch: expected=${plan.selected.length} actual=${rows.length}`);
  }
  for (const row of rows) if ((row[2] === 'OK') !== (row[3] === '0')) throw new Error('inconsistent verdict/exit');
  const code = rows.some(row => row[2] === 'UNTRUSTWORTHY') ? 2 : rows.some(row => row[2] === 'FAIL') ? 1 : 0;
  return { expected: plan.selected.length, actual: rows.length, code };
}

export function runSweep(argv, root = process.cwd()) {
  let names = [], shard = '0/1', directory;
  for (let i = 0; i < argv.length; i++) {
    const value = () => { if (!argv[i + 1] || argv[i + 1].startsWith('--')) throw new Error(`missing value for ${argv[i]}`); return argv[++i]; };
    if (argv[i] === '--specs') {
      names = value().split(/[\s,]+/).filter(Boolean);
      if (!names.length) throw new Error('explicit spec selection is empty');
    }
    else if (argv[i] === '--shard') shard = value();
    else if (argv[i] === '--report-dir') directory = resolve(value());
    else if (argv[i] === '--verify' && argv.length === 2) {
      const result = verifyReport(resolve(value()));
      console.log(`VERDICT coverage: ${result.actual}/${result.expected}; exit=${result.code}`);
      return result.code;
    } else throw new Error(`unknown argument: ${argv[i]}`);
  }
  const plan = selectSpecs(root, names, shard);
  directory ??= mkdtempSync(join(tmpdir(), 'wa-mutation-sweep-'));
  const rel = relative(resolve(root), directory);
  if (rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))) {
    throw new Error('report directory must be outside the owner working tree');
  }
  mkdirSync(directory, { recursive: true });
  // Exclusive creation prevents a retry from reusing or overwriting old evidence.
  writeFileSync(join(directory, 'manifest.json'), JSON.stringify(plan, null, 2) + '\n', { flag: 'wx' });
  writeFileSync(join(directory, 'verdicts.log'), '', { flag: 'wx' });
  console.log(`Sweep: discovered=${plan.discovered} selected=${plan.selected.length} shard=${shard}`);
  console.log(`Report: ${directory}`);
  for (const spec of plan.selected) {
    const child = spawnSync(process.execPath, ['scripts/mutate-verify.mjs', spec], {
      cwd: root, encoding: 'utf8', env: process.env,
      maxBuffer: 128 * 1024 * 1024,
    });
    const output = `${child.stdout || ''}${child.stderr || ''}`;
    // Child output cannot masquerade as an authoritative sweep record.
    process.stdout.write(output.split(/\r?\n/).filter(Boolean).map(line => `  ${line}\n`).join(''));
    const code = child.error || child.signal || child.status === null ? 2 : child.status;
    const verdict = code === 0 ? 'OK' : code === 2 ? 'UNTRUSTWORTHY' : 'FAIL';
    if (child.error) console.error(`  spawn failure: ${child.error.message}`);
    const line = `VERDICT ${spec} ${verdict} exit=${code}\n`;
    appendFileSync(join(directory, 'verdicts.log'), line);
    process.stdout.write(line);
  }
  // Read the persisted records, not a loop counter: omissions and duplicates are failures.
  const result = verifyReport(directory);
  console.log(`VERDICT coverage: ${result.actual}/${result.expected}; exit=${result.code}`);
  return result.code;
}

if (resolve(process.argv[1] || '') === resolve(fileURLToPath(import.meta.url))) {
  try { process.exitCode = runSweep(process.argv.slice(2)); }
  catch (error) { console.error(`mutation sweep: ${error.message}`); process.exitCode = 2; }
}