feat: add intelligent test scheduler

This commit is contained in:
张翔
2026-03-13 11:31:53 +08:00
parent a3e7114349
commit dd1ea3f9a9
2 changed files with 141 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
import { TestHistoryManager } from './test-history';
interface TestSchedule {
testId: string;
file: string;
title: string;
priority: number;
estimatedDuration: number;
dependencies: string[];
}
export class TestScheduler {
private historyManager: TestHistoryManager;
constructor() {
this.historyManager = new TestHistoryManager();
}
scheduleTests(testFiles: string[]): TestSchedule[] {
const schedules: TestSchedule[] = [];
for (const file of testFiles) {
const testId = this.generateTestId(file);
const priority = this.calculatePriority(file);
const estimatedDuration = this.historyManager.getAverageDuration(testId) || 60000;
const dependencies = this.analyzeDependencies(file);
schedules.push({
testId,
file,
title: this.extractTestTitle(file),
priority,
estimatedDuration,
dependencies,
});
}
return schedules.sort((a, b) => {
if (a.priority !== b.priority) {
return a.priority - b.priority;
}
return a.estimatedDuration - b.estimatedDuration;
});
}
private generateTestId(file: string): string {
return file.replace(/[^a-zA-Z0-9]/g, '-');
}
private calculatePriority(file: string): number {
if (file.includes('smoke')) return 1;
if (file.includes('api')) return 2;
if (file.includes('admin')) return 3;
if (file.includes('regression')) return 4;
return 5;
}
private extractTestTitle(file: string): string {
const parts = file.split('/');
return parts[parts.length - 1].replace('.spec.ts', '');
}
private analyzeDependencies(file: string): string[] {
const dependencies: string[] = [];
if (file.includes('admin') && !file.includes('login')) {
dependencies.push('admin-login');
}
return dependencies;
}
optimizeExecutionOrder(schedules: TestSchedule[]): TestSchedule[] {
const optimized: TestSchedule[] = [];
const executed = new Set<string>();
const noDeps = schedules.filter(s => s.dependencies.length === 0);
optimized.push(...noDeps);
noDeps.forEach(s => executed.add(s.testId));
let remaining = schedules.filter(s => !executed.has(s.testId));
let iterations = 0;
while (remaining.length > 0 && iterations < 100) {
const canExecute = remaining.filter(s =>
s.dependencies.every(dep => executed.has(dep))
);
if (canExecute.length === 0) {
optimized.push(remaining[0]);
executed.add(remaining[0].testId);
remaining = remaining.slice(1);
} else {
optimized.push(...canExecute);
canExecute.forEach(s => executed.add(s.testId));
remaining = remaining.filter(s => !executed.has(s.testId));
}
iterations++;
}
return optimized;
}
}