Files
proffesor-for-testing__agen…/tests/benchmarks/integration-benchmark.ts
Profa 8bb2ad1aee feat(v2.6.6): Web-Tree-Sitter migration, MinCut analysis, CriticalPathDetector
## Web-Tree-Sitter Migration (Phases 1-5)
- New WebTreeSitterParser replacing native tree-sitter bindings
- Eliminates npm install warnings about native compilation
- Updated all language extractors (TS, JS, Python, Go, Rust)

## MinCut Analysis Integration (Phases 1-6)
- New src/code-intelligence/analysis/mincut/ module
- MinCutAnalyzer: Stoer-Wagner algorithm for graph partitioning
- GraphAdapter: Code graph to MinCut format conversion
- CircularDependencyDetector: Dependency cycle detection
- ModuleCouplingAnalyzer: Module coupling analysis

## CriticalPathDetector for Coverage Analysis
- New src/coverage/CriticalPathDetector.ts
- Identifies structurally critical code paths using MinCut
- Prioritizes coverage gaps by criticality score
- Integrated with CoverageAnalyzerAgent

## Fleet Topology Management
- New src/fleet/topology/ module
- MinCut-based test file partitioning
- Integrated with test-execute-parallel MCP handler

## Other Changes
- MCP tool count corrected: 102 → 105 tools
- New CLI commands: aqe kg mincut, aqe kg circular
- 19 new tests for CriticalPathDetector

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 11:02:28 +00:00

