feat(test runner): workers as percentage (#17400)

Allows to set workers as a percentage of logical CPUs, for example
"50%".

Examples :
```bash
npx playwright test --workers 3
npx playwright test --workers 50%
```

```js
const config: PlaywrightTestConfig = {
  // ...
  workers: '33%',
}
```
This commit is contained in:
Jean-François Greffier 2022-09-21 20:17:36 +02:00 committed by GitHub
parent d2300674ef
commit a15fe50e7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 20 deletions

View File

@ -791,13 +791,13 @@ module.exports = config;
## property: TestConfig.workers
* since: v1.10
- type: ?<[int]>
- type: ?<[int]|[string]>
The maximum number of concurrent worker processes to use for parallelizing tests.
The maximum number of concurrent worker processes to use for parallelizing tests. Can also be set as percentage of logical CPU cores, e.g. `'50%'.`
Playwright Test uses worker processes to run tests. There is always at least one worker process, but more can be used to speed up test execution.
Defaults to one half of the number of CPU cores. Learn more about [parallelism and sharding](../test-parallel.md) with Playwright Test.
Defaults to half of the number of logical CPU cores. Learn more about [parallelism and sharding](../test-parallel.md) with Playwright Test.
```js tab=js-js
// playwright.config.js

View File

@ -516,7 +516,7 @@ In addition to configuring [Browser] or [BrowserContext], videos or screenshots,
- `testMatch`: Glob patterns or regular expressions that match test files. For example, `'**/todo-tests/*.spec.ts'`. By default, Playwright Test runs `.*(test|spec)\.(js|ts|mjs)` files.
- `timeout`: Time in milliseconds given to each test. Learn more about [various timeouts](./test-timeouts.md).
- `webServer: { command: string, port?: number, url?: string, ignoreHTTPSErrors?: boolean, timeout?: number, reuseExistingServer?: boolean, cwd?: string, env?: object }` - Launch a process and wait that it's ready before the tests will start. See [launch web server](./test-advanced.md#launching-a-development-web-server-during-the-tests) configuration for examples.
- `workers`: The maximum number of concurrent worker processes to use for parallelizing tests.
- `workers`: The maximum number of concurrent worker processes to use for parallelizing tests. Can also be set as percentage of logical CPU cores, e.g. `'50%'.`
You can specify these options in the configuration file. Note that testing options are **top-level**, do not put them into the `use` section.

View File

@ -100,7 +100,7 @@ function addTestCommand(program: Command) {
command.option('-gv, --grep-invert <grep>', `Only run tests that do not match this regular expression`);
command.option('--global-timeout <timeout>', `Maximum time this test suite can run in milliseconds (default: unlimited)`);
command.option('--ignore-snapshots', `Ignore screenshot and snapshot expectations`);
command.option('-j, --workers <workers>', `Number of concurrent workers, use 1 to run in a single worker (default: number of CPU cores / 2)`);
command.option('-j, --workers <workers>', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`);
command.option('--list', `Collect all the tests and report them, but do not run`);
command.option('--max-failures <N>', `Stop after the first N failures`);
command.option('--output <dir>', `Folder for output artifacts (default: "test-results")`);
@ -272,7 +272,7 @@ function overridesFromOptions(options: { [key: string]: any }): ConfigCLIOverrid
timeout: options.timeout ? parseInt(options.timeout, 10) : undefined,
ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : undefined,
updateSnapshots: options.updateSnapshots ? 'all' as const : undefined,
workers: options.workers ? parseInt(options.workers, 10) : undefined,
workers: options.workers,
};
}

View File

