Skip to content

Commit c20e94a

Browse files
authored
Support retrying failed tests from previous build (#15)
1 parent 39fdf8f commit c20e94a

File tree

8 files changed

+71
-9
lines changed

8 files changed

+71
-9
lines changed

javascript/dist/queue/BaseRunner.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,14 @@ export declare class BaseRunner {
332332
recordFailedTest(testName: string, testSuite: string): Promise<void>;
333333
recordPassingTest(testName: string, testSuite: string): Promise<void>;
334334
getFailedTests(): Promise<string>;
335+
getFailedTestNamesFromPreviousBuild(): Promise<string[]>;
335336
waitForMaster(): Promise<void>;
336337
getMasterStatus(): Promise<string | null>;
337338
isInitialized(): Promise<boolean>;
338339
size(): Promise<number>;
339340
progress(): Promise<number>;
340341
toArray(): Promise<string[]>;
341342
key(...args: string[]): string;
343+
retriedBuildKey(...args: string[]): string;
342344
private createRedisScripts;
343345
}

javascript/dist/queue/BaseRunner.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ class BaseRunner {
5858
const failures = Object.values(failedTests).map(test => JSON.parse(test));
5959
return JSON.stringify(failures);
6060
}
61+
async getFailedTestNamesFromPreviousBuild() {
62+
const previousBuildId = this.config.retriedBuildId;
63+
if (!previousBuildId) {
64+
return [];
65+
}
66+
const failedTests = await this.client.hGetAll(this.retriedBuildKey('error-reports'));
67+
const failures = Object.values(failedTests).map(test => JSON.parse(test).test_name);
68+
return failures;
69+
}
6170
async waitForMaster() {
6271
if (this.isMaster) {
6372
return;
@@ -115,6 +124,13 @@ class BaseRunner {
115124
const uniqueID = this.config.namespace ? `${this.config.namespace}:#${this.config.buildId}` : this.config.buildId;
116125
return ['build', uniqueID, ...args].join(':');
117126
}
127+
retriedBuildKey(...args) {
128+
if (!Array.isArray(args)) {
129+
args = [args];
130+
}
131+
const uniqueID = this.config.namespace ? `${this.config.namespace}:#${this.config.retriedBuildId}` : this.config.retriedBuildId;
132+
return ['build', uniqueID, ...args].join(':');
133+
}
118134
createRedisScripts() {
119135
return {
120136
acknowledge: (0, redis_1.defineScript)({

javascript/dist/queue/Configuration.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type InitConfig = {
1111
inactiveWorkersTimeout?: number;
1212
namespace?: string;
1313
failureFile?: string;
14+
retriedBuildId?: string;
1415
};
1516
export declare class Configuration {
1617
buildId: string;
@@ -25,7 +26,8 @@ export declare class Configuration {
2526
inactiveWorkersTimeout: number;
2627
namespace?: string;
2728
failureFile?: string;
28-
constructor({ buildId, workerId, seed, redisTTL, maxRequeues, requeueTolerance, maxTestsAllowedToFail, timeout, reportTimeout, inactiveWorkersTimeout, namespace, failureFile, }: {
29+
retriedBuildId?: string;
30+
constructor({ buildId, workerId, seed, redisTTL, maxRequeues, requeueTolerance, maxTestsAllowedToFail, timeout, reportTimeout, inactiveWorkersTimeout, namespace, failureFile, retriedBuildId, }: {
2931
buildId: string;
3032
workerId: string;
3133
seed?: string;
@@ -38,6 +40,7 @@ export declare class Configuration {
3840
inactiveWorkersTimeout?: number;
3941
namespace?: string;
4042
failureFile?: string;
43+
retriedBuildId?: string;
4144
});
4245
static fromEnv(): any;
4346
globalMaxRequeues(testCount: number): number;

javascript/dist/queue/Configuration.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Object.defineProperty(exports, "__esModule", { value: true });
33
exports.Configuration = void 0;
44
class Configuration {
5-
constructor({ buildId, workerId, seed, redisTTL, maxRequeues, requeueTolerance, maxTestsAllowedToFail, timeout, reportTimeout, inactiveWorkersTimeout, namespace, failureFile, }) {
5+
constructor({ buildId, workerId, seed, redisTTL, maxRequeues, requeueTolerance, maxTestsAllowedToFail, timeout, reportTimeout, inactiveWorkersTimeout, namespace, failureFile, retriedBuildId, }) {
66
this.buildId = buildId;
77
this.workerId = workerId;
88
this.seed = seed;
@@ -15,6 +15,7 @@ class Configuration {
1515
this.inactiveWorkersTimeout = inactiveWorkersTimeout ?? this.timeout;
1616
this.namespace = namespace;
1717
this.failureFile = failureFile;
18+
this.retriedBuildId = retriedBuildId;
1819
}
1920
static fromEnv() {
2021
const buildId = process.env['CIRCLE_BUILD_URL'] ||

javascript/dist/queue/Worker.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,18 @@ class Worker extends BaseRunner_1.BaseRunner {
6161
await this.client.release(this.key('running'), this.key('worker', this.config.workerId, 'queue'), this.key('owners'));
6262
}
6363
async populate(tests, seed) {
64-
if (seed !== undefined) {
65-
tests = (0, utils_1.shuffleArray)(tests, seed);
64+
if (this.config.retriedBuildId) {
65+
console.log(`[ci-queue] Retrying failed tests for build ${this.config.retriedBuildId}`);
66+
const failedTests = await this.getFailedTestNamesFromPreviousBuild();
67+
console.log(`[ci-queue] Failed tests: ${failedTests}`);
68+
await this.push(failedTests);
69+
}
70+
else {
71+
if (seed !== undefined) {
72+
tests = (0, utils_1.shuffleArray)(tests, seed);
73+
}
74+
await this.push(tests);
6675
}
67-
await this.push(tests);
6876
}
6977
shutdown() {
7078
this.shutdownRequired = true;

javascript/src/queue/BaseRunner.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ export class BaseRunner {
7575
return JSON.stringify(failures);
7676
}
7777

78+
async getFailedTestNamesFromPreviousBuild(): Promise<string[]> {
79+
const previousBuildId = this.config.retriedBuildId;
80+
if (!previousBuildId) {
81+
return [];
82+
}
83+
84+
const failedTests = await this.client.hGetAll(this.retriedBuildKey('error-reports'));
85+
const failures = Object.values(failedTests).map(test => JSON.parse(test).test_name);
86+
87+
return failures;
88+
}
89+
7890
async waitForMaster(): Promise<void> {
7991
if (this.isMaster) {
8092
return;
@@ -142,6 +154,14 @@ export class BaseRunner {
142154
return ['build', uniqueID, ...args].join(':');
143155
}
144156

157+
retriedBuildKey(...args: string[]): string {
158+
if (!Array.isArray(args)) {
159+
args = [args];
160+
}
161+
const uniqueID = this.config.namespace ? `${this.config.namespace}:#${this.config.retriedBuildId}` : this.config.retriedBuildId;
162+
return ['build', uniqueID, ...args].join(':');
163+
}
164+
145165
private createRedisScripts() {
146166
return {
147167
acknowledge: defineScript({

javascript/src/queue/Configuration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type InitConfig = {
1111
inactiveWorkersTimeout?: number;
1212
namespace?: string;
1313
failureFile?: string;
14+
retriedBuildId?: string;
1415
};
1516

1617
export class Configuration {
@@ -26,6 +27,7 @@ export class Configuration {
2627
inactiveWorkersTimeout: number;
2728
namespace?: string;
2829
failureFile?: string;
30+
retriedBuildId?: string;
2931
constructor({
3032
buildId,
3133
workerId,
@@ -39,6 +41,7 @@ export class Configuration {
3941
inactiveWorkersTimeout,
4042
namespace,
4143
failureFile,
44+
retriedBuildId,
4245
}: {
4346
buildId: string;
4447
workerId: string;
@@ -52,6 +55,7 @@ export class Configuration {
5255
inactiveWorkersTimeout?: number;
5356
namespace?: string;
5457
failureFile?: string;
58+
retriedBuildId?: string;
5559
}) {
5660
this.buildId = buildId;
5761
this.workerId = workerId;
@@ -65,6 +69,7 @@ export class Configuration {
6569
this.inactiveWorkersTimeout = inactiveWorkersTimeout ?? this.timeout;
6670
this.namespace = namespace;
6771
this.failureFile = failureFile;
72+
this.retriedBuildId = retriedBuildId;
6873
}
6974

7075
static fromEnv() {

javascript/src/queue/Worker.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,17 @@ export class Worker extends BaseRunner {
9292
}
9393

9494
async populate(tests: string[], seed?: number) {
95-
if (seed !== undefined) {
96-
tests = shuffleArray(tests, seed);
97-
}
98-
await this.push(tests);
95+
if (this.config.retriedBuildId) {
96+
console.log(`[ci-queue] Retrying failed tests for build ${this.config.retriedBuildId}`);
97+
const failedTests = await this.getFailedTestNamesFromPreviousBuild();
98+
console.log(`[ci-queue] Failed tests: ${failedTests}`);
99+
await this.push(failedTests);
100+
} else {
101+
if (seed !== undefined) {
102+
tests = shuffleArray(tests, seed);
103+
}
104+
await this.push(tests);
105+
}
99106
}
100107

101108
shutdown() {

0 commit comments

Comments
 (0)