225 lines
9.7 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env npx tsx
/**
* Integration Test Benchmark
*
* Tests MinCut on integration tests which are more likely to have
* shared fixtures and interdependencies.
*/
import * as path from 'path';
import { glob } from 'fast-glob';
import {
MinCutPartitioner,
TestDependencyAnalyzer,
RealTestExecutor,
aggregateBatchResults,
TestFile,
} from '../../src/test/partition/index.js';
const CONFIG = {
workerCount: 4,
maxTests: 16, // Limit for benchmark speed
testPattern: 'tests/integration/**/*.test.ts',
excludePatterns: [
'**/node_modules/**',
'**/*.skip.test.ts',
'**/phase2/**', // Skip slow phase2 tests
],
};
async function discoverTestFiles(): Promise<string[]> {
console.log(`\n🔍 Discovering integration tests: ${CONFIG.testPattern}`);
const files = await glob(CONFIG.testPattern, {
cwd: process.cwd(),
ignore: CONFIG.excludePatterns,
absolute: false,
});
console.log(` Found ${files.length} integration test files`);
const limitedFiles = files.slice(0, CONFIG.maxTests);
if (files.length > CONFIG.maxTests) {
console.log(` (Limited to ${CONFIG.maxTests} for benchmark)`);
}
return limitedFiles;
}
async function analyzeRealDependencies(testFiles: string[]): Promise<TestFile[]> {
console.log(`\n📊 Analyzing REAL dependencies using ts-morph...`);
const analyzer = new TestDependencyAnalyzer('tests');
const analysis = await analyzer.analyzeTestDependencies(testFiles);
console.log(` Analysis time: ${analysis.analysisTimeMs.toFixed(2)}ms`);
console.log(` Shared fixtures found: ${analysis.sharedFixtures.length}`);
if (analysis.sharedFixtures.length > 0) {
console.log(` Fixtures: ${analysis.sharedFixtures.slice(0, 5).map(f => path.basename(f)).join(', ')}`);
}
const totalDeps = Array.from(analysis.dependencies.values())
.reduce((sum, deps) => sum + deps.length, 0);
console.log(` Total dependencies: ${totalDeps}`);
// Show dependency details
for (const [testPath, deps] of analysis.dependencies) {
if (deps.length > 0) {
console.log(` ${path.basename(testPath)} → ${deps.map(d => path.basename(d)).join(', ')}`);
}
}
return analyzer.toTestFiles(testFiles, analysis);
}
function roundRobinPartition(tests: TestFile[], k: number): TestFile[][] {
const partitions: TestFile[][] = Array.from({ length: k }, () => []);
tests.forEach((test, i) => {
partitions[i % k].push(test);
});
return partitions;
}
function countCrossPartitionDeps(partitions: TestFile[][]): number {
let count = 0;
const allPaths = new Set(partitions.flat().map(t => t.path));
for (const partition of partitions) {
const partitionPaths = new Set(partition.map(t => t.path));
for (const test of partition) {
for (const dep of [...test.dependencies, ...test.dependents]) {
if (!partitionPaths.has(dep) && allPaths.has(dep)) {
count++;
}
}
}
}
return Math.floor(count / 2);
}
async function runBenchmark() {
console.log('═'.repeat(60));
console.log(' Integration Test Benchmark');
console.log(' Testing MinCut on tests with shared fixtures');
console.log('═'.repeat(60));
const testFiles = await discoverTestFiles();
if (testFiles.length === 0) {
console.error('❌ No integration test files found!');
process.exit(1);
}
const testsWithDeps = await analyzeRealDependencies(testFiles);
// Check if we have any dependencies
const totalDeps = testsWithDeps.reduce((sum, t) => sum + t.dependencies.length, 0);
console.log(`\n📋 Dependency summary: ${totalDeps} total dependencies across ${testsWithDeps.length} tests`);
// Partition with MinCut
console.log(`\n🔀 Partitioning ${testsWithDeps.length} tests into ${CONFIG.workerCount} workers...`);
const partitioner = new MinCutPartitioner({ partitionCount: CONFIG.workerCount });
const mincutStart = performance.now();
const mincutResult = await partitioner.partition(testsWithDeps);
const mincutPartitionTime = performance.now() - mincutStart;
console.log(` MinCut algorithm: ${mincutResult.algorithm}`);
console.log(` Partition time: ${mincutPartitionTime.toFixed(2)}ms`);
console.log(` Cross-partition deps (MinCut): ${mincutResult.totalCrossPartitionDeps}`);
// Round-robin for comparison
const rrPartitions = roundRobinPartition(testsWithDeps, CONFIG.workerCount);
const rrCrossPartitionDeps = countCrossPartitionDeps(rrPartitions);
console.log(` Cross-partition deps (Round-Robin): ${rrCrossPartitionDeps}`);
// Show partition distribution
console.log('\n📦 MinCut Partition Distribution:');
for (let i = 0; i < mincutResult.partitions.length; i++) {
const p = mincutResult.partitions[i];
console.log(` Worker ${i}: ${p.tests.length} tests (${p.estimatedDuration.toFixed(0)}ms est)`);
p.tests.forEach(t => console.log(` - ${path.basename(t.path)}`));
}
console.log('\n📦 Round-Robin Partition Distribution:');
for (let i = 0; i < rrPartitions.length; i++) {
const p = rrPartitions[i];
const duration = p.reduce((sum, t) => sum + t.estimatedDuration, 0);
console.log(` Worker ${i}: ${p.length} tests (${duration.toFixed(0)}ms est)`);
p.forEach(t => console.log(` - ${path.basename(t.path)}`));
}
// Execute REAL tests
console.log('\n⏱️ Executing REAL integration tests...\n');
const executor = new RealTestExecutor({
timeout: 60000, // Integration tests may be slower
collectCoverage: false,
});
// MinCut execution
console.log('--- MinCut Execution ---');
const mincutBatches = mincutResult.partitions.map(p => p.tests.map(t => t.path));
const mincutExecStart = performance.now();
const mincutBatchResults = await executor.executeParallel(mincutBatches, (worker, result) => {
const status = result.passed ? '✅' : '❌';
console.log(` [W${worker}] ${status} ${path.basename(result.testFile)} (${result.duration.toFixed(0)}ms)`);
});
const mincutExecTime = performance.now() - mincutExecStart;
const mincutAgg = aggregateBatchResults(mincutBatchResults);
// Round-robin execution
console.log('\n--- Round-Robin Execution ---');
const rrBatches = rrPartitions.map(p => p.map(t => t.path));
const rrExecStart = performance.now();
const rrBatchResults = await executor.executeParallel(rrBatches, (worker, result) => {
const status = result.passed ? '✅' : '❌';
console.log(` [W${worker}] ${status} ${path.basename(result.testFile)} (${result.duration.toFixed(0)}ms)`);
});
const rrExecTime = performance.now() - rrExecStart;
const rrAgg = aggregateBatchResults(rrBatchResults);
// Results
console.log('\n' + '═'.repeat(60));
console.log(' RESULTS: Integration Test Comparison');
console.log('═'.repeat(60));
console.log('\n┌─────────────────────┬──────────────┬──────────────┐');
console.log('│ Metric │ MinCut │ Round-Robin │');
console.log('├─────────────────────┼──────────────┼──────────────┤');
console.log(`│ Tests executed │ ${String(mincutAgg.total).padStart(12)} │ ${String(rrAgg.total).padStart(12)} │`);
console.log(`│ Passed │ ${String(mincutAgg.passed).padStart(12)} │ ${String(rrAgg.passed).padStart(12)} │`);
console.log(`│ Failed │ ${String(mincutAgg.failed).padStart(12)} │ ${String(rrAgg.failed).padStart(12)} │`);
console.log(`│ Cross-partition deps│ ${String(mincutResult.totalCrossPartitionDeps).padStart(12)} │ ${String(rrCrossPartitionDeps).padStart(12)} │`);
console.log('├─────────────────────┼──────────────┼──────────────┤');
console.log(`│ Total test time │ ${(mincutAgg.totalDuration / 1000).toFixed(2).padStart(10)}s │ ${(rrAgg.totalDuration / 1000).toFixed(2).padStart(10)}s │`);
console.log(`│ Wall-clock time │ ${(mincutExecTime / 1000).toFixed(2).padStart(10)}s │ ${(rrExecTime / 1000).toFixed(2).padStart(10)}s │`);
console.log(`│ Max worker time │ ${(mincutAgg.maxWorkerDuration / 1000).toFixed(2).padStart(10)}s │ ${(rrAgg.maxWorkerDuration / 1000).toFixed(2).padStart(10)}s │`);
console.log(`│ Parallel speedup │ ${mincutAgg.parallelSpeedup.toFixed(2).padStart(10)}x │ ${rrAgg.parallelSpeedup.toFixed(2).padStart(10)}x │`);
console.log('└─────────────────────┴──────────────┴──────────────┘');
const wallClockImprovement = rrExecTime > 0 ? ((rrExecTime - mincutExecTime) / rrExecTime * 100) : 0;
const crossDepReduction = rrCrossPartitionDeps > 0
? ((rrCrossPartitionDeps - mincutResult.totalCrossPartitionDeps) / rrCrossPartitionDeps * 100)
: 0;
console.log('\n📈 IMPROVEMENT SUMMARY:');
console.log(` Cross-partition dependencies: ${crossDepReduction.toFixed(1)}% reduction`);
console.log(` Wall-clock time: ${wallClockImprovement.toFixed(1)}% ${wallClockImprovement > 0 ? 'faster' : 'slower'}`);
if (totalDeps === 0) {
console.log('\n⚠️ No test-to-test dependencies found.');
console.log(' Integration tests in this project are also well-isolated.');
console.log(' MinCut provides no benefit without inter-test dependencies.');
} else if (wallClockImprovement >= 10) {
console.log('\n✅ MinCut provides MEASURABLE improvement!');
} else if (wallClockImprovement > 0) {
console.log('\n⚠️ MinCut provides slight improvement.');
} else {
console.log('\n❌ No wall-clock improvement detected.');
}
console.log('\n' + '═'.repeat(60));
}
runBenchmark().catch(console.error);