| 515 | } |
| 516 | |
| 517 | async function main() { |
| 518 | let examples = specificExample ? [specificExample] : findExamples(examplesDir); |
| 519 | let passes = 0; |
| 520 | let fails = 0; |
| 521 | let results = []; |
| 522 | |
| 523 | const reportPath = path.join(moddableDir, 'tools', 'test-examples', 'report.json'); |
| 524 | |
| 525 | if (isContinue && fs.existsSync(reportPath)) { |
| 526 | try { |
| 527 | const pastData = JSON.parse(fs.readFileSync(reportPath, 'utf8')); |
| 528 | if (Array.isArray(pastData)) { |
| 529 | results = pastData; |
| 530 | const completedPaths = new Set(results.map(r => r.path ? path.join(moddableDir, r.path) : '')); |
| 531 | examples = examples.filter(ex => !completedPaths.has(ex)); |
| 532 | passes = results.filter(r => r.success === true).length; |
| 533 | fails = results.filter(r => r.success === false).length; |
| 534 | let skips = results.filter(r => r.reason === 'NO_BASE_MANIFEST').length; |
| 535 | console.log(`[Test Harness] --continue flag set. Resuming from report.json.`); |
| 536 | console.log(`[Test Harness] Skipping ${results.length} previously tested examples (${passes} passed, ${fails} failed, ${skips} explicitly bypassed).\n`); |
| 537 | } |
| 538 | } catch { |
| 539 | console.warn(`[Test Harness] Failed to parse previous report.json. Starting fresh.`); |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | let initialPasses = passes; |
| 544 | let initialFails = fails; |
| 545 | let initialSkips = results.filter(r => r.reason === 'NO_BASE_MANIFEST').length; |
| 546 | |
| 547 | process.on('SIGINT', () => { |
| 548 | let currentSkips = results.filter(r => r.reason === 'NO_BASE_MANIFEST').length; |
| 549 | let completedThisRun = (passes - initialPasses) + (fails - initialFails) + (currentSkips - initialSkips); |
| 550 | let aborted = examples.length - completedThisRun; |
| 551 | console.log('\n\n====================================='); |
| 552 | console.log(`TEST RUN INTERRUPTED BY USER (^C)`); |
| 553 | console.log(`Total Passed: ${passes}, Total Failed: ${fails}, Bypassed: ${currentSkips}, Aborted Remaining: ${aborted}`); |
| 554 | console.log('====================================='); |
| 555 | fs.writeFileSync(path.join(moddableDir, 'tools', 'test-examples', 'report.json'), JSON.stringify(results, null, 2)); |
| 556 | |
| 557 | try { |
| 558 | require('child_process').execSync('killall -9 mcsim 2>/dev/null', { stdio: 'ignore', timeout: 2000 }); |
| 559 | require('child_process').execSync('pkill -9 -f xsbug-log 2>/dev/null', { stdio: 'ignore', timeout: 2000 }); |
| 560 | require('child_process').execSync('pkill -9 -f xsdb 2>/dev/null', { stdio: 'ignore', timeout: 2000 }); |
| 561 | } catch {} |
| 562 | |
| 563 | process.exit(1); |
| 564 | }); |
| 565 | |
| 566 | for (let ex of examples) { |
| 567 | const res = await runTest(ex); |
| 568 | const durationSecs = (res.durationMs / 1000).toFixed(1); |
| 569 | if (res.reason === 'NO_BASE_MANIFEST') { |
| 570 | console.log(`⏭️ SKIPPED - Target missing 'manifest_base.json' (likely a library).`); |
| 571 | } else if (res.success === true) { |
| 572 | console.log(`✅ PASS (${durationSecs}s)`); |
| 573 | passes++; |
| 574 | } else { |