codex-ipc-smoke.mjs 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import { spawn } from 'node:child_process';
  2. import fs from 'node:fs';
  3. import path from 'node:path';
  4. import { createRequire } from 'node:module';
  5. import { fileURLToPath } from 'node:url';
  6. /**
  7. * 在真实 Electron 里跑 IPC 探针:拉起应用(窗口加载 scripts/codex-ipc-probe.html),
  8. * 探针页逐条调用 controller/codexCtl/* 并写出结果,这里汇总打印。
  9. *
  10. * 用法:npm run smoke:ipc
  11. */
  12. const require = createRequire(import.meta.url);
  13. const here = path.dirname(fileURLToPath(import.meta.url));
  14. const appDir = path.join(here, '..');
  15. const probeFile = path.join(here, 'codex-ipc-probe.html');
  16. const outFile = path.join(here, 'codex-ipc-probe.json');
  17. const timeoutMs = Number(process.env.ZSJZ_PROBE_TIMEOUT || 180_000);
  18. fs.rmSync(outFile, { force: true });
  19. const electronPath = require('electron');
  20. const child = spawn(electronPath, ['.', '--env=local'], {
  21. cwd: appDir,
  22. stdio: 'inherit',
  23. env: {
  24. ...process.env,
  25. ZSJZ_CODEX_PROBE: `file:///${probeFile.replace(/\\/g, '/')}`,
  26. ZSJZ_CODEX_PROBE_OUT: outFile,
  27. },
  28. });
  29. let finished = false;
  30. const timer = setTimeout(() => {
  31. if (finished) return;
  32. finished = true;
  33. clearInterval(poll);
  34. console.error(`\n[smoke:ipc] 超时(${timeoutMs / 1000}s)未拿到探针结果,结束应用`);
  35. child.kill('SIGKILL');
  36. printAndExit(null, '探针超时未完成');
  37. }, timeoutMs);
  38. /**
  39. * 探针页跑完只写结果文件——渲染进程里的 process.exit() 关不掉主进程,
  40. * 所以这里轮询结果文件,拿到后再结束应用。
  41. */
  42. const poll = setInterval(() => {
  43. if (finished || !fs.existsSync(outFile)) return;
  44. setTimeout(() => {
  45. if (finished) return;
  46. finished = true;
  47. clearInterval(poll);
  48. clearTimeout(timer);
  49. let report = null;
  50. try {
  51. report = JSON.parse(fs.readFileSync(outFile, 'utf8'));
  52. } catch {
  53. // 结果文件损坏,按失败处理
  54. }
  55. child.kill('SIGKILL');
  56. printAndExit(report, report ? null : '探针结果文件无法解析');
  57. }, 800);
  58. }, 500);
  59. child.on('exit', (code) => {
  60. if (finished) return;
  61. finished = true;
  62. clearInterval(poll);
  63. clearTimeout(timer);
  64. let report = null;
  65. try {
  66. report = JSON.parse(fs.readFileSync(outFile, 'utf8'));
  67. } catch {
  68. // 探针没跑完就退出了
  69. }
  70. printAndExit(report, report ? null : `探针未写出结果(electron exit=${code})`);
  71. });
  72. function printAndExit(report, fatal) {
  73. if (!report) {
  74. console.error(`[smoke:ipc] ${fatal}`);
  75. process.exit(1);
  76. }
  77. for (const item of report.results) {
  78. const detail = JSON.stringify(item.detail ?? '');
  79. console.log(`${item.ok ? 'PASS' : 'FAIL'} ${item.name} ${detail.slice(0, 160)}`);
  80. }
  81. const failed = report.results.filter((item) => !item.ok);
  82. console.log(`\n[smoke:ipc] ${report.results.length - failed.length}/${report.results.length} 通过`);
  83. process.exit(failed.length === 0 ? 0 : 1);
  84. }