claude-flow
Version:
Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)
520 lines (427 loc) • 20.2 kB
JavaScript
const { TestHarness } = require('../test-harness');
const assert = require('assert');
const os = require('os');
describe('Resource Usage Tests', () => {
let harness;
beforeEach(() => {
harness = new TestHarness();
});
afterEach(() => {
harness.reset();
if (global.gc) global.gc(); // Force garbage collection if available
});
describe('Memory Usage Patterns', () => {
it('should maintain stable memory usage during batch operations', async () => {
const iterations = 5;
const memorySnapshots = [];
for (let i = 0; i < iterations; i++) {
// Create and process files
const fileCount = 100;
for (let j = 0; j < fileCount; j++) {
harness.mockFS.set(`iteration${i}_file${j}.txt`, 'x'.repeat(10000)); // 10KB per file
}
const files = Array.from({ length: fileCount }, (_, j) => `iteration${i}_file${j}.txt`);
const beforeMemory = process.memoryUsage();
await harness.batchReadFiles(files);
const afterMemory = process.memoryUsage();
memorySnapshots.push({
iteration: i,
before: beforeMemory.heapUsed / 1024 / 1024, // MB
after: afterMemory.heapUsed / 1024 / 1024,
delta: (afterMemory.heapUsed - beforeMemory.heapUsed) / 1024 / 1024
});
// Clean up files from this iteration
files.forEach(f => harness.mockFS.delete(f));
}
console.log('\n=== Memory Stability Test ===');
console.log('Iteration | Before (MB) | After (MB) | Delta (MB)');
console.log('----------|-------------|------------|------------');
memorySnapshots.forEach(snapshot => {
console.log(`${snapshot.iteration.toString().padEnd(9)} | ${snapshot.before.toFixed(2).padEnd(11)} | ${snapshot.after.toFixed(2).padEnd(10)} | ${snapshot.delta > 0 ? '+' : ''}${snapshot.delta.toFixed(2)}`);
});
// Check for memory leaks (memory shouldn't grow significantly)
const firstSnapshot = memorySnapshots[0];
const lastSnapshot = memorySnapshots[memorySnapshots.length - 1];
const totalGrowth = lastSnapshot.after - firstSnapshot.before;
assert(totalGrowth < 50, `Memory grew by ${totalGrowth.toFixed(2)}MB, possible leak`);
});
it('should efficiently handle large data volumes', async () => {
const dataSizes = [
{ files: 10, sizeKB: 100 },
{ files: 50, sizeKB: 100 },
{ files: 100, sizeKB: 100 },
{ files: 10, sizeKB: 1000 },
{ files: 50, sizeKB: 1000 }
];
const results = [];
for (const config of dataSizes) {
// Create files
const content = 'x'.repeat(config.sizeKB * 1024);
for (let i = 0; i < config.files; i++) {
harness.mockFS.set(`large${i}.dat`, content);
}
const files = Array.from({ length: config.files }, (_, i) => `large${i}.dat`);
const totalDataMB = (config.files * config.sizeKB) / 1024;
const { result, metrics } = await harness.measureResourceUsage(async () => {
return await harness.batchReadFiles(files);
});
results.push({
files: config.files,
sizePerFile: config.sizeKB,
totalData: totalDataMB,
memoryUsed: metrics.memory.heapUsed / 1024 / 1024,
memoryEfficiency: totalDataMB / (metrics.memory.heapUsed / 1024 / 1024),
duration: metrics.duration,
throughputMBps: totalDataMB / (metrics.duration / 1000)
});
// Clean up
harness.reset();
}
console.log('\n=== Large Data Volume Handling ===');
console.log('Files | Size/File | Total Data | Memory Used | Efficiency | Throughput');
console.log('------|-----------|------------|-------------|------------|------------');
results.forEach(r => {
console.log(`${r.files.toString().padEnd(5)} | ${r.sizePerFile.toString().padEnd(9)}KB | ${r.totalData.toFixed(1).padEnd(10)}MB | ${r.memoryUsed.toFixed(1).padEnd(11)}MB | ${r.memoryEfficiency.toFixed(2).padEnd(10)} | ${r.throughputMBps.toFixed(1)}MB/s`);
});
// Verify memory efficiency
results.forEach(r => {
assert(r.memoryEfficiency > 0.5, `Poor memory efficiency: ${r.memoryEfficiency.toFixed(2)}`);
});
});
});
describe('CPU Usage Optimization', () => {
it('should distribute CPU load effectively across workers', async () => {
const cpuIntensiveTasks = Array.from({ length: 20 }, (_, i) => async () => {
// Simulate CPU-intensive work
let result = 0;
const iterations = 1000000;
for (let j = 0; j < iterations; j++) {
result += Math.sqrt(j) * Math.sin(j);
}
return { task: i, result: result > 0 };
});
// Test different concurrency levels
const concurrencyLevels = [1, 2, 4, 8, os.cpus().length];
const cpuResults = [];
for (const concurrency of concurrencyLevels) {
harness.concurrencyLimit = concurrency;
const startCPU = process.cpuUsage();
const startTime = Date.now();
await harness.executeBatch(cpuIntensiveTasks, async (task) => await task());
const endTime = Date.now();
const endCPU = process.cpuUsage(startCPU);
const wallTime = endTime - startTime;
const cpuTime = (endCPU.user + endCPU.system) / 1000; // Convert to ms
const cpuEfficiency = cpuTime / (wallTime * concurrency);
cpuResults.push({
concurrency,
wallTime,
cpuTime,
cpuEfficiency,
parallelEfficiency: wallTime * concurrency / (wallTime * concurrencyLevels[0])
});
}
console.log('\n=== CPU Load Distribution ===');
console.log('Concurrency | Wall Time | CPU Time | CPU Efficiency | Parallel Efficiency');
console.log('------------|-----------|----------|----------------|-------------------');
cpuResults.forEach(r => {
console.log(`${r.concurrency.toString().padEnd(11)} | ${r.wallTime.toString().padEnd(9)}ms | ${r.cpuTime.toFixed(0).padEnd(8)}ms | ${(r.cpuEfficiency * 100).toFixed(1).padEnd(14)}% | ${(r.parallelEfficiency * 100).toFixed(1)}%`);
});
// Verify CPU is being utilized effectively
const optimalConcurrency = cpuResults.find(r => r.concurrency === os.cpus().length);
assert(optimalConcurrency.cpuEfficiency > 0.5, `Low CPU efficiency: ${(optimalConcurrency.cpuEfficiency * 100).toFixed(1)}%`);
});
it('should handle mixed IO and CPU workloads efficiently', async () => {
const mixedTasks = Array.from({ length: 30 }, (_, i) => {
const taskType = i % 3 === 0 ? 'io' : i % 3 === 1 ? 'cpu' : 'mixed';
return async () => {
if (taskType === 'io') {
// IO-bound task
await harness.simulateDelay(50);
const data = await harness.mockReadFile('package.json');
return { task: i, type: 'io', dataSize: data.length };
} else if (taskType === 'cpu') {
// CPU-bound task
let result = 0;
for (let j = 0; j < 500000; j++) {
result += Math.sqrt(j);
}
return { task: i, type: 'cpu', result: result > 0 };
} else {
// Mixed task
await harness.simulateDelay(20);
let result = 0;
for (let j = 0; j < 250000; j++) {
result += Math.sqrt(j);
}
return { task: i, type: 'mixed', result: result > 0 };
}
};
});
// Add test file
harness.mockFS.set('package.json', JSON.stringify({ name: 'test', version: '1.0.0' }));
const { result, metrics } = await harness.measureResourceUsage(async () => {
harness.concurrencyLimit = os.cpus().length;
return await harness.executeBatch(mixedTasks, async (task) => await task());
});
const taskTypes = result.successful.reduce((acc, r) => {
acc[r.type] = (acc[r.type] || 0) + 1;
return acc;
}, {});
console.log('\n=== Mixed Workload Performance ===');
console.log(`Total tasks: ${mixedTasks.length}`);
console.log(`Task distribution: IO=${taskTypes.io}, CPU=${taskTypes.cpu}, Mixed=${taskTypes.mixed}`);
console.log(`Total duration: ${metrics.duration.toFixed(2)}ms`);
console.log(`CPU time: ${(metrics.cpu.user + metrics.cpu.system).toFixed(2)}ms`);
console.log(`Memory delta: ${(metrics.memory.heapUsed / 1024 / 1024).toFixed(2)}MB`);
console.log(`Throughput: ${(mixedTasks.length / (metrics.duration / 1000)).toFixed(2)} tasks/s`);
// Verify efficient handling
const throughput = mixedTasks.length / (metrics.duration / 1000);
assert(throughput > 50, `Low throughput for mixed workload: ${throughput.toFixed(2)} tasks/s`);
});
});
describe('Resource Limits and Constraints', () => {
it('should respect memory constraints during batch operations', async () => {
// Simulate memory-constrained environment
const memoryLimit = 100; // MB
const fileSize = 5; // MB per file
const totalFiles = 50;
// Create large files
const largeContent = 'x'.repeat(fileSize * 1024 * 1024);
for (let i = 0; i < totalFiles; i++) {
harness.mockFS.set(`constrained${i}.dat`, largeContent);
}
const files = Array.from({ length: totalFiles }, (_, i) => `constrained${i}.dat`);
// Process in batches to stay within memory limit
const batchSize = Math.floor(memoryLimit / fileSize / 2); // Safety factor of 2
const batches = [];
for (let i = 0; i < files.length; i += batchSize) {
batches.push(files.slice(i, i + batchSize));
}
console.log('\n=== Memory-Constrained Processing ===');
console.log(`Memory limit: ${memoryLimit}MB`);
console.log(`File size: ${fileSize}MB`);
console.log(`Total files: ${totalFiles}`);
console.log(`Batch size: ${batchSize} files`);
console.log(`Total batches: ${batches.length}`);
const batchResults = [];
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
const beforeMemory = process.memoryUsage();
const result = await harness.batchReadFiles(batch);
const afterMemory = process.memoryUsage();
const memoryUsed = (afterMemory.heapUsed - beforeMemory.heapUsed) / 1024 / 1024;
batchResults.push({
batch: i + 1,
filesProcessed: batch.length,
memoryUsed,
withinLimit: memoryUsed < memoryLimit
});
// Simulate cleanup between batches
if (global.gc) global.gc();
}
console.log('\nBatch | Files | Memory Used | Within Limit');
console.log('------|-------|-------------|-------------');
batchResults.forEach(r => {
console.log(`${r.batch.toString().padEnd(5)} | ${r.filesProcessed.toString().padEnd(5)} | ${r.memoryUsed.toFixed(2).padEnd(11)}MB | ${r.withinLimit ? 'YES' : 'NO'}`);
});
// Verify all batches stayed within memory limit
assert(batchResults.every(r => r.withinLimit), 'Some batches exceeded memory limit');
});
it('should handle resource exhaustion gracefully', async () => {
// Simulate resource exhaustion scenarios
const scenarios = [
{
name: 'High concurrency',
concurrency: 100,
tasks: 200,
expectedBehavior: 'throttle'
},
{
name: 'Large data volume',
concurrency: 10,
tasks: 50,
dataSize: 10000000, // 10MB per task
expectedBehavior: 'batch'
},
{
name: 'CPU intensive',
concurrency: os.cpus().length * 2,
tasks: 50,
cpuIntensive: true,
expectedBehavior: 'queue'
}
];
const scenarioResults = [];
for (const scenario of scenarios) {
harness.concurrencyLimit = scenario.concurrency;
const tasks = Array.from({ length: scenario.tasks }, (_, i) => async () => {
if (scenario.cpuIntensive) {
// CPU-intensive work
let result = 0;
for (let j = 0; j < 1000000; j++) {
result += Math.sqrt(j);
}
return { task: i, result };
} else if (scenario.dataSize) {
// Memory-intensive work
const data = 'x'.repeat(scenario.dataSize);
return { task: i, processed: data.length };
} else {
// Regular task
await harness.simulateDelay(10);
return { task: i };
}
});
const startTime = Date.now();
const startMemory = process.memoryUsage();
try {
const result = await harness.executeBatch(tasks, async (task) => await task());
const endTime = Date.now();
const endMemory = process.memoryUsage();
scenarioResults.push({
scenario: scenario.name,
success: true,
duration: endTime - startTime,
memoryDelta: (endMemory.heapUsed - startMemory.heapUsed) / 1024 / 1024,
throughput: scenario.tasks / ((endTime - startTime) / 1000),
successRate: result.successRate
});
} catch (error) {
scenarioResults.push({
scenario: scenario.name,
success: false,
error: error.message
});
}
}
console.log('\n=== Resource Exhaustion Handling ===');
console.log('Scenario | Success | Duration | Memory Δ | Throughput | Success Rate');
console.log('-----------------|---------|----------|----------|------------|-------------');
scenarioResults.forEach(r => {
if (r.success) {
console.log(`${r.scenario.padEnd(16)} | YES | ${r.duration.toString().padEnd(8)}ms | ${r.memoryDelta.toFixed(1).padEnd(8)}MB | ${r.throughput.toFixed(1).padEnd(10)} | ${(r.successRate * 100).toFixed(1)}%`);
} else {
console.log(`${r.scenario.padEnd(16)} | NO | - | - | - | Error: ${r.error}`);
}
});
// All scenarios should complete successfully
assert(scenarioResults.every(r => r.success), 'Some scenarios failed to handle resource constraints');
});
});
describe('Resource Monitoring', () => {
it('should track resource usage over time', async () => {
const duration = 5000; // 5 seconds
const sampleInterval = 500; // Sample every 500ms
const samples = [];
// Start background tasks
const backgroundTasks = Array.from({ length: 100 }, (_, i) => async () => {
const delay = Math.random() * 200 + 50;
await harness.simulateDelay(delay);
// Some tasks do more work
if (i % 5 === 0) {
let result = 0;
for (let j = 0; j < 100000; j++) {
result += Math.sqrt(j);
}
}
return { task: i, completed: true };
});
// Monitor resources while executing tasks
const monitoring = setInterval(() => {
const usage = process.memoryUsage();
const cpuUsage = process.cpuUsage();
samples.push({
timestamp: Date.now(),
memory: {
heapUsed: usage.heapUsed / 1024 / 1024,
heapTotal: usage.heapTotal / 1024 / 1024,
external: usage.external / 1024 / 1024,
rss: usage.rss / 1024 / 1024
},
cpu: cpuUsage
});
}, sampleInterval);
const startTime = Date.now();
harness.concurrencyLimit = 5;
await harness.executeBatch(backgroundTasks, async (task) => await task());
clearInterval(monitoring);
const endTime = Date.now();
// Calculate statistics
const memoryStats = {
min: Math.min(...samples.map(s => s.memory.heapUsed)),
max: Math.max(...samples.map(s => s.memory.heapUsed)),
avg: samples.reduce((sum, s) => sum + s.memory.heapUsed, 0) / samples.length
};
console.log('\n=== Resource Usage Over Time ===');
console.log(`Monitoring duration: ${endTime - startTime}ms`);
console.log(`Samples collected: ${samples.length}`);
console.log(`\nMemory Usage (MB):`);
console.log(` Min: ${memoryStats.min.toFixed(2)}`);
console.log(` Max: ${memoryStats.max.toFixed(2)}`);
console.log(` Avg: ${memoryStats.avg.toFixed(2)}`);
console.log(` Range: ${(memoryStats.max - memoryStats.min).toFixed(2)}`);
// Verify resource usage stayed reasonable
assert(memoryStats.max - memoryStats.min < 100, 'Memory usage fluctuated too much');
assert(memoryStats.avg < 200, 'Average memory usage too high');
});
it('should provide resource usage predictions', async () => {
// Test resource usage with different input sizes
const testSizes = [10, 25, 50, 100, 200];
const measurements = [];
for (const size of testSizes) {
// Create tasks of varying complexity
const tasks = Array.from({ length: size }, (_, i) => async () => {
const dataSize = 1000 * (i % 10 + 1); // 1-10KB
const data = 'x'.repeat(dataSize);
await harness.simulateDelay(10);
return {
task: i,
processed: data.length,
result: data.substring(0, 10)
};
});
const { result, metrics } = await harness.measureResourceUsage(async () => {
harness.concurrencyLimit = 10;
return await harness.executeBatch(tasks, async (task) => await task());
});
measurements.push({
inputSize: size,
duration: metrics.duration,
memory: metrics.memory.heapUsed / 1024 / 1024,
throughput: size / (metrics.duration / 1000)
});
harness.reset();
}
// Calculate regression for predictions
const n = measurements.length;
const sumX = measurements.reduce((sum, m) => sum + m.inputSize, 0);
const sumY = measurements.reduce((sum, m) => sum + m.duration, 0);
const sumXY = measurements.reduce((sum, m) => sum + m.inputSize * m.duration, 0);
const sumX2 = measurements.reduce((sum, m) => sum + m.inputSize * m.inputSize, 0);
const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
const intercept = (sumY - slope * sumX) / n;
console.log('\n=== Resource Usage Scaling ===');
console.log('Input Size | Duration (ms) | Memory (MB) | Throughput');
console.log('-----------|---------------|-------------|------------');
measurements.forEach(m => {
console.log(`${m.inputSize.toString().padEnd(10)} | ${m.duration.toFixed(2).padEnd(13)} | ${m.memory.toFixed(2).padEnd(11)} | ${m.throughput.toFixed(2)}`);
});
console.log(`\nLinear regression: Duration = ${slope.toFixed(2)} * InputSize + ${intercept.toFixed(2)}`);
// Predict for larger sizes
const predictions = [500, 1000];
console.log('\nPredictions:');
predictions.forEach(size => {
const predictedDuration = slope * size + intercept;
console.log(` ${size} items: ~${predictedDuration.toFixed(0)}ms`);
});
// Verify linear scaling
const r2 = measurements.reduce((sum, m) => {
const predicted = slope * m.inputSize + intercept;
const error = m.duration - predicted;
return sum + error * error;
}, 0);
assert(r2 < 100000, 'Resource usage not scaling linearly');
});
});
});