fix(test runner): make obtainWorker() resolve with null when stopping (#8018)

This ensures that we properly exit from `Dispatcher.run()`, print
epilogue and set the right exit code.
This commit is contained in:
Dmitry Gozman 2021-08-05 15:00:00 -07:00 committed by GitHub
parent 7da669bcc4
commit d846c05619
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 4 deletions

View file

@ -71,11 +71,11 @@ export class Dispatcher {
const testGroup = this._queue.shift()!; const testGroup = this._queue.shift()!;
const requiredHash = testGroup.workerHash; const requiredHash = testGroup.workerHash;
let worker = await this._obtainWorker(testGroup); let worker = await this._obtainWorker(testGroup);
while (!this._isStopped && worker.hash && worker.hash !== requiredHash) { while (worker && worker.hash && worker.hash !== requiredHash) {
worker.stop(); worker.stop();
worker = await this._obtainWorker(testGroup); worker = await this._obtainWorker(testGroup);
} }
if (this._isStopped) if (this._isStopped || !worker)
break; break;
jobs.push(this._runJob(worker, testGroup)); jobs.push(this._runJob(worker, testGroup));
} }
@ -182,6 +182,8 @@ export class Dispatcher {
async _obtainWorker(testGroup: TestGroup) { async _obtainWorker(testGroup: TestGroup) {
const claimWorker = (): Promise<Worker> | null => { const claimWorker = (): Promise<Worker> | null => {
if (this._isStopped)
return null;
// Use available worker. // Use available worker.
if (this._freeWorkers.length) if (this._freeWorkers.length)
return Promise.resolve(this._freeWorkers.pop()!); return Promise.resolve(this._freeWorkers.pop()!);
@ -199,7 +201,7 @@ export class Dispatcher {
await new Promise<void>(f => this._workerClaimers.push(f)); await new Promise<void>(f => this._workerClaimers.push(f));
worker = claimWorker(); worker = claimWorker();
} }
return worker!; return worker;
} }
async _notifyWorkerClaimer() { async _notifyWorkerClaimer() {
@ -294,6 +296,8 @@ export class Dispatcher {
worker.stop(); worker.stop();
await result; await result;
} }
while (this._workerClaimers.length)
this._workerClaimers.shift()!();
} }
private _hasReachedMaxFailures() { private _hasReachedMaxFailures() {

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { test, expect } from './playwright-test-fixtures'; import { test, expect, stripAscii } from './playwright-test-fixtures';
test('max-failures should work', async ({ runInlineTest }) => { test('max-failures should work', async ({ runInlineTest }) => {
const result = await runInlineTest({ const result = await runInlineTest({
@ -112,3 +112,34 @@ test('max-failures should stop workers', async ({ runInlineTest }) => {
expect(result.output).toContain('%%interrupted'); expect(result.output).toContain('%%interrupted');
expect(result.output).not.toContain('%%skipped'); expect(result.output).not.toContain('%%skipped');
}); });
test('max-failures should properly shutdown', async ({ runInlineTest }) => {
const result = await runInlineTest({
'playwright.config.ts': `
const config = {
testDir: './',
maxFailures: 1,
};
export default config;
`,
'test1.spec.ts': `
const { test } = pwt;
test.describe('spec 1', () => {
test('test 1', async () => {
expect(false).toBeTruthy()
})
});
`,
'test2.spec.ts': `
const { test } = pwt;
test.describe('spec 2', () => {
test('test 2', () => {
expect(true).toBeTruthy()
})
});
`,
}, { workers: 1 });
expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1);
expect(stripAscii(result.output)).toContain('expect(false).toBeTruthy()');
});