@ -29,7 +29,6 @@ import type { Reporter } from '../types/testReporter';
import { builtInReporters } from './runner';
import { isRegExp, calculateSha1 } from 'playwright-core/lib/utils';
import { serializeError } from './util';
import { hostPlatform } from 'playwright-core/lib/utils/hostPlatform';
import { FixturePool, isFixtureOption } from './fixtures';
import type { TestTypeImpl } from './testType';
@ -143,7 +142,19 @@ export class Loader {
this._fullConfig.shard = takeFirst(config.shard, baseFullConfig.shard);
this._fullConfig._ignoreSnapshots = takeFirst(config.ignoreSnapshots, baseFullConfig._ignoreSnapshots);
this._fullConfig.updateSnapshots = takeFirst(config.updateSnapshots, baseFullConfig.updateSnapshots);
this._fullConfig.workers = takeFirst(config.workers, baseFullConfig.workers);
const workers = takeFirst(config.workers, '50%');
if (typeof workers === 'string') {
if (workers.endsWith('%')) {
const cpus = os.cpus().length;
this._fullConfig.workers = Math.max(1, Math.floor(cpus * (parseInt(workers, 10) / 100)));
} else {
this._fullConfig.workers = parseInt(workers, 10);
}
} else {
this._fullConfig.workers = workers;
}
const webServers = takeFirst(config.webServer, baseFullConfig.webServer);
if (Array.isArray(webServers)) { // multiple web server mode
// Due to previous choices, this value shows up to the user in globalSetup as part of FullConfig. Arrays are not supported by the old type.
@ -572,8 +583,10 @@ function validateConfig(file: string, config: Config) {
}
if ('workers' in config && config.workers !== undefined) {
if (typeof config.workers !== 'number' || config.workers <= 0)
if (typeof config.workers === 'number' && config.workers <= 0)
throw errorWithFile(file, `config.workers must be a positive number`);
else if (typeof config.workers === 'string' && !config.workers.endsWith('%'))
throw errorWithFile(file, `config.workers must be a number or percentage`);
}
}
@ -631,9 +644,6 @@ function validateProject(file: string, project: Project, title: string) {
}
}
const cpus = os.cpus().length;
const workers = hostPlatform.startsWith('mac') && hostPlatform.endsWith('arm64') ? cpus : Math.ceil(cpus / 2);
export const baseFullConfig: FullConfigInternal = {
forbidOnly: false,
fullyParallel: false,
@ -654,7 +664,7 @@ export const baseFullConfig: FullConfigInternal = {
shard: null,
updateSnapshots: 'missing',
version: require('../package.json').version,
workers,
workers: 0,
webServer: null,
_watchMode: false,
_webServers: [],

View File

@ -936,13 +936,14 @@ interface TestConfig {
updateSnapshots?: "all"|"none"|"missing";
/**
* The maximum number of concurrent worker processes to use for parallelizing tests.
* The maximum number of concurrent worker processes to use for parallelizing tests. Can also be set as percentage of
* logical CPU cores, e.g. `'50%'.`
*
* Playwright Test uses worker processes to run tests. There is always at least one worker process, but more can be used to
* speed up test execution.
*
* Defaults to one half of the number of CPU cores. Learn more about [parallelism and sharding](https://playwright.dev/docs/test-parallel) with
* Playwright Test.
* Defaults to half of the number of logical CPU cores. Learn more about [parallelism and sharding](https://playwright.dev/docs/test-parallel)
* with Playwright Test.
*
* ```js
* // playwright.config.ts
@ -955,7 +956,7 @@ interface TestConfig {
* ```
*
*/
workers?: number;}
workers?: number|string;}
/**
* Playwright Test provides many options to configure how your tests are collected and executed, for example `timeout` or
@ -1210,13 +1211,14 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
*/
updateSnapshots: 'all' | 'none' | 'missing';
/**
* The maximum number of concurrent worker processes to use for parallelizing tests.
* The maximum number of concurrent worker processes to use for parallelizing tests. Can also be set as percentage of
* logical CPU cores, e.g. `'50%'.`
*
* Playwright Test uses worker processes to run tests. There is always at least one worker process, but more can be used to
* speed up test execution.
*
* Defaults to one half of the number of CPU cores. Learn more about [parallelism and sharding](https://playwright.dev/docs/test-parallel) with
* Playwright Test.
* Defaults to half of the number of logical CPU cores. Learn more about [parallelism and sharding](https://playwright.dev/docs/test-parallel)
* with Playwright Test.
*
* ```js
* // playwright.config.ts

View File

@ -393,6 +393,41 @@ test('should support ignoreSnapshots config option', async ({ runInlineTest }) =
expect(result.passed).toBe(1);
});
test('should validate workers option set to percent', async ({ runInlineTest }, testInfo) => {
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = {
workers: '50%'
};
`,
'a.test.ts': `
const { test } = pwt;
test('pass', async () => {
});
`
});
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
});
test('should throw when workers option is invalid', async ({ runInlineTest }) => {
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = {
workers: ''
};
`,
'a.test.ts': `
const { test } = pwt;
test('pass', async () => {
});
`
});
expect(result.exitCode).toBe(1);
expect(result.output).toContain('config.workers must be a number or percentage');
});
test('should work with undefined values and base', async ({ runInlineTest }) => {
const result = await runInlineTest({
'playwright.config.ts': `