From 85215413cbfd5f28a4448b0d649b9198458e00e8 Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Sat, 22 Jun 2024 12:33:37 -0400 Subject: [PATCH] [ci] Improve parallelism of yarn test This PR adds parallelism similar to our existing circleci setup for running yarn tests with the various test params. It does this by sharding tests into `$SHARD_COUNT` number of groups, then spawning a job for each of them and using jest's built in `--shard` option. Effectively this means that the job will spawn an additional (where `n` is the number of test params) `n * $SHARD_COUNT` number of jobs to run tests in parallel for a total of `n + (n * $SHARD_COUNT)` jobs. This does mean the GitHub UI at the bottom of each PR gets longer and unfortunately it's not sorted in any way as far as I can tell. But if something goes wrong it should still be easy to find out what the problem is. The PR also changes the `ci` argument for jest-cli to be an enum instead so the tests use all available workers in GitHub actions. This will have to live around for a bit until we can fully migrate off of circleci. ghstack-source-id: 08f2d16353aeca7cd15655d5ce2b7963a12507a7 Pull Request resolved: https://github.com/facebook/react/pull/30033 --- .circleci/config.yml | 6 +- .github/workflows/fuzz_tests.yml | 4 +- .github/workflows/runtime_test.yml | 84 +++++++++++++------ .../src/__tests__/ReactMultiChildText-test.js | 2 +- scripts/jest/jest-cli.js | 30 ++++--- 5 files changed, 81 insertions(+), 45 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 894e52fab609d..7ce9c199d4c7f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -281,7 +281,7 @@ jobs: at: . - setup_node_modules - run: ./scripts/circleci/download_devtools_regression_build.js << parameters.version >> --replaceBuild - - run: node ./scripts/jest/jest-cli.js --build --project devtools --release-channel=experimental --reactVersion << parameters.version >> --ci + - run: node ./scripts/jest/jest-cli.js --build --project devtools --release-channel=experimental --reactVersion << parameters.version >> --ci=circleci run_devtools_e2e_tests_for_versions: docker: *docker @@ -368,7 +368,7 @@ jobs: steps: - checkout - setup_node_modules - - run: yarn test <> --ci + - run: yarn test <> --ci=circleci yarn_test_build: docker: *docker @@ -382,7 +382,7 @@ jobs: - attach_workspace: at: . - setup_node_modules - - run: yarn test --build <> --ci + - run: yarn test --build <> --ci=circleci RELEASE_CHANNEL_stable_yarn_test_dom_fixtures: docker: *docker diff --git a/.github/workflows/fuzz_tests.yml b/.github/workflows/fuzz_tests.yml index 492dd7dc4c81d..4c4373be373ea 100644 --- a/.github/workflows/fuzz_tests.yml +++ b/.github/workflows/fuzz_tests.yml @@ -28,5 +28,5 @@ jobs: shell: bash - name: Run fuzz tests run: |- - FUZZ_TEST_SEED=$RANDOM yarn test fuzz --ci - FUZZ_TEST_SEED=$RANDOM yarn test --prod fuzz --ci + FUZZ_TEST_SEED=$RANDOM yarn test fuzz --ci=github + FUZZ_TEST_SEED=$RANDOM yarn test --prod fuzz --ci=github diff --git a/.github/workflows/runtime_test.yml b/.github/workflows/runtime_test.yml index 1acce045217ee..a9ec4dbc7acd1 100644 --- a/.github/workflows/runtime_test.yml +++ b/.github/workflows/runtime_test.yml @@ -7,37 +7,67 @@ on: paths-ignore: - 'compiler/**' +env: + # Number of workers (one per shard) to spawn + SHARD_COUNT: 5 + jobs: + # Define the various test parameters and parallelism for this workflow + build_test_params: + name: Build test params + runs-on: ubuntu-latest + outputs: + params: ${{ steps.define-params.outputs.result }} + shard_id: ${{ steps.define-shards.outputs.result }} + steps: + - uses: actions/github-script@v7 + id: define-shards + with: + script: | + function range(from, to) { + const arr = []; + for (let n = from; n <= to; n++) { + arr.push(n); + } + return arr; + } + return range(1, process.env.SHARD_COUNT); + - uses: actions/github-script@v7 + id: define-params + with: + script: | + return [ + "-r=stable --env=development", + "-r=stable --env=production", + "-r=experimental --env=development", + "-r=experimental --env=production", + "-r=www-classic --env=development --variant=false", + "-r=www-classic --env=production --variant=false", + "-r=www-classic --env=development --variant=true", + "-r=www-classic --env=production --variant=true", + "-r=www-modern --env=development --variant=false", + "-r=www-modern --env=production --variant=false", + "-r=www-modern --env=development --variant=true", + "-r=www-modern --env=production --variant=true", + "-r=xplat --env=development --variant=false", + "-r=xplat --env=development --variant=true", + "-r=xplat --env=production --variant=false", + "-r=xplat --env=production --variant=true", + // TODO: Test more persistent configurations? + "-r=stable --env=development --persistent", + "-r=experimental --env=development --persistent" + ]; + + # Spawn a job for each shard for a given set of test params test: - name: yarn test + name: yarn test ${{ matrix.params }} (Shard ${{ matrix.shard_id }}) runs-on: ubuntu-latest - continue-on-error: true + needs: build_test_params strategy: matrix: - # Intentionally passing these as strings instead of creating a - # separate parameter per CLI argument, since it's easier to - # control/see which combinations we want to run. - params: [ - "-r=stable --env=development", - "-r=stable --env=production", - "-r=experimental --env=development", - "-r=experimental --env=production", - "-r=www-classic --env=development --variant=false", - "-r=www-classic --env=production --variant=false", - "-r=www-classic --env=development --variant=true", - "-r=www-classic --env=production --variant=true", - "-r=www-modern --env=development --variant=false", - "-r=www-modern --env=production --variant=false", - "-r=www-modern --env=development --variant=true", - "-r=www-modern --env=production --variant=true", - "-r=xplat --env=development --variant=false", - "-r=xplat --env=development --variant=true", - "-r=xplat --env=production --variant=false", - "-r=xplat --env=production --variant=true", - # TODO: Test more persistent configurations? - "-r=stable --env=development --persistent", - "-r=experimental --env=development --persistent" - ] + params: ${{ fromJSON(needs.build_test_params.outputs.params) }} + shard_id: ${{ fromJSON(needs.build_test_params.outputs.shard_id) }} + continue-on-error: true steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -52,4 +82,4 @@ jobs: path: "**/node_modules" key: ${{ runner.arch }}-${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - run: yarn install --frozen-lockfile - - run: yarn test ${{ matrix.params }} --ci + - run: yarn test ${{ matrix.params }} --ci=github --shard=${{ matrix.shard_id }}/${{ env.SHARD_COUNT }} diff --git a/packages/react-dom/src/__tests__/ReactMultiChildText-test.js b/packages/react-dom/src/__tests__/ReactMultiChildText-test.js index c32d13d3b0151..5aeb321308090 100644 --- a/packages/react-dom/src/__tests__/ReactMultiChildText-test.js +++ b/packages/react-dom/src/__tests__/ReactMultiChildText-test.js @@ -77,7 +77,7 @@ const expectChildren = function (container, children) { * faster to render and update. */ describe('ReactMultiChildText', () => { - jest.setTimeout(20000); + jest.setTimeout(30000); it('should correctly handle all possible children for render and update', async () => { await expect(async () => { diff --git a/scripts/jest/jest-cli.js b/scripts/jest/jest-cli.js index 9c3be220fb645..70d4d04b2b456 100644 --- a/scripts/jest/jest-cli.js +++ b/scripts/jest/jest-cli.js @@ -91,8 +91,8 @@ const argv = yargs ci: { describe: 'Run tests in CI', requiresArg: false, - type: 'boolean', - default: false, + type: 'choices', + choices: ['circleci', 'github'], }, compactConsole: { alias: 'c', @@ -309,10 +309,14 @@ function getCommandArgs() { } // CI Environments have limited workers. - if (argv.ci) { + if (argv.ci === 'circleci') { args.push('--maxWorkers=2'); } + if (argv.ci === 'github') { + args.push('--maxConcurrency=10'); + } + // Push the remaining args onto the command. // This will send args like `--watch` to Jest. args.push(...argv._); @@ -364,16 +368,18 @@ function main() { const envars = getEnvars(); const env = Object.entries(envars).map(([k, v]) => `${k}=${v}`); - // Print the full command we're actually running. - const command = `$ ${env.join(' ')} node ${args.join(' ')}`; - console.log(chalk.dim(command)); + if (argv.ci !== 'github') { + // Print the full command we're actually running. + const command = `$ ${env.join(' ')} node ${args.join(' ')}`; + console.log(chalk.dim(command)); - // Print the release channel and project we're running for quick confirmation. - console.log( - chalk.blue( - `\nRunning tests for ${argv.project} (${argv.releaseChannel})...` - ) - ); + // Print the release channel and project we're running for quick confirmation. + console.log( + chalk.blue( + `\nRunning tests for ${argv.project} (${argv.releaseChannel})...` + ) + ); + } // Print a message that the debugger is starting just // for some extra feedback when running the debugger.