diff --git a/.cirrus.yml b/.cirrus.yml index 9eb90deced8..3b380f30695 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,18 +1,41 @@ -container: - image: gcr.io/flutter-cirrus/build-flutter-image:latest +# CIRRUS CONFIGURATION FILE +use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' + +environment: + # For details about environment variables used in Cirrus, including how encrypted variables work, + # see https://cirrus-ci.org/guide/writing-tasks/#environment-variables + GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] + # We change Flutter's directory to include a space in its name (see $CIRRUS_WORKING_DIR) so that + # we constantly test path names with spaces in them. The FLUTTER_SDK_PATH_WITH_SPACE variable must + # therefore have a space in it. + FLUTTER_SDK_PATH_WITH_SPACE: "flutter sdk" + # We force BOT to true so that all our tools know we're in a CI environment. This avoids any + # dependency on precisely how Cirrus is detected by our tools. + BOT: "true" + # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they might include non-ASCII + # characters which makes Gradle crash. See: https://github.com/flutter/flutter/issues/24935 + # TODO(amirha): remove once we've migrated to newer Gradle + CIRRUS_CHANGE_MESSAGE: "" + CIRRUS_COMMIT_MESSAGE: "" + +# LINUX SHARDS task: - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - env: - # Name the SDK directory to include a space so that we constantly - # test path names with spaces in them. - CIRRUS_WORKING_DIR: "/tmp/flutter sdk" + container: + image: gcr.io/flutter-cirrus/build-flutter-image:latest + cpu: $CPU + memory: $MEMORY + environment: + # We shrink our default resource requirement as much as possible because that way we are more + # likely to get scheduled. We require 4G of RAM because most of the shards (all but one as of + # October 2019) just get OOM-killed with less. Some shards may need more. When increasing the + # requirements for select shards, please leave a comment on those shards saying when you + # increased the requirements, what numbers you tried, and what the results were. + CPU: 1 # 0.1-8 without compute credits, 0.1-30 with (yes, you can go fractional) + MEMORY: 4G # 256M-24G without compute credits, 256M-90G with + CIRRUS_WORKING_DIR: "/tmp/$FLUTTER_SDK_PATH_WITH_SPACE" PATH: "$CIRRUS_WORKING_DIR/bin:$CIRRUS_WORKING_DIR/bin/cache/dart-sdk/bin:$PATH" ANDROID_SDK_ROOT: "/opt/android_sdk" - git_fetch_script: - - git clean -xfd - - git fetch origin - - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work pub_cache: folder: $HOME/.pub-cache fingerprint_script: echo $OS; grep -r --include=pubspec.yaml 'PUBSPEC CHECKSUM' "$CIRRUS_WORKING_DIR" @@ -22,345 +45,302 @@ task: artifacts_cache: folder: bin/cache/artifacts fingerprint_script: echo $OS; cat bin/internal/*.version - setup_script: ./dev/bots/cirrus_setup.sh + setup_script: + - date + - git clean -xffd + - git fetch origin + - git fetch origin master # To set FETCH_HEAD, so that "git merge-base" works. + - flutter config --no-analytics + - flutter doctor -v + - flutter update-packages + - ./dev/bots/accept_android_sdk_licenses.sh + - date + on_failure: + failure_script: + - date + - which flutter matrix: - - name: docs - skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_drive/**', 'packages/flutter_localizations/**', 'packages/flutter_goldens/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master'" - env: - SHARD: docs + - name: analyze-linux # linux-only + # environment: + # Empirically, the analyze-linux shard runs surprisingly fast (under 15 minutes) with just 1 + # CPU and 4G RAM as of October 2019. It fails with less than 4G though. + script: + - dart --enable-asserts ./dev/bots/analyze.dart + + - name: framework_tests-widgets-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # We use 3 CPUs because that's the minimum required to get framework_tests-widgets-linux + # running fast enough that it is not the long pole, as of OCtober 2019. + CPU: 3 + GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl" + GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095] + script: + - ./dev/bots/download_goldctl.sh + - dart --enable-asserts ./dev/bots/test.dart + + - name: framework_tests-libraries-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # We use 3 CPUs because that's the minimum required to get the + # framework_tests-libraries-linux shard running fast enough that it is not the long pole, as + # of October 2019. + CPU: 3 + GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl" + GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095] + script: + - ./dev/bots/download_goldctl.sh + - dart --enable-asserts ./dev/bots/test.dart + + - name: framework_tests-misc-linux + # this includes the tests for directories in dev/ + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # We use 3 CPUs because that's the minimum required to get framework_tests-misc-linux + # running fast enough that it is not the long pole, as of October 2019. + CPU: 3 + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: tool_tests-general-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the tool_tests-general-linux shard got faster with more CPUs up to 4 + # CPUs, and needed at least 8G of RAM to not run out of memory. + CPU: 4 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: tool_tests-commands-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the tool_tests-commands-linux shard got faster with more CPUs up to 6 + # CPUs, and needed at least 8G of RAM to not run out of memory. + CPU: 6 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: tool_tests-integration-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the tool_tests-integration-linux shard got faster with more CPUs up to + # 6 CPUs, and needed at least 8G of RAM to not run out of memory. + CPU: 6 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: tool_coverage-linux # linux-only + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**/*.dart', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the tool_coverage-linux shard needed at least 12G of RAM to run without + # getting OOM-killed, and even 8 CPUs took 25 minutes. + CPU: 8 + MEMORY: 12G + CODECOV_TOKEN: ENCRYPTED[7c76a7f8c9264f3b7f3fd63fcf186f93c62c4dfe43ec288861c2f506d456681032b89efe7b7a139c82156350ca2c752c] + script: + - dart --enable-asserts ./dev/bots/test.dart + - bash <(curl -s https://codecov.io/bash) -c -f packages/flutter_tools/coverage/lcov.info -F flutter_tool + + - name: web_tests-0-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the Web shards needed more than 6G of RAM. + CPU: 2 + MEMORY: 8G + allow_failures: true + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: web_tests-1-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the Web shards needed more than 6G of RAM. + CPU: 2 + MEMORY: 8G + allow_failures: true + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: web_tests-2-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the Web shards needed more than 6G of RAM. + CPU: 2 + MEMORY: 8G + allow_failures: true + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: web_tests-3-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the Web shards needed more than 6G of RAM. + CPU: 2 + MEMORY: 8G + allow_failures: true + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: web_tests-4-linux + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the Web shards needed more than 6G of RAM. + CPU: 2 + MEMORY: 8G + allow_failures: true + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: web_tests-5_last-linux # last Web shard must end with _last + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # As of October 2019, the Web shards needed more than 6G of RAM. + CPU: 2 + MEMORY: 8G + allow_failures: true + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: build_tests-linux + environment: + # With 1 CPU and 4G of RAM, as of October 2019, build_tests-linux would get OOM-killed. + # Increasing the RAM to 12G allowed it to finish in about 30 minutes, any extra CPU (tried 2 + # and 4) reduced that to just over 20 minutes. 6G was enough not to get OOM-killed. + CPU: 2 + MEMORY: 6G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-0-linux + environment: + # Some of the host-only devicelab tests are pretty involved and need a lot of RAM. + CPU: 2 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-1-linux + environment: + # Some of the host-only devicelab tests are pretty involved and need a lot of RAM. + CPU: 2 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-2-linux + environment: + # Some of the host-only devicelab tests are pretty involved and need a lot of RAM. + CPU: 2 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-3-linux + environment: + # Some of the host-only devicelab tests are pretty involved and need a lot of RAM. + CPU: 2 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-4-linux + environment: + # Some of the host-only devicelab tests are pretty involved and need a lot of RAM. + CPU: 2 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-5_last-linux + environment: + # Some of the host-only devicelab tests are pretty involved and need a lot of RAM. + CPU: 2 + MEMORY: 8G + script: + - dart --enable-asserts ./dev/bots/test.dart + + # TODO(ianh): name: add_to_app_tests-linux + + - name: docs-linux # linux-only + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_drive/**', 'packages/flutter_localizations/**', 'packages/flutter_goldens/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: + # Empirically, as of October 2019, the docs-linux shard took about 30 minutes when run with + # 1 CPU and 4G of RAM. 2 CPUs reduced that to 20 minutes, more CPUs did not improve matters. + CPU: 2 # For uploading master docs to Firebase master branch staging site FIREBASE_MASTER_TOKEN: ENCRYPTED[eb768d18798fdc5abfe09b224e1724c4d82831d715ccf90df2c79d618c317216cbd99493278361f6fe7948b409b603f0] # For uploading beta docs to Firebase public live site FIREBASE_PUBLIC_TOKEN: ENCRYPTED[37e8b82f167864cae9a3f4d2cf3f37dea331d9375c295327c45de524f6c588fa6f6d63e5784f10f6d43ce29689f36c92] - docs_script: ./dev/bots/docs.sh - - name: deploy_gallery - depends_on: - - docs - - analyze - - build_tests-linux - env: - SHARD: deploy_gallery - GOOGLE_DEVELOPER_SERVICE_ACCOUNT_ACTOR_FASTLANE: ENCRYPTED[d9ac1462c3c556fc2f8165c9d5566a16497d8ebc38a50357f7f3abf136b7f83e1d1d76dde36fee356cb0f9ebf7a89346] - ANDROID_GALLERY_UPLOAD_KEY: ENCRYPTED[0f2aca35f05b26add5d9edea2a7449341269a2b7e22d5c667f876996e2e8bc44ff1369431ebf73b7c5581fd95d0e5902] - test_script: - # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they - # might include non-ASCII characters which makes Gradle crash. - # See: https://github.com/flutter/flutter/issues/24935 - # This is a temporary workaround until we figure how to properly configure - # a UTF8 locale on Cirrus (or until the Gradle bug is fixed). - # TODO(amirh): Set the locale to UTF8. - - echo "$CIRRUS_CHANGE_MESSAGE" > /tmp/cirrus_change_message.txt - - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt - - export CIRRUS_CHANGE_MESSAGE="" - - export CIRRUS_COMMIT_MESSAGE="" - - ./dev/bots/deploy_gallery.sh - - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt` - - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt` - - name: analyze - test_script: - - dart --enable-asserts ./dev/bots/analyze.dart - - name: bots_tests-linux - skip: "!changesInclude('dev/bots/**')" - test_script: - - (cd ./dev/bots && pub run test) - container: - cpu: 4 - memory: 12G - - name: tests_widgets-linux - skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tests - SUBSHARD: widgets - GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl" - GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095] - goldctl_script: ./dev/bots/download_goldctl.sh - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: tests_framework_other-linux - skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tests - SUBSHARD: framework_other - GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl" - GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095] - goldctl_script: ./dev/bots/download_goldctl.sh - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: tests_extras-linux - skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**', 'dev/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tests - SUBSHARD: extras - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - # all of the tests except the ones in test/integration and test/commands/create_test for packages/flutter_tools - - name: tool_tests_general-linux - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: general - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: tool_tests_commands-linux - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: commands - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - # all of the tests in test/integration for packages/flutter_tools - - name: tool_tests_integration-linux - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: integration - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: tool_coverage-linux - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**/*.dart') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - CODECOV_TOKEN: ENCRYPTED[7c76a7f8c9264f3b7f3fd63fcf186f93c62c4dfe43ec288861c2f506d456681032b89efe7b7a139c82156350ca2c752c] - SHARD: tool_coverage - test_script: - - dart --enable-asserts ./dev/bots/test.dart - - bash <(curl -s https://codecov.io/bash) -c -s ./packages/flutter_tools/coverage/ -f '*.lcov.info' -F flutter_tool - container: - cpu: 8 - memory: 24G - - name: web_tests-linux-shard-0 - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - env: - SHARD: web_tests - WEB_SHARD: 0 - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: web_tests-linux-shard-1 - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - env: - SHARD: web_tests - WEB_SHARD: 1 - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: web_tests-linux-shard-2 - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - env: - SHARD: web_tests - WEB_SHARD: 2 - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: web_tests-linux-shard-3 - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - env: - SHARD: web_tests - WEB_SHARD: 3 - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: web_tests-linux-shard-4 - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - env: - SHARD: web_tests - WEB_SHARD: 4 - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: web_tests-linux-shard-5 - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - env: - SHARD: web_tests - WEB_SHARD: 5 - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: build_tests-linux - env: - SHARD: build_tests - - name: integration_tests-linux - env: - SHARD: integration_tests - test_script: - # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they - # might include non-ASCII characters which makes Gradle crash. - # See: https://github.com/flutter/flutter/issues/24935 - # This is a temporary workaround until we figure how to properly configure - # a UTF8 locale on Cirrus (or until the Gradle bug is fixed). - # TODO(amirh): Set the locale to UTF8. - - echo "$CIRRUS_CHANGE_MESSAGE" > /tmp/cirrus_change_message.txt - - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt - - export CIRRUS_CHANGE_MESSAGE="" - - export CIRRUS_COMMIT_MESSAGE="" - - dart --enable-asserts ./dev/bots/test.dart - - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt` - - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt` - container: - cpu: 4 - memory: 12G - - name: gradle_tests-linux-shard-1 - env: - SHARD: integration_tests - SUBSHARD: gradle1 - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: gradle_tests-linux-shard-2 - env: - SHARD: integration_tests - SUBSHARD: gradle2 - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: gradle_embedding_v2_tests-linux-shard-1 - env: - SHARD: integration_tests - SUBSHARD: gradle1 - ENABLE_ANDROID_EMBEDDING_V2: 'true' - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: gradle_embedding_v2_tests-linux-shard-2 - env: - SHARD: integration_tests - SUBSHARD: gradle2 - ENABLE_ANDROID_EMBEDDING_V2: 'true' - test_script: - - dart --enable-asserts ./dev/bots/test.dart - container: - cpu: 4 - memory: 12G - - name: release_smoke_tests - env: - CLOUDSDK_CORE_DISABLE_PROMPTS: 1 - GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[1c140257edc48f5578fa5a0e5038b84c8e53270c405efa5a8e35ea303a4e0d135853989f448f72136206de854d17fbec] - test_script: - - echo "$CIRRUS_CHANGE_MESSAGE" > /tmp/cirrus_change_message.txt - - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt - - export CIRRUS_CHANGE_MESSAGE="" - - export CIRRUS_COMMIT_MESSAGE="" - - ./dev/bots/firebase_testlab.sh - - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt` - - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt` + script: + - ./dev/bots/docs.sh + - name: customer_testing-linux + # environment: + # Empirically, this shard runs fine at 1 CPU and 4G RAM as of October 2019. We will probably + # want to grow this container when we invite people to add their tests in large numbers. script: - rm -rf bin/cache/pkg/tests - git clone https://github.com/flutter/tests.git bin/cache/pkg/tests - dart --enable-asserts dev/customer_testing/run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test -task: - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - windows_container: - image: cirrusci/android-sdk:28-windowsservercore-2019 - os_version: 2019 - cpu: 4 - env: - CIRRUS_WORKING_DIR: "C:\\Windows\\Temp\\flutter sdk" - PATH: "$CIRRUS_WORKING_DIR/bin;$CIRRUS_WORKING_DIR/bin/cache/dart-sdk/bin;$PATH" - git_fetch_script: - - git clean -xfd - - git fetch origin - - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work - pub_cache: - folder: $APPDATA\Pub\Cache - fingerprint_script: - - ps: $Env:OS; Get-ChildItem -Path "$Env:CIRRUS_WORKING_DIR" pubspec.yaml -Recurse | Select-String -Pattern "PUBSPEC CHECKSUM" -SimpleMatch - flutter_pkg_cache: - folder: bin\cache\pkg - fingerprint_script: echo %OS% & type bin\internal\*.version - artifacts_cache: - folder: bin\cache\artifacts - fingerprint_script: echo %OS% & type bin\internal\engine.version - setup_script: - - flutter config --no-analytics - - flutter doctor -v - - flutter update-packages - - git fetch origin master - test_all_script: - - dart --enable-asserts dev\bots\test.dart - matrix: - # all of the tests except test/integration and test/commands/create_test for packages/flutter_tools - - name: tool_tests_general-windows - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: general - # all of the tests in test/commands/create_test - - name: tool_tests_commands-windows - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: commands - # all of the tests in test/integration for packages/flutter_tools - - name: tool_tests_integration-windows - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: integration + - name: firebase_test_lab_tests-linux # linux-only + environment: + # Empirically, this shard runs in 20-25 minutes with just one CPU and 4G of RAM, as of + # October 2019. It does not seem to be sensitive to the number of CPUs or amount of RAM; + # doubling CPUs had no effect (mere seconds under 20 minutes), increasing RAM to 24G left it + # on the high end of the 20-25 minute range. (This makes sense, as it's just driving the + # Firebase test lab remotely.) Less than 4G of RAM made it go OOM. + CLOUDSDK_CORE_DISABLE_PROMPTS: 1 + GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[1c140257edc48f5578fa5a0e5038b84c8e53270c405efa5a8e35ea303a4e0d135853989f448f72136206de854d17fbec] + script: + - ./dev/bots/firebase_testlab.sh + - name: deploy_gallery-linux # linux- and macos- only + depends_on: + - analyze-linux + - framework_tests-widgets-linux + - framework_tests-libraries-linux + - framework_tests-misc-linux + - tool_tests-general-linux + - tool_tests-commands-linux + - tool_tests-integration-linux + - build_tests-linux + - hostonly_devicelab_tests-0-linux + - hostonly_devicelab_tests-1-linux + - hostonly_devicelab_tests-2-linux + - hostonly_devicelab_tests-3-linux + - hostonly_devicelab_tests-4-linux + - hostonly_devicelab_tests-5_last-linux + - firebase_test_lab_tests-linux + environment: + # As of October 2019, 1 CPU and 4G of RAM let deploy_gallery-linux finish in about 15 + # minutes, once it got started. + GOOGLE_DEVELOPER_SERVICE_ACCOUNT_ACTOR_FASTLANE: ENCRYPTED[d9ac1462c3c556fc2f8165c9d5566a16497d8ebc38a50357f7f3abf136b7f83e1d1d76dde36fee356cb0f9ebf7a89346] + ANDROID_GALLERY_UPLOAD_KEY: ENCRYPTED[0f2aca35f05b26add5d9edea2a7449341269a2b7e22d5c667f876996e2e8bc44ff1369431ebf73b7c5581fd95d0e5902] + script: + - ./dev/bots/deploy_gallery.sh + +# WINDOWS SHARDS task: - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' windows_container: image: cirrusci/android-sdk:28-windowsservercore-2019 os_version: 2019 - cpu: 4 - env: - CIRRUS_WORKING_DIR: "C:\\Windows\\Temp\\flutter sdk" + cpu: $CPU + memory: $MEMORY + environment: + # It's not clear that changing the cpu/memory fields here has any impact, so we use + # small numbers... + CPU: 2 # 1-8 without compute credits, 1-30 with + MEMORY: 2G # 256M-24G without compute credits, 256M-90G with + CIRRUS_WORKING_DIR: "C:\\Windows\\Temp\\$FLUTTER_SDK_PATH_WITH_SPACE" PATH: "$CIRRUS_WORKING_DIR/bin;$CIRRUS_WORKING_DIR/bin/cache/dart-sdk/bin;$PATH" - git_fetch_script: - - git clean -xfd - - git fetch origin - - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work pub_cache: folder: $APPDATA\Pub\Cache fingerprint_script: - - ps: $Env:OS; Get-ChildItem -Path "$Env:CIRRUS_WORKING_DIR" pubspec.yaml -Recurse | Select-String -Pattern "PUBSPEC CHECKSUM" -SimpleMatch + - ps: $Environment:OS; Get-ChildItem -Path "$Environment:CIRRUS_WORKING_DIR" pubspec.yaml -Recurse | Select-String -Pattern "PUBSPEC CHECKSUM" -SimpleMatch flutter_pkg_cache: folder: bin\cache\pkg fingerprint_script: echo %OS% & type bin\internal\*.version @@ -368,290 +348,285 @@ task: folder: bin\cache\artifacts fingerprint_script: echo %OS% & type bin\internal\*.version setup_script: + - git clean -xffd + - git fetch origin + - git fetch origin master # To set FETCH_HEAD, so that "git merge-base" works. - flutter config --no-analytics - flutter doctor -v - flutter update-packages - git fetch origin master matrix: - - name: bots_tests-windows - skip: "!changesInclude('dev/bots/**')" - test_script: - - cd dev\bots - - pub run test - - name: tests_widgets-windows - skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tests - SUBSHARD: widgets + - name: framework_tests-widgets-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: GOLDCTL: "C:\\Windows\\Temp\\depot_tools\\goldctl.exe" GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095] - goldctl_script: powershell dev\bots\download_goldctl.ps1 - test_all_script: + script: + - powershell dev\bots\download_goldctl.ps1 - dart --enable-asserts dev\bots\test.dart - - name: tests_framework_other-windows - skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tests - SUBSHARD: framework_other + + - name: framework_tests-libraries-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: GOLDCTL: "C:\\Windows\\Temp\\depot_tools\\goldctl.exe" GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095] - goldctl_script: powershell dev\bots\download_goldctl.ps1 - test_all_script: + script: + - powershell dev\bots\download_goldctl.ps1 - dart --enable-asserts dev\bots\test.dart - - name: tests_extras-windows - skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**', 'dev/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tests - SUBSHARD: extras - test_all_script: + + - name: framework_tests-misc-windows + # this includes the tests for directories in dev/ + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + script: - dart --enable-asserts dev\bots\test.dart + + - name: tool_tests-general-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: tool_tests-commands-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: tool_tests-integration-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + script: + - dart --enable-asserts ./dev/bots/test.dart + + # TODO(ianh): Enable Web tests on Windows + - name: build_tests-windows - env: - SHARD: build_tests - container: - cpu: 4 - memory: 12G - test_all_script: - - dart --enable-asserts dev\bots\test.dart - - name: integration_tests-windows - env: - SHARD: integration_tests - container: - cpu: 4 - memory: 12G - test_all_script: + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941 + script: - dart --enable-asserts dev\bots\test.dart + + - name: hostonly_devicelab_tests-0-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941 + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-1-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941 + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-2-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941 + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-3-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941 + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-4-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941 + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-5_last-windows + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941 + script: + - dart --enable-asserts ./dev/bots/test.dart + + # TODO(ianh): name: add_to_app_tests-windows + - name: customer_testing-windows - test_script: + script: - CMD /S /C "IF EXIST "bin\cache\pkg\tests\" RMDIR /S /Q bin\cache\pkg\tests" - git clone https://github.com/flutter/tests.git bin\cache\pkg\tests - dart --enable-asserts dev\customer_testing\run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test - - name: gradle_tests-windows-shard-1 - env: - SHARD: integration_tests - SUBSHARD: gradle1 - test_script: - - dart --enable-asserts dev\bots\test.dart - container: - cpu: 4 - memory: 12G - - name: gradle_tests-windows-shard-2 - env: - SHARD: integration_tests - SUBSHARD: gradle2 - test_script: - - dart --enable-asserts dev\bots\test.dart - container: - cpu: 4 - memory: 12G - - name: gradle_embedding_v2_tests-windows-shard-1 - env: - SHARD: integration_tests - SUBSHARD: gradle1 - ENABLE_ANDROID_EMBEDDING_V2: 'true' - test_script: - - dart --enable-asserts dev\bots\test.dart - container: - cpu: 4 - memory: 12G - - name: gradle_embedding_v2_tests-windows-shard-2 - env: - SHARD: integration_tests - SUBSHARD: gradle2 - ENABLE_ANDROID_EMBEDDING_V2: 'true' - test_script: - - dart --enable-asserts dev\bots\test.dart - container: - cpu: 4 - memory: 12G +# MACOS SHARDS +# Mac doesn't use caches because they apparently take longer to populate and save +# than just fetching the data in the first place. task: - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - name: deploy_gallery-macos - depends_on: - - analyze - env: - # Name the SDK directory to include a space so that we constantly - # test path names with spaces in them. - CIRRUS_WORKING_DIR: "/tmp/flutter sdk" - SHARD: deploy_gallery - # Apple Fastlane password. - FASTLANE_PASSWORD: ENCRYPTED[4b1f0b8d52874e9de965acd46c79743f3b81f3a513614179b9be7cf53dc8258753e257bdadb11a298ee455259df21865] - # Private repo for publishing certificates. - PUBLISHING_MATCH_CERTIFICATE_REPO: ENCRYPTED[3c0e78877d933fc80107aa6f3790fd1cf927250b852d6cb53202be696b4903ed8ca839b809626aaf18050bf7e436fab7] - PUBLISHING_MATCH_REPO_TOKEN: ENCRYPTED[3d1230b744c6ed6c788a91bec741b769401dbcd426b18f9af8080bfeefdfc21913ca4047980c5b5b7ce823f12e7b6b19] - # Apple Certificates Match Passphrase - MATCH_PASSWORD: ENCRYPTED[db07f252234397090e3ec59152d9ec1831f5ecd0ef97d247b1dca757bbb9ef9b7c832a39bce2caf1949ccdf097e59a73] osx_instance: - image: mojave-xcode-10.2 - # occasionally the clock on these machines is out of sync - # with the actual time - this should help to verify - print_date_script: - - date - install_cocoapods_script: - - sudo gem install cocoapods --no-document - git_fetch_script: - - git clean -xfd - - git fetch origin - - git fetch origin master # To set FETCH_HEAD - setup_script: - - bin/flutter config --no-analytics - - bin/flutter update-packages - test_all_script: - - ./dev/bots/deploy_gallery.sh - -task: - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - osx_instance: - image: mojave-xcode-10.2 - depends_on: - - analyze - env: - CIRRUS_WORKING_DIR: "/tmp/flutter sdk" - COCOAPODS_DISABLE_STATS: true - print_date_script: - - date - git_fetch_script: - - git clean -xfd - - git fetch origin - - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work - setup_script: - - bin/flutter config --no-analytics - - bin/flutter doctor -v - - bin/flutter update-packages - test_all_script: - - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 - - bin/cache/dart-sdk/bin/dart --enable-asserts dev/bots/test.dart - matrix: - # all of the tests except test/integration and test/commands/create_test for packages/flutter_tools - - name: tool_tests_general-macos - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: general - # all of the tests in test/commands/create_test - - name: tool_tests_commands-macos - skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'" - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: commands - # all of the tests in test/integration for packages/flutter_tools - - name: tool_tests_integration-macos - only_if: $CIRRUS_BRANCH == 'master' - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tool_tests - SUBSHARD: integration - -task: - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - osx_instance: - image: mojave-xcode-10.2 - depends_on: - - analyze - env: - CIRRUS_WORKING_DIR: "/tmp/flutter sdk" - COCOAPODS_DISABLE_STATS: true + image: mojave-xcode-10.2-flutter # see https://cirrus-ci.org/guide/macOS/ for list of images (we should update regularly) + # cpu is always 2 + # memory is always 8G + environment: + CIRRUS_WORKING_DIR: "/tmp/$FLUTTER_SDK_PATH_WITH_SPACE" + FLUTTER_FRAMEWORK_DIR: "$CIRRUS_WORKING_DIR/bin/cache/artifacts/engine/ios/" PATH: "$CIRRUS_WORKING_DIR/bin:$CIRRUS_WORKING_DIR/bin/cache/dart-sdk/bin:$PATH" - # occasionally the clock on these machines is out of sync - # with the actual time - this should help to verify - print_date_script: - - date - install_cocoapods_script: - - sudo gem install cocoapods --no-document - git_fetch_script: - - git clean -xfd - - git fetch origin - - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work + COCOAPODS_DISABLE_STATS: true + CPU: 2 + MEMORY: 8G setup_script: - - bin/flutter config --no-analytics - - bin/flutter doctor -v - - bin/flutter update-packages + - date + - which flutter + - sudo gem install cocoapods + - sudo gem install xcpretty + - git clean -xffd + - git fetch origin + - git fetch origin master # To set FETCH_HEAD, so that "git merge-base" works. + - flutter config --no-analytics + - flutter doctor -v + - flutter update-packages + - date + - which flutter + on_failure: + failure_script: + - date + - which flutter matrix: - - name: bots_tests-macos - skip: "!changesInclude('dev/bots/**')" - test_script: - - (cd ./dev/bots && pub run test) - - name: tests_widgets-macos - only_if: $CIRRUS_BRANCH == 'master' - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tests - SUBSHARD: widgets + - name: framework_tests-widgets-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl" GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095] - goldctl_script: ./dev/bots/download_goldctl.sh - test_all_script: + script: - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - ./dev/bots/download_goldctl.sh - dart --enable-asserts dev/bots/test.dart - on_failure: - print_failure_time_script: date - - name: tests_framework_other-macos - only_if: $CIRRUS_BRANCH == 'master' - env: - GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb] - SHARD: tests - SUBSHARD: framework_other + + - name: framework_tests-libraries-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + environment: GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl" GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095] - goldctl_script: ./dev/bots/download_goldctl.sh - test_all_script: + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - ./dev/bots/download_goldctl.sh + - dart --enable-asserts dev/bots/test.dart + + - name: framework_tests-misc-macos + # this includes the tests for directories in dev/ + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''" + script: - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 - dart --enable-asserts dev/bots/test.dart - on_failure: - print_failure_time_script: date - - name: integration_tests-macos - only_if: $CIRRUS_BRANCH == 'master' - env: - SHARD: integration_tests - test_all_script: - - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 - - dart --enable-asserts dev/bots/test.dart - - name: add2app-macos - skip: true # https://github.com/flutter/flutter/issues/39507 - env: - SHARD: add2app_test - setup_xcpretty_script: - - sudo gem install xcpretty - test_all_script: + + - name: tool_tests-general-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + - name: tool_tests-commands-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + - name: tool_tests-integration-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''" + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + # TODO(ianh): Enable Web tests on macOS. + + - name: build_tests-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940 + script: + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-0-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940 + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-1-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940 + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-2-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940 + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-3-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940 + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-4-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940 + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + - name: hostonly_devicelab_tests-5_last-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940 + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/test.dart + + - name: add_to_app_tests-macos + only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940 + skip: true # https://github.com/flutter/flutter/pull/42444 + script: - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 - dart --enable-asserts dev/bots/test.dart + - name: customer_testing-macos - test_script: + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 - rm -rf bin/cache/pkg/tests - git clone https://github.com/flutter/tests.git bin/cache/pkg/tests - dart --enable-asserts dev/customer_testing/run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test + - name: deploy_gallery-macos # linux- and macos- only + depends_on: + - analyze-linux + - framework_tests-widgets-macos + - framework_tests-libraries-macos + - framework_tests-misc-macos + - tool_tests-general-macos + - tool_tests-commands-macos + - tool_tests-integration-macos + - build_tests-macos + - hostonly_devicelab_tests-0-macos + - hostonly_devicelab_tests-1-macos + - hostonly_devicelab_tests-2-macos + - hostonly_devicelab_tests-3-macos + - hostonly_devicelab_tests-4-macos + - hostonly_devicelab_tests-5_last-macos + - firebase_test_lab_tests-linux + environment: + # Apple Fastlane password. + FASTLANE_PASSWORD: ENCRYPTED[4b1f0b8d52874e9de965acd46c79743f3b81f3a513614179b9be7cf53dc8258753e257bdadb11a298ee455259df21865] + # Private repo for publishing certificates. + PUBLISHING_MATCH_CERTIFICATE_REPO: ENCRYPTED[3c0e78877d933fc80107aa6f3790fd1cf927250b852d6cb53202be696b4903ed8ca839b809626aaf18050bf7e436fab7] + PUBLISHING_MATCH_REPO_TOKEN: ENCRYPTED[3d1230b744c6ed6c788a91bec741b769401dbcd426b18f9af8080bfeefdfc21913ca4047980c5b5b7ce823f12e7b6b19] + # Apple Certificates Match Passphrase + MATCH_PASSWORD: ENCRYPTED[db07f252234397090e3ec59152d9ec1831f5ecd0ef97d247b1dca757bbb9ef9b7c832a39bce2caf1949ccdf097e59a73] + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - ./dev/bots/deploy_gallery.sh + docker_builder: # Only build a new docker image when we tag a release (for dev, beta, or release.) only_if: $CIRRUS_TAG != '' - env: + environment: GCLOUD_CREDENTIALS: ENCRYPTED[f7c098d4dd7f5ee1bfee0bb7e944cce72efbe10e97ad6440ae72de4de6a1c24d23f421a2619c668e94377fb64b0bb3e6] depends_on: - - docs - - analyze - - tests_widgets-linux - - tests_framework_other-linux - - tests_extras-linux - - tool_tests_general-linux - - tool_tests_commands-linux - - tool_tests_integration-linux + - docs-linux + - analyze-linux + - framework_tests-widgets-linux + - framework_tests-libraries-linux + - framework_tests-misc-linux + - tool_tests-general-linux + - tool_tests-commands-linux + - tool_tests-integration-linux - build_tests-linux - - integration_tests-linux - - gradle_tests-linux-shard-1 - - gradle_tests-linux-shard-2 - - gradle_embedding_v2_tests-linux-shard-1 - - gradle_embedding_v2_tests-linux-shard-2 - - gradle_tests-windows-shard-1 - - gradle_tests-windows-shard-2 - - gradle_embedding_v2_tests-windows-shard-1 - - gradle_embedding_v2_tests-windows-shard-2 - - release_smoke_tests - build_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_build.sh" - login_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_login.sh" - push_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_push.sh" + - hostonly_devicelab_tests-0-linux + - hostonly_devicelab_tests-1-linux + - hostonly_devicelab_tests-2-linux + - hostonly_devicelab_tests-3-linux + - hostonly_devicelab_tests-4-linux + - hostonly_devicelab_tests-5_last-linux + - firebase_test_lab_tests-linux + script: + - "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_build.sh" + - "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_login.sh" + - "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_push.sh" diff --git a/dev/README.md b/dev/README.md index afe8f260633..3c287dd6924 100644 --- a/dev/README.md +++ b/dev/README.md @@ -2,3 +2,6 @@ This directory contains tools and resources that the Flutter team uses during development of the framework. The tools in this directory should not be necessary for developing Flutter applications, though of course they may be interesting if you are curious. + +The tests in this directory are run in the `framework_tests_misc-*` +shards. diff --git a/dev/bots/cirrus_setup.sh b/dev/bots/accept_android_sdk_licenses.sh similarity index 53% rename from dev/bots/cirrus_setup.sh rename to dev/bots/accept_android_sdk_licenses.sh index 451bcbc2817..a5b6abbea6e 100755 --- a/dev/bots/cirrus_setup.sh +++ b/dev/bots/accept_android_sdk_licenses.sh @@ -1,31 +1,19 @@ #!/bin/bash set -e +# This script is only meant to be run by the Cirrus CI system, not locally. +# It must be run from the root of the Flutter repo. + function error() { echo "$@" 1>&2 } -# This script is only meant to be run by the Cirrus CI system, not locally. -# It must be run from the root of the Flutter repo. - function accept_android_licenses() { yes "y" | flutter doctor --android-licenses > /dev/null 2>&1 } echo "Flutter SDK directory is: $PWD" -# Run flutter to download dependencies and precompile things, and to disable -# analytics on the bots. -echo "Downloading build dependencies and pre-compiling Flutter snapshot" -./bin/flutter config --no-analytics - -# Run doctor, to print it to the log for debugging purposes. -./bin/flutter doctor -v - # Accept licenses. echo "Accepting Android licenses." accept_android_licenses || (error "Accepting Android licenses failed." && false) - -# Run pub get in all the repo packages. -echo "Updating packages for Flutter." -./bin/flutter update-packages diff --git a/dev/bots/deploy_gallery.sh b/dev/bots/deploy_gallery.sh index f372fec181e..86d34d9354c 100755 --- a/dev/bots/deploy_gallery.sh +++ b/dev/bots/deploy_gallery.sh @@ -24,88 +24,90 @@ set -x cd "$FLUTTER_ROOT" -if [[ "$SHARD" = "deploy_gallery" ]]; then - version="$( /root/.android/debug.keystore + fi + set -x ( - cd examples/flutter_gallery - flutter build apk --release -t lib/main_publish.dart + cd examples/flutter_gallery/android + fastlane deploy_play_store ) - echo "Android Flutter Gallery built" - if [[ -z "$CIRRUS_PR" && "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then - echo "Deploying Flutter Gallery $version to Play Store..." - set +x # Don't echo back the below. - if [ -n "$ANDROID_GALLERY_UPLOAD_KEY" ]; then - echo "$ANDROID_GALLERY_UPLOAD_KEY" | base64 --decode > /root/.android/debug.keystore - fi - set -x + else + echo "(Not deploying; Flutter Gallery is only deployed to Play store for tagged dev branch commits.)" + fi +elif [[ "$OS" == "darwin" ]]; then + echo "Building Flutter Gallery $version for iOS..." + ( + cd examples/flutter_gallery + flutter build ios --release --no-codesign -t lib/main_publish.dart + + # flutter build ios will run CocoaPods script. Check generated locations. + if [[ ! -d "ios/Pods" ]]; then + echo "Error: pod install failed to setup plugins" + exit 1 + fi + + if [[ ! -d "ios/.symlinks/plugins" ]]; then + echo "Error: pod install failed to setup plugin symlinks" + exit 1 + fi + + if [[ -d "ios/.symlinks/flutter" ]]; then + echo "Error: pod install created flutter symlink" + exit 1 + fi + + if [[ ! -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets" ]]; then + echo "Error: flutter_assets not assembled" + exit 1 + fi + + if [[ + -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" || + -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" || + -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data" + ]]; then + echo "Error: compiled debug version of app with --release flag" + exit 1 + fi + ) + echo "iOS Flutter Gallery built" + if [[ -z "$CIRRUS_PR" ]]; then + if [[ "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then + echo "Archiving with distribution profile and deploying to TestFlight..." ( - cd examples/flutter_gallery/android - fastlane deploy_play_store + cd examples/flutter_gallery/ios + export DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS="-t DAV" + fastlane build_and_deploy_testflight upload:true ) else - echo "Not deployed: Flutter Gallery is only deployed to the Play Store on merged and tagged dev branch commits" - fi - elif [[ "$OS" == "darwin" ]]; then - echo "Building Flutter Gallery $version for iOS..." - ( - cd examples/flutter_gallery - flutter build ios --release --no-codesign -t lib/main_publish.dart - - # flutter build ios will run CocoaPods script. Check generated locations. - if [[ ! -d "ios/Pods" ]]; then - echo "Error: pod install failed to setup plugins" - exit 1 - fi - - if [[ ! -d "ios/.symlinks/plugins" ]]; then - echo "Error: pod install failed to setup plugin symlinks" - exit 1 - fi - - if [[ -d "ios/.symlinks/flutter" ]]; then - echo "Error: pod install created flutter symlink" - exit 1 - fi - - if [[ ! -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets" ]]; then - echo "Error: flutter_assets not assembled" - exit 1 - fi - - if [[ - -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" || - -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" || - -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data" - ]]; then - echo "Error: compiled debug version of app with --release flag" - exit 1 - fi - ) - echo "iOS Flutter Gallery built" - if [[ -z "$CIRRUS_PR" ]]; then - if [[ "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then - echo "Archiving with distribution profile and deploying to TestFlight..." - ( - cd examples/flutter_gallery/ios - export DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS="-t DAV" - fastlane build_and_deploy_testflight upload:true - ) - else - echo "Archiving with distribution profile..." - ( - cd examples/flutter_gallery/ios - fastlane build_and_deploy_testflight - ) - echo "Archive is only deployed to TestFlight on tagged dev branch commits" - fi - else - echo "Not deployed: Flutter Gallery is only deployed to TestFlight on merged and tagged dev branch commits" + # On iOS the signing can break as well, so we verify this regularly (not just + # on tagged dev branch commits). We can only do this post-merge, though, because + # the secrets aren't available on PRs. + echo "Testing archiving with distribution profile..." + ( + cd examples/flutter_gallery/ios + fastlane build_and_deploy_testflight + ) + echo "(Not deploying; Flutter Gallery is only deployed to TestFlight for tagged dev branch commits.)" fi + else + echo "(Not archiving or deploying; Flutter Gallery archiving is only tested post-commit.)" fi else - echo "Doing nothing: not on the 'deploy_gallery' SHARD." + echo "Unknown OS: $OS" + echo "Aborted." + exit 1 fi diff --git a/dev/bots/run_command.dart b/dev/bots/run_command.dart index 634a23230a3..f240a5cce0c 100644 --- a/dev/bots/run_command.dart +++ b/dev/bots/run_command.dart @@ -95,7 +95,6 @@ Future runCommand(String executable, List arguments, { OutputMode outputMode = OutputMode.print, CapturedOutput output, bool skip = false, - bool expectFlaky = false, bool Function(String) removeLine, }) async { assert((outputMode == OutputMode.capture) == (output != null), @@ -145,10 +144,6 @@ Future runCommand(String executable, List arguments, { output.stderr = _flattenToString(await savedStderr); } - // If the test is flaky we don't care about the actual exit. - if (expectFlaky) - return; - if ((exitCode == 0) == expectNonZeroExit || (expectedExitCode != null && exitCode != expectedExitCode)) { if (failureMessage != null) { print(failureMessage); diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 9914dd24588..fea54fb5c2a 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -4,11 +4,11 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math' as math; import 'package:googleapis/bigquery/v2.dart' as bq; import 'package:googleapis_auth/auth_io.dart' as auth; import 'package:http/http.dart' as http; -import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; import 'flutter_compact_formatter.dart'; @@ -30,21 +30,54 @@ final String dart = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Pl final String pub = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'pub.bat' : 'pub'); final String pubCache = path.join(flutterRoot, '.pub-cache'); final String toolRoot = path.join(flutterRoot, 'packages', 'flutter_tools'); + +/// The arguments to pass to `flutter test` (typically the local engine +/// configuration) -- prefilled with the arguments passed to test.dart. final List flutterTestArgs = []; final bool useFlutterTestFormatter = Platform.environment['FLUTTER_TEST_FORMATTER'] == 'true'; final bool canUseBuildRunner = Platform.environment['FLUTTER_TEST_NO_BUILD_RUNNER'] != 'true'; -const Map _kShards = { - 'tests': _runTests, - 'web_tests': _runWebTests, - 'tool_tests': _runToolTests, - 'tool_coverage': _runToolCoverage, - 'build_tests': _runBuildTests, - 'coverage': _runCoverage, - 'integration_tests': _runIntegrationTests, - 'add2app_test': _runAdd2AppTest, -}; +/// The number of Cirrus jobs that run host-only devicelab tests in parallel. +/// +/// WARNING: if you change this number, also change .cirrus.yml +/// and make sure it runs _all_ shards. +const int kDeviceLabShardCount = 6; + +/// The number of Cirrus jobs that run Web tests in parallel. +/// +/// WARNING: if you change this number, also change .cirrus.yml +/// and make sure it runs _all_ shards. +/// +/// The last shard also runs the Web plugin tests. +const int kWebShardCount = 6; + +/// Maximum number of Web tests to run in a single `flutter test`. We found that +/// large batches can get flaky, possibly because we reuse a single instance of +/// the browser, and after many tests the browser's state gets corrupted. +const int kWebBatchSize = 20; + +/// Tests that we don't run on Web for various reasons. +// +// TODO(yjbanov): we're getting rid of these blacklists as part of https://github.com/flutter/flutter/projects/60 +const List kWebTestDirectoryBlacklist = [ + 'cupertino', + 'examples', + 'material', +]; +const List kWebTestFileBlacklist = [ + 'test/widgets/heroes_test.dart', + 'test/widgets/text_test.dart', + 'test/widgets/selectable_text_test.dart', + 'test/widgets/color_filter_test.dart', + 'test/widgets/editable_text_cursor_test.dart', + 'test/widgets/shadow_test.dart', + 'test/widgets/raw_keyboard_listener_test.dart', + 'test/widgets/editable_text_test.dart', + 'test/widgets/widget_inspector_test.dart', + 'test/widgets/draggable_test.dart', + 'test/widgets/shortcuts_test.dart', +]; /// When you call this, you can pass additional arguments to pass custom /// arguments to flutter test. For example, you might want to call this @@ -53,31 +86,30 @@ const Map _kShards = { /// /// To run the tool_tests part, run it with SHARD=tool_tests /// -/// For example: +/// Examples: /// SHARD=tool_tests bin/cache/dart-sdk/bin/dart dev/bots/test.dart /// bin/cache/dart-sdk/bin/dart dev/bots/test.dart --local-engine=host_debug_unopt Future main(List args) async { flutterTestArgs.addAll(args); - - final String shard = Platform.environment['SHARD']; - if (shard != null) { - if (!_kShards.containsKey(shard)) { - print('Invalid shard: $shard'); - print('The available shards are: ${_kShards.keys.join(", ")}'); - exit(1); - } - print('${bold}SHARD=$shard$reset'); - await _kShards[shard](); - } else { - for (String currentShard in _kShards.keys) { - print('${bold}SHARD=$currentShard$reset'); - await _kShards[currentShard](); - print(''); - } - } + if (Platform.environment.containsKey(CIRRUS_TASK_NAME)) + print('Running task: ${Platform.environment[CIRRUS_TASK_NAME]}'); + print('═' * 80); + await _runSmokeTests(); + print('═' * 80); + await selectShard(const { + 'add_to_app_tests': _runAddToAppTests, + 'build_tests': _runBuildTests, + 'framework_coverage': _runFrameworkCoverage, + 'framework_tests': _runFrameworkTests, + 'hostonly_devicelab_tests': _runHostOnlyDeviceLabTests, + 'tool_coverage': _runToolCoverage, + 'tool_tests': _runToolTests, + 'web_tests': _runWebTests, + }); } Future _runSmokeTests() async { + print('${green}Running smoketests...$reset'); // Verify that the tests actually return failure on failure and success on // success. final String automatedTests = path.join(flutterRoot, 'dev', 'automated_tests'); @@ -108,10 +140,11 @@ Future _runSmokeTests() async { script: path.join('test_smoke_test', 'pending_timer_fail_test.dart'), expectFailure: true, printOutput: false, - outputChecker: (CapturedOutput output) => - output.stdout.contains('failingPendingTimerTest') - ? null - : 'Failed to find the stack trace for the pending Timer.', + outputChecker: (CapturedOutput output) { + return output.stdout.contains('failingPendingTimerTest') + ? null + : 'Failed to find the stack trace for the pending Timer.'; + } ); // We run the remaining smoketests in parallel, because they each take some // time to run (e.g. compiling), so we don't want to run them in series, @@ -153,8 +186,11 @@ Future _runSmokeTests() async { ); // Verify that we correctly generated the version file. - final bool validVersion = await verifyVersion(path.join(flutterRoot, 'version')); - if (!validVersion) { + final String versionError = await verifyVersion(File(path.join(flutterRoot, 'version'))); + if (versionError != null) { + print(redLine); + print(versionError); + print(redLine); exit(1); } } @@ -179,7 +215,7 @@ Future _getBigqueryApi() async { final http.Client client = await auth.clientViaServiceAccount(accountCredentials, scopes); return bq.BigqueryApi(client); } catch (e) { - print('Failed to get BigQuery API client.'); + print('${red}Failed to get BigQuery API client.$reset'); print(e); return null; } @@ -203,7 +239,6 @@ Future _runToolCoverage() async { Future _runToolTests() async { final bq.BigqueryApi bigqueryApi = await _getBigqueryApi(); - await _runSmokeTests(); const String kDotShard = '.shard'; const String kTest = 'test'; @@ -232,11 +267,10 @@ Future _runToolTests() async { await selectSubshard(subshards); } -/// Verifies that AOT, APK, and IPA (if on macOS) builds the -/// examples apps without crashing. It does not actually -/// launch the apps. That happens later in the devicelab. This is -/// just a smoke-test. In particular, this will verify we can build -/// when there are spaces in the path name for the Flutter SDK and +/// Verifies that AOT, APK, and IPA (if on macOS) builds the examples apps +/// without crashing. It does not actually launch the apps. That happens later +/// in the devicelab. This is just a smoke-test. In particular, this will verify +/// we can build when there are spaces in the path name for the Flutter SDK and /// target app. Future _runBuildTests() async { final Stream exampleDirectories = Directory(path.join(flutterRoot, 'examples')).list(); @@ -245,10 +279,11 @@ Future _runBuildTests() async { continue; } final String examplePath = fileEntity.path; - await _flutterBuildAot(examplePath); await _flutterBuildApk(examplePath); - await _flutterBuildIpa(examplePath); + if (Platform.isMacOS) { + await _flutterBuildIpa(examplePath); + } } // Web compilation tests. await _flutterBuildDart2js(path.join('dev', 'integration_tests', 'web'), path.join('lib', 'main.dart')); @@ -256,12 +291,44 @@ Future _runBuildTests() async { await _flutterBuildDart2js(path.join('dev', 'integration_tests', 'web_compile_tests'), path.join('lib', 'dart_io_import.dart'), ); +} - print('${bold}DONE: All build tests successful.$reset'); +Future _flutterBuildAot(String relativePathToApplication) async { + print('${green}Testing AOT build$reset for $cyan$relativePathToApplication$reset...'); + await runCommand(flutter, + ['build', 'aot', '-v'], + workingDirectory: path.join(flutterRoot, relativePathToApplication), + ); +} + +Future _flutterBuildApk(String relativePathToApplication) async { + print('${green}Testing APK --debug build$reset for $cyan$relativePathToApplication$reset...'); + await runCommand(flutter, + ['build', 'apk', '--debug', '-v'], + workingDirectory: path.join(flutterRoot, relativePathToApplication), + ); +} + +Future _flutterBuildIpa(String relativePathToApplication) async { + assert(Platform.isMacOS); + print('${green}Testing IPA build$reset for $cyan$relativePathToApplication$reset...'); + // Install Cocoapods. We don't have these checked in for the examples, + // and build ios doesn't take care of it automatically. + final File podfile = File(path.join(flutterRoot, relativePathToApplication, 'ios', 'Podfile')); + if (podfile.existsSync()) { + await runCommand('pod', + ['install'], + workingDirectory: podfile.parent.path, + ); + } + await runCommand(flutter, + ['build', 'ios', '--no-codesign', '--debug', '-v'], + workingDirectory: path.join(flutterRoot, relativePathToApplication), + ); } Future _flutterBuildDart2js(String relativePathToApplication, String target, { bool expectNonZeroExit = false }) async { - print('Running Dart2JS build tests...'); + print('${green}Testing Dart2JS build$reset for $cyan$relativePathToApplication$reset...'); await runCommand(flutter, ['build', 'web', '-v', '--target=$target'], workingDirectory: path.join(flutterRoot, relativePathToApplication), @@ -270,211 +337,101 @@ Future _flutterBuildDart2js(String relativePathToApplication, String targe 'FLUTTER_WEB': 'true', }, ); - print('Done.'); } -Future _flutterBuildAot(String relativePathToApplication) async { - print('Running AOT build tests...'); - await runCommand(flutter, - ['build', 'aot', '-v'], - workingDirectory: path.join(flutterRoot, relativePathToApplication), - expectNonZeroExit: false, - ); - print('Done.'); -} - -Future _flutterBuildApk(String relativePathToApplication) async { - if ( - (Platform.environment['ANDROID_HOME']?.isEmpty ?? true) && - (Platform.environment['ANDROID_SDK_ROOT']?.isEmpty ?? true)) { - return; - } - print('Running APK build tests...'); - await runCommand(flutter, - ['build', 'apk', '--debug', '-v'], - workingDirectory: path.join(flutterRoot, relativePathToApplication), - expectNonZeroExit: false, - ); - print('Done.'); -} - -Future _flutterBuildIpa(String relativePathToApplication) async { - if (!Platform.isMacOS) { - return; - } - print('Running IPA build tests...'); - // Install Cocoapods. We don't have these checked in for the examples, - // and build ios doesn't take care of it automatically. - final File podfile = File(path.join(flutterRoot, relativePathToApplication, 'ios', 'Podfile')); - if (podfile.existsSync()) { - await runCommand('pod', - ['install'], - workingDirectory: podfile.parent.path, - expectNonZeroExit: false, +Future _runAddToAppTests() async { + if (Platform.isMacOS) { + print('${green}Running add-to-app iOS integration tests$reset...'); + final String addToAppDir = path.join(flutterRoot, 'dev', 'integration_tests', 'ios_add2app'); + await runCommand('./build_and_test.sh', + [], + workingDirectory: addToAppDir, ); } - await runCommand(flutter, - ['build', 'ios', '--no-codesign', '--debug', '-v'], - workingDirectory: path.join(flutterRoot, relativePathToApplication), - expectNonZeroExit: false, - ); - print('Done.'); } -Future _runAdd2AppTest() async { - if (!Platform.isMacOS) { - return; - } - print('Running Add2App iOS integration tests...'); - final String add2AppDir = path.join(flutterRoot, 'dev', 'integration_tests', 'ios_add2app'); - await runCommand('./build_and_test.sh', - [], - workingDirectory: add2AppDir, - expectNonZeroExit: false, - ); - print('Done.'); -} - -Future _runTests() async { +Future _runFrameworkTests() async { final bq.BigqueryApi bigqueryApi = await _getBigqueryApi(); - await _runSmokeTests(); - final String subShard = Platform.environment['SUBSHARD']; Future runWidgets() async { - await _runFlutterTest( - path.join(flutterRoot, 'packages', 'flutter'), - tableData: bigqueryApi?.tabledata, - tests: [ - path.join('test', 'widgets') + path.separator, - ], - ); - // Only packages/flutter/test/widgets/widget_inspector_test.dart really - // needs to be run with --track-widget-creation but it is nice to run - // all of the tests in package:flutter with the flag to ensure that - // the Dart kernel transformer triggered by the flag does not break anything. + print('${green}Running packages/flutter tests for$reset: ${cyan}test/widgets/$reset'); await _runFlutterTest( path.join(flutterRoot, 'packages', 'flutter'), options: ['--track-widget-creation'], tableData: bigqueryApi?.tabledata, - tests: [ - path.join('test', 'widgets') + path.separator, - ], + tests: [ path.join('test', 'widgets') + path.separator ], ); + await _runFlutterTest( + path.join(flutterRoot, 'packages', 'flutter'), + options: ['--no-track-widget-creation'], + tableData: bigqueryApi?.tabledata, + tests: [ path.join('test', 'widgets') + path.separator ], + ); + // Try compiling code outside of the packages/flutter directory with and without --track-widget-creation + await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), options: ['--track-widget-creation'], tableData: bigqueryApi?.tabledata); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), options: ['--no-track-widget-creation'], tableData: bigqueryApi?.tabledata); } - Future runFrameworkOthers() async { + Future runLibraries() async { final List tests = Directory(path.join(flutterRoot, 'packages', 'flutter', 'test')) .listSync(followLinks: false, recursive: false) .whereType() .where((Directory dir) => dir.path.endsWith('widgets') == false) - .map((Directory dir) => path.join('test', path.basename(dir.path)) + path.separator) + .map((Directory dir) => path.join('test', path.basename(dir.path)) + path.separator) .toList(); - - print('Running tests for: ${tests.join(';')}'); - - await _runFlutterTest( - path.join(flutterRoot, 'packages', 'flutter'), - tableData: bigqueryApi?.tabledata, - tests: tests, - ); - // Only packages/flutter/test/widgets/widget_inspector_test.dart really - // needs to be run with --track-widget-creation but it is nice to run - // all of the tests in package:flutter with the flag to ensure that - // the Dart kernel transformer triggered by the flag does not break anything. + print('${green}Running packages/flutter tests$reset for: $cyan${tests.join(", ")}$reset'); await _runFlutterTest( path.join(flutterRoot, 'packages', 'flutter'), options: ['--track-widget-creation'], tableData: bigqueryApi?.tabledata, tests: tests, ); + await _runFlutterTest( + path.join(flutterRoot, 'packages', 'flutter'), + options: ['--no-track-widget-creation'], + tableData: bigqueryApi?.tabledata, + tests: tests, + ); } - Future runExtras() async { - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), tableData: bigqueryApi?.tabledata); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tableData: bigqueryApi?.tabledata); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'), tableData: bigqueryApi?.tabledata); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'fuchsia_remote_debug_protocol'), tableData: bigqueryApi?.tabledata); + Future runMisc() async { + print('${green}Running package tests$reset for directories other than packages/flutter'); await _pubRunTest(path.join(flutterRoot, 'dev', 'bots'), tableData: bigqueryApi?.tabledata); await _pubRunTest(path.join(flutterRoot, 'dev', 'devicelab'), tableData: bigqueryApi?.tabledata); await _pubRunTest(path.join(flutterRoot, 'dev', 'snippets'), tableData: bigqueryApi?.tabledata); await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'android_semantics_testing'), tableData: bigqueryApi?.tabledata); await _runFlutterTest(path.join(flutterRoot, 'dev', 'manual_tests'), tableData: bigqueryApi?.tabledata); await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'vitool'), tableData: bigqueryApi?.tabledata); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'catalog'), tableData: bigqueryApi?.tabledata); await _runFlutterTest(path.join(flutterRoot, 'examples', 'hello_world'), tableData: bigqueryApi?.tabledata); await _runFlutterTest(path.join(flutterRoot, 'examples', 'layers'), tableData: bigqueryApi?.tabledata); await _runFlutterTest(path.join(flutterRoot, 'examples', 'stocks'), tableData: bigqueryApi?.tabledata); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), tableData: bigqueryApi?.tabledata); - // Regression test to ensure that code outside of package:flutter can run - // with --track-widget-creation. - await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), options: ['--track-widget-creation'], tableData: bigqueryApi?.tabledata); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'catalog'), tableData: bigqueryApi?.tabledata); - // Smoke test for code generation. - await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'codegen'), tableData: bigqueryApi?.tabledata, environment: { - 'FLUTTER_EXPERIMENTAL_BUILD': 'true', - }); - } - switch (subShard) { - case 'widgets': - await runWidgets(); - break; - case 'framework_other': - await runFrameworkOthers(); - break; - case 'extras': - runExtras(); - break; - default: - print('Unknown sub-shard $subShard, running all tests!'); - await runWidgets(); - await runFrameworkOthers(); - await runExtras(); - + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tableData: bigqueryApi?.tabledata); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), tableData: bigqueryApi?.tabledata); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'), tableData: bigqueryApi?.tabledata); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'fuchsia_remote_debug_protocol'), tableData: bigqueryApi?.tabledata); + await _runFlutterTest( + path.join(flutterRoot, 'dev', 'integration_tests', 'codegen'), + tableData: bigqueryApi?.tabledata, + environment: { + 'FLUTTER_EXPERIMENTAL_BUILD': 'true', + }, + ); } - print('${bold}DONE: All tests successful.$reset'); + await selectSubshard({ + 'widgets': runWidgets, + 'libraries': runLibraries, + 'misc': runMisc, + }); } -// TODO(yjbanov): we're getting rid of these blacklists as part of https://github.com/flutter/flutter/projects/60 -const List kWebTestDirectoryBlacklist = [ - 'test/cupertino', - 'test/examples', - 'test/material', -]; -const List kWebTestFileBlacklist = [ - 'test/widgets/heroes_test.dart', - 'test/widgets/text_test.dart', - 'test/widgets/selectable_text_test.dart', - 'test/widgets/color_filter_test.dart', - 'test/widgets/editable_text_cursor_test.dart', - 'test/widgets/shadow_test.dart', - 'test/widgets/raw_keyboard_listener_test.dart', - 'test/widgets/editable_text_test.dart', - 'test/widgets/widget_inspector_test.dart', - 'test/widgets/draggable_test.dart', - 'test/widgets/shortcuts_test.dart', -]; - -Future _runWebTests() async { - final Directory flutterPackageDir = Directory(path.join(flutterRoot, 'packages', 'flutter')); - final Directory testDir = Directory(path.join(flutterPackageDir.path, 'test')); - - final List directories = testDir - .listSync() - .whereType() - .map((Directory dir) => path.relative(dir.path, from: flutterPackageDir.path)) - .where((String relativePath) => !kWebTestDirectoryBlacklist.contains(relativePath)) - .toList(); - - await _runFlutterWebTest(flutterPackageDir.path, tests: directories); - await _runFlutterWebTest(path.join(flutterRoot, 'packages', 'flutter_web_plugins'), tests: ['test']); -} - -Future _runCoverage() async { +Future _runFrameworkCoverage() async { final File coverageFile = File(path.join(flutterRoot, 'packages', 'flutter', 'coverage', 'lcov.info')); if (!coverageFile.existsSync()) { print('${red}Coverage file not found.$reset'); - print('Expected to find: ${coverageFile.absolute}'); - print('This file is normally obtained by running `flutter update-packages`.'); + print('Expected to find: $cyan${coverageFile.absolute}$reset'); + print('This file is normally obtained by running `${green}flutter update-packages$reset`.'); exit(1); } coverageFile.deleteSync(); @@ -483,11 +440,98 @@ Future _runCoverage() async { ); if (!coverageFile.existsSync()) { print('${red}Coverage file not found.$reset'); - print('Expected to find: ${coverageFile.absolute}'); - print('This file should have been generated by the `flutter test --coverage` script, but was not.'); + print('Expected to find: $cyan${coverageFile.absolute}$reset'); + print('This file should have been generated by the `${green}flutter test --coverage$reset` script, but was not.'); exit(1); } - print('${bold}DONE: Coverage collection successful.$reset'); +} + +Future _runWebTests() async { + final Map subshards = {}; + + final Directory flutterPackageDirectory = Directory(path.join(flutterRoot, 'packages', 'flutter')); + final Directory flutterPackageTestDirectory = Directory(path.join(flutterPackageDirectory.path, 'test')); + + final List allTests = flutterPackageTestDirectory + .listSync() + .whereType() + .where((Directory directory) => !kWebTestDirectoryBlacklist.contains(path.basename(directory.path))) + .expand((Directory directory) => directory + .listSync(recursive: true) + .where((FileSystemEntity entity) => entity.path.endsWith('_test.dart')) + ) + .whereType() + .map((File file) => path.relative(file.path, from: flutterPackageDirectory.path)) + .where((String filePath) => !kWebTestFileBlacklist.contains(filePath)) + .toList() + // Finally we shuffle the list because we want the average cost per file to be uniformly + // distributed. If the list is not sorted then different shards and batches may have + // very different characteristics. + // We use a constant seed for repeatability. + ..shuffle(math.Random(0)); + + assert(kWebShardCount >= 1); + final int testsPerShard = (allTests.length / kWebShardCount).ceil(); + assert(testsPerShard * kWebShardCount >= allTests.length); + + // This for loop computes all but the last shard. + for (int index = 0; index < kWebShardCount - 1; index += 1) { + subshards['$index'] = () => _runFlutterWebTest( + flutterPackageDirectory.path, + allTests.sublist( + index * testsPerShard, + (index + 1) * testsPerShard, + ), + ); + } + + // The last shard also runs the flutter_web_plugins tests. + // + // We make sure the last shard ends in _last so it's easier to catch mismatches + // between `.cirrus.yml` and `test.dart`. + subshards['${kWebShardCount - 1}_last'] = () async { + await _runFlutterWebTest( + flutterPackageDirectory.path, + allTests.sublist( + (kWebShardCount - 1) * testsPerShard, + allTests.length, + ), + ); + await _runFlutterWebTest( + path.join(flutterRoot, 'packages', 'flutter_web_plugins'), + ['test'], + ); + }; + + await selectSubshard(subshards); +} + +Future _runFlutterWebTest(String workingDirectory, List tests) async { + final List batch = []; + for (int i = 0; i < tests.length; i += 1) { + final String testFilePath = tests[i]; + batch.add(testFilePath); + if (batch.length == kWebBatchSize || i == tests.length - 1) { + await runCommand( + flutter, + [ + 'test', + if (ciProvider == CiProviders.cirrus) + '--concurrency=1', // do not parallelize on Cirrus, to reduce flakiness + '-v', + '--platform=chrome', + ...?flutterTestArgs, + ...batch, + ], + workingDirectory: workingDirectory, + environment: { + 'FLUTTER_WEB': 'true', + 'FLUTTER_LOW_RESOURCE_MODE': 'true', + }, + ); + batch.clear(); + } + } } Future _pubRunTest(String workingDirectory, { @@ -496,14 +540,33 @@ Future _pubRunTest(String workingDirectory, { bool useBuildRunner = false, bq.TabledataResourceApi tableData, }) async { - final List args = ['run', '--verbose']; + final List args = ['run']; if (useBuildRunner) { - args.addAll(['build_runner', 'test', '--']); + final String posixTestPath = path.posix.joinAll(path.split(testPath)); + args.addAll([ + 'build_runner', + 'test', + '--build-filter=$posixTestPath/*.dill', + '--build-filter=$posixTestPath/**/*.dill', + '--', + ]); } else { args.add('test'); } args.add(useFlutterTestFormatter ? '-rjson' : '-rcompact'); - args.add('-j1'); // TODO(ianh): Scale based on CPUs. + int cpus; + final String cpuVariable = Platform.environment['CPU']; // CPU is set in cirrus.yml + if (cpuVariable != null) { + cpus = int.tryParse(cpuVariable, radix: 10); + if (cpus == null) { + print('${red}The CPU environment variable, if set, must be set to the integer number of available cores.$reset'); + print('Actual value: "$cpuVariable"'); + exit(1); + } + } else { + cpus = 2; // Don't default to 1, otherwise we won't catch race conditions. + } + args.add('-j$cpus'); if (!hasColor) args.add('--no-color'); if (testPath != null) @@ -547,238 +610,6 @@ Future _pubRunTest(String workingDirectory, { } } -void deleteFile(String path) { - // There's a race condition here but in theory we're not racing anyone - // while this script runs, so should be ok. - final File file = File(path); - if (file.existsSync()) - file.deleteSync(); -} - -enum CiProviders { - cirrus, - luci, -} - -CiProviders _getCiProvider() { - if (Platform.environment['CIRRUS_CI'] == 'true') { - return CiProviders.cirrus; - } - if (Platform.environment['LUCI_CONTEXT'] != null) { - return CiProviders.luci; - } - return null; -} - -String _getCiProviderName() { - switch(_getCiProvider()) { - case CiProviders.cirrus: - return 'cirrusci'; - case CiProviders.luci: - return 'luci'; - } - return 'unknown'; -} - -int _getPrNumber() { - switch(_getCiProvider()) { - case CiProviders.cirrus: - return Platform.environment['CIRRUS_PR'] == null - ? -1 - : int.tryParse(Platform.environment['CIRRUS_PR']); - case CiProviders.luci: - return -1; // LUCI doesn't know about this. - } - return -1; -} - -Future _getAuthors() async { - final String exe = Platform.isWindows ? '.exe' : ''; - final String author = await runAndGetStdout( - 'git$exe', ['-c', 'log.showSignature=false', 'log', _getGitHash(), '--pretty="%an <%ae>"'], - workingDirectory: flutterRoot, - ).first; - return author; -} - -String _getCiUrl() { - switch(_getCiProvider()) { - case CiProviders.cirrus: - return 'https://cirrus-ci.com/task/${Platform.environment['CIRRUS_TASK_ID']}'; - case CiProviders.luci: - return 'https://ci.chromium.org/p/flutter/g/framework/console'; // TODO(dnfield): can we get a direct link to the actual build? - } - return ''; -} - -String _getGitHash() { - switch(_getCiProvider()) { - case CiProviders.cirrus: - return Platform.environment['CIRRUS_CHANGE_IN_REPO']; - case CiProviders.luci: - return 'HEAD'; // TODO(dnfield): Set this in the env for LUCI. - } - return ''; -} - -Future _processTestOutput( - FlutterCompactFormatter formatter, - Stream testOutput, - bq.TabledataResourceApi tableData, -) async { - final Timer heartbeat = Timer.periodic(const Duration(seconds: 30), (Timer timer) { - print('Processing...'); - }); - - await testOutput.forEach(formatter.processRawOutput); - heartbeat.cancel(); - formatter.finish(); - if (tableData == null || formatter.tests.isEmpty) { - return; - } - final bq.TableDataInsertAllRequest request = bq.TableDataInsertAllRequest(); - final String authors = await _getAuthors(); - request.rows = List.from( - formatter.tests.map((TestResult result) => - bq.TableDataInsertAllRequestRows.fromJson( { - 'json': { - 'source': { - 'provider': _getCiProviderName(), - 'url': _getCiUrl(), - 'platform': { - 'os': Platform.operatingSystem, - 'version': Platform.operatingSystemVersion, - }, - }, - 'test': { - 'name': result.name, - 'result': result.status.toString(), - 'file': result.path, - 'line': result.line, - 'column': result.column, - 'time': result.totalTime, - }, - 'git': { - 'author': authors, - 'pull_request': _getPrNumber(), - 'commit': _getGitHash(), - 'organization': 'flutter', - 'repository': 'flutter', - }, - 'error': result.status != TestStatus.failed ? null : { - 'message': result.errorMessage, - 'stack_trace': result.stackTrace, - }, - 'information': result.messages, - }, - }), - ), - growable: false, - ); - final bq.TableDataInsertAllResponse response = await tableData.insertAll(request, 'flutter-infra', 'tests', 'ci'); - if (response.insertErrors != null && response.insertErrors.isNotEmpty) { - print('${red}BigQuery insert errors:'); - print(response.toJson()); - print(reset); - } -} - -class EvalResult { - EvalResult({ - this.stdout, - this.stderr, - this.exitCode = 0, - }); - - final String stdout; - final String stderr; - final int exitCode; -} - -/// The number of Cirrus jobs that run web tests in parallel. -/// -/// WARNING: if you change this number, also change .cirrus.yml -/// and make sure it runs _all_ shards. -const int _kWebShardCount = 6; - -Future _runFlutterWebTest(String workingDirectory, { - List tests, -}) async { - List allTests = []; - for (String testDirPath in tests) { - final Directory testDir = Directory(path.join(workingDirectory, testDirPath)); - allTests.addAll( - testDir.listSync(recursive: true) - .whereType() - .where((File file) => file.path.endsWith('_test.dart')) - .map((File file) => path.relative(file.path, from: workingDirectory)) - .where((String filePath) => !kWebTestFileBlacklist.contains(filePath)), - ); - } - - // If a shard is specified only run tests in that shard. - final int webShard = int.tryParse(Platform.environment['WEB_SHARD'] ?? 'n/a'); - if (webShard != null) { - if (webShard >= _kWebShardCount) { - throw 'WEB_SHARD must be <= _kWebShardCount, but was $webShard'; - } - final List shard = []; - for (int i = webShard; i < allTests.length; i += _kWebShardCount) { - shard.add(allTests[i]); - } - allTests = shard; - } - - print(allTests.join('\n')); - print('${allTests.length} tests total'); - - // Maximum number of tests to run in a single `flutter test`. We found that - // large batches can get flaky, possibly because we reuse a single instance - // of the browser, and after many tests the browser's state gets corrupted. - const int kBatchSize = 20; - List batch = []; - for (int i = 0; i < allTests.length; i += 1) { - final String testFilePath = allTests[i]; - batch.add(testFilePath); - if (batch.length == kBatchSize || i == allTests.length - 1) { - await _runFlutterWebTestBatch(workingDirectory, batch: batch); - batch = []; - } - } -} - -Future _runFlutterWebTestBatch(String workingDirectory, { - List batch, -}) async { - final List args = [ - 'test', - if (_getCiProvider() == CiProviders.cirrus) - '--concurrency=1', // do not parallelize on Cirrus to reduce flakiness - '-v', - '--platform=chrome', - ...?flutterTestArgs, - ...batch, - ]; - - // TODO(jonahwilliams): fix relative path issues to make this unecessary. - final Directory oldCurrent = Directory.current; - Directory.current = Directory(path.join(flutterRoot, 'packages', 'flutter')); - try { - await runCommand( - flutter, - args, - workingDirectory: workingDirectory, - expectFlaky: false, - environment: { - 'FLUTTER_WEB': 'true', - 'FLUTTER_LOW_RESOURCE_MODE': 'true', - }, - ); - } finally { - Directory.current = oldCurrent; - } -} - Future _runFlutterTest(String workingDirectory, { String script, bool expectFailure = false, @@ -790,8 +621,7 @@ Future _runFlutterTest(String workingDirectory, { Map environment, List tests = const [], }) async { - assert(!printOutput || outputChecker == null, - 'Output either can be printed or checked but not both'); + assert(!printOutput || outputChecker == null, 'Output either can be printed or checked but not both'); final List args = [ 'test', @@ -800,16 +630,15 @@ Future _runFlutterTest(String workingDirectory, { ]; final bool shouldProcessOutput = useFlutterTestFormatter && !expectFailure && !options.contains('--coverage'); - if (shouldProcessOutput) { + if (shouldProcessOutput) args.add('--machine'); - } if (script != null) { final String fullScriptPath = path.join(workingDirectory, script); if (!FileSystemEntity.isFileSync(fullScriptPath)) { - print('Could not find test: $fullScriptPath'); - print('Working directory: $workingDirectory'); - print('Script: $script'); + print('${red}Could not find test$reset: $green$fullScriptPath$reset'); + print('Working directory: $cyan$workingDirectory$reset'); + print('Script: $green$script$reset'); if (!printOutput) print('This is one of the tests that does not normally print output.'); if (skip) @@ -876,110 +705,290 @@ Future _runFlutterTest(String workingDirectory, { } } -// the optional `file` argument is an override for testing -@visibleForTesting -Future verifyVersion(String filename, [File file]) async { - final RegExp pattern = RegExp(r'^\d+\.\d+\.\d+(\+hotfix\.\d+)?(-pre\.\d+)?$'); - file ??= File(filename); - final String version = await file.readAsString(); - if (!file.existsSync()) { - print('$redLine'); - print('The version logic failed to create the Flutter version file.'); - print('$redLine'); - return false; - } - if (version == '0.0.0-unknown') { - print('$redLine'); - print('The version logic failed to determine the Flutter version.'); - print('$redLine'); - return false; - } - if (!version.contains(pattern)) { - print('$redLine'); - print('The version logic generated an invalid version string: "$version".'); - print('$redLine'); - return false; - } - return true; -} - -Future _runIntegrationTests() async { - final String subShard = Platform.environment['SUBSHARD']; - - switch (subShard) { - case 'gradle1': - case 'gradle2': - // This runs some gradle integration tests if the subshard is Android. - await _androidGradleTests(subShard); - break; - default: - await _runDevicelabTest('dartdocs'); - - if (Platform.isLinux) { - await _runDevicelabTest('flutter_create_offline_test_linux'); - } else if (Platform.isWindows) { - await _runDevicelabTest('flutter_create_offline_test_windows'); - } else if (Platform.isMacOS) { - await _runDevicelabTest('flutter_create_offline_test_mac'); - await _runDevicelabTest('plugin_lint_mac'); -// TODO(jmagman): Re-enable once flakiness is resolved. -// await _runDevicelabTest('module_test_ios'); - } - } -} - -Future _runDevicelabTest(String testName, {Map env}) async { - await runCommand( - dart, - ['bin/run.dart', '-t', testName], - workingDirectory: path.join(flutterRoot, 'dev', 'devicelab'), - environment: env, - ); -} - -String get androidSdkRoot { +Map _initGradleEnvironment() { final String androidSdkRoot = (Platform.environment['ANDROID_HOME']?.isEmpty ?? true) ? Platform.environment['ANDROID_SDK_ROOT'] : Platform.environment['ANDROID_HOME']; if (androidSdkRoot == null || androidSdkRoot.isEmpty) { - return null; + print('${red}Could not find Android SDK; set ANDROID_SDK_ROOT (or ANDROID_HOME).$reset'); + exit(1); } - return androidSdkRoot; -} - -Future _androidGradleTests(String subShard) async { - // TODO(dnfield): gradlew is crashing on the cirrus image and it's not clear why. - if (androidSdkRoot == null || Platform.isWindows) { - print('No Android SDK detected or on Windows, skipping Android gradle test.'); - return; - } - final Map defaultEnv = { + return { 'ANDROID_HOME': androidSdkRoot, 'ANDROID_SDK_ROOT': androidSdkRoot, - 'ENABLE_ANDROID_EMBEDDING_V2': Platform.environment['ENABLE_ANDROID_EMBEDDING_V2'] ?? '', }; - if (subShard == 'gradle1') { - await _runDevicelabTest('gradle_plugin_light_apk_test', env: defaultEnv); - await _runDevicelabTest('gradle_plugin_fat_apk_test', env: defaultEnv); - await _runDevicelabTest('gradle_r8_test', env: defaultEnv); - await _runDevicelabTest('gradle_non_android_plugin_test', env: defaultEnv); - await _runDevicelabTest('gradle_jetifier_test', env: defaultEnv); +} + +final Map gradleEnvironment = _initGradleEnvironment(); + +Future _runHostOnlyDeviceLabTests() async { + if (Platform.isWindows) { + // TODO(ianh): remove when https://github.com/flutter/flutter/issues/36311 fixed by https://github.com/flutter/flutter/pull/42709 + return; } - if (subShard == 'gradle2') { - await _runDevicelabTest('gradle_plugin_bundle_test', env: defaultEnv); - await _runDevicelabTest('module_test', env: defaultEnv); - await _runDevicelabTest('module_host_with_custom_build_test', env: defaultEnv); - await _runDevicelabTest('build_aar_module_test', env: defaultEnv); - await _runDevicelabTest('plugin_test', env: defaultEnv); + + // Please don't add more tests here. We should not be using the devicelab + // logic to run tests outside devicelab, that's just confusing. + // Instead, create tests that are not devicelab tests, and run those. + + // TODO(ianh): Move the tests that are not running on devicelab any more out + // of the device lab directory. + + // List the tests to run. + // We split these into subshards. The tests are randomly distributed into + // those subshards so as to get a uniform distribution of costs, but the + // seed is fixed so that issues are reproducible. + final List tests = [ + // Keep this in alphabetical order. + () => _runDevicelabTest('build_aar_module_test', environment: gradleEnvironment, testEmbeddingV2: true), + () => _runDevicelabTest('build_aar_module_test', environment: gradleEnvironment, testEmbeddingV2: false), + if (Platform.isMacOS) () => _runDevicelabTest('flutter_create_offline_test_mac'), + if (Platform.isLinux) () => _runDevicelabTest('flutter_create_offline_test_linux'), + if (Platform.isWindows) () => _runDevicelabTest('flutter_create_offline_test_windows'), + // TODO(ianh): Fails on macOS looking for "dexdump", https://github.com/flutter/flutter/issues/42494 + if (!Platform.isMacOS) () => _runDevicelabTest('gradle_jetifier_test', environment: gradleEnvironment, testEmbeddingV2: false), + if (!Platform.isMacOS) () => _runDevicelabTest('gradle_jetifier_test', environment: gradleEnvironment, testEmbeddingV2: true), + () => _runDevicelabTest('gradle_non_android_plugin_test', environment: gradleEnvironment, testEmbeddingV2: false), + () => _runDevicelabTest('gradle_non_android_plugin_test', environment: gradleEnvironment, testEmbeddingV2: true), + () => _runDevicelabTest('gradle_plugin_bundle_test', environment: gradleEnvironment, testEmbeddingV2: false), + () => _runDevicelabTest('gradle_plugin_bundle_test', environment: gradleEnvironment, testEmbeddingV2: true), + () => _runDevicelabTest('gradle_plugin_fat_apk_test', environment: gradleEnvironment, testEmbeddingV2: false), + () => _runDevicelabTest('gradle_plugin_fat_apk_test', environment: gradleEnvironment, testEmbeddingV2: true), + () => _runDevicelabTest('gradle_plugin_light_apk_test', environment: gradleEnvironment, testEmbeddingV2: false), + () => _runDevicelabTest('gradle_plugin_light_apk_test', environment: gradleEnvironment, testEmbeddingV2: true), + () => _runDevicelabTest('gradle_r8_test', environment: gradleEnvironment, testEmbeddingV2: false), + () => _runDevicelabTest('gradle_r8_test', environment: gradleEnvironment, testEmbeddingV2: true), + () => _runDevicelabTest('module_host_with_custom_build_test', environment: gradleEnvironment, testEmbeddingV2: false), + () => _runDevicelabTest('module_host_with_custom_build_test', environment: gradleEnvironment, testEmbeddingV2: true), + () => _runDevicelabTest('module_test', environment: gradleEnvironment, testEmbeddingV2: false), + () => _runDevicelabTest('module_test', environment: gradleEnvironment, testEmbeddingV2: true), + // TODO(jmagman): Re-enable once flakiness is resolved, https://github.com/flutter/flutter/issues/37525 + // if (Platform.isMacOS) () => _runDevicelabTest('module_test_ios'), + if (Platform.isMacOS) () => _runDevicelabTest('plugin_lint_mac'), + () => _runDevicelabTest('plugin_test', environment: gradleEnvironment, testEmbeddingV2: false), + () => _runDevicelabTest('plugin_test', environment: gradleEnvironment, testEmbeddingV2: true), + ]..shuffle(math.Random(0)); + + final int testsPerShard = tests.length ~/ kDeviceLabShardCount; + final Map subshards = {}; + + for (int subshard = 0; subshard < kDeviceLabShardCount; subshard += 1) { + String last = ''; + List sublist; + if (subshard < kDeviceLabShardCount - 1) { + sublist = tests.sublist(subshard * testsPerShard, (subshard + 1) * testsPerShard); + } else { + sublist = tests.sublist(subshard * testsPerShard, tests.length); + // We make sure the last shard ends in _last so it's easier to catch mismatches + // between `.cirrus.yml` and `test.dart`. + last = '_last'; + } + subshards['$subshard$last'] = () async { + for (ShardRunner test in sublist) + await test(); + }; + } + + await selectSubshard(subshards); +} + +Future _runDevicelabTest(String testName, { + Map environment, + bool testEmbeddingV2 = false, +}) async { + await runCommand( + dart, + ['bin/run.dart', '-t', testName], + workingDirectory: path.join(flutterRoot, 'dev', 'devicelab'), + environment: { + ...?environment, + if (testEmbeddingV2) + 'ENABLE_ANDROID_EMBEDDING_V2': 'true', + }, + ); +} + +void deleteFile(String path) { + // This is technically a race condition but nobody else should be running + // while this script runs, so we should be ok. (Sadly recursive:true does not + // obviate the need for existsSync, at least on Windows.) + final File file = File(path); + if (file.existsSync()) + file.deleteSync(); +} + +enum CiProviders { + cirrus, + luci, +} + +Future _processTestOutput( + FlutterCompactFormatter formatter, + Stream testOutput, + bq.TabledataResourceApi tableData, +) async { + final Timer heartbeat = Timer.periodic(const Duration(seconds: 30), (Timer timer) { + print('Processing...'); + }); + + await testOutput.forEach(formatter.processRawOutput); + heartbeat.cancel(); + formatter.finish(); + if (tableData == null || formatter.tests.isEmpty) { + return; + } + final bq.TableDataInsertAllRequest request = bq.TableDataInsertAllRequest(); + final String authors = await _getAuthors(); + request.rows = List.from( + formatter.tests.map((TestResult result) => + bq.TableDataInsertAllRequestRows.fromJson( { + 'json': { + 'source': { + 'provider': ciProviderName, + 'url': ciUrl, + 'platform': { + 'os': Platform.operatingSystem, + 'version': Platform.operatingSystemVersion, + }, + }, + 'test': { + 'name': result.name, + 'result': result.status.toString(), + 'file': result.path, + 'line': result.line, + 'column': result.column, + 'time': result.totalTime, + }, + 'git': { + 'author': authors, + 'pull_request': prNumber, + 'commit': gitHash, + 'organization': 'flutter', + 'repository': 'flutter', + }, + 'error': result.status != TestStatus.failed ? null : { + 'message': result.errorMessage, + 'stack_trace': result.stackTrace, + }, + 'information': result.messages, + }, + }), + ), + growable: false, + ); + final bq.TableDataInsertAllResponse response = await tableData.insertAll(request, 'flutter-infra', 'tests', 'ci'); + if (response.insertErrors != null && response.insertErrors.isNotEmpty) { + print('${red}BigQuery insert errors:'); + print(response.toJson()); + print(reset); } } -Future selectShard(Map shards) => _runFromList(shards, 'SHARD', 'shard'); -Future selectSubshard(Map subshards) => _runFromList(subshards, 'SUBSHARD', 'subshard'); +CiProviders get ciProvider { + if (Platform.environment['CIRRUS_CI'] == 'true') { + return CiProviders.cirrus; + } + if (Platform.environment['LUCI_CONTEXT'] != null) { + return CiProviders.luci; + } + return null; +} -Future _runFromList(Map items, String key, String name) async { - final String item = Platform.environment[key]; - if (item != null) { +String get ciProviderName { + switch (ciProvider) { + case CiProviders.cirrus: + return 'cirrusci'; + case CiProviders.luci: + return 'luci'; + } + return 'unknown'; +} + +int get prNumber { + switch (ciProvider) { + case CiProviders.cirrus: + return Platform.environment['CIRRUS_PR'] == null + ? -1 + : int.tryParse(Platform.environment['CIRRUS_PR']); + case CiProviders.luci: + return -1; // LUCI doesn't know about this. + } + return -1; +} + +Future _getAuthors() async { + final String exe = Platform.isWindows ? '.exe' : ''; + final String author = await runAndGetStdout( + 'git$exe', ['-c', 'log.showSignature=false', 'log', gitHash, '--pretty="%an <%ae>"'], + workingDirectory: flutterRoot, + ).first; + return author; +} + +String get ciUrl { + switch (ciProvider) { + case CiProviders.cirrus: + return 'https://cirrus-ci.com/task/${Platform.environment['CIRRUS_TASK_ID']}'; + case CiProviders.luci: + return 'https://ci.chromium.org/p/flutter/g/framework/console'; // TODO(dnfield): can we get a direct link to the actual build? + } + return ''; +} + +String get gitHash { + switch(ciProvider) { + case CiProviders.cirrus: + return Platform.environment['CIRRUS_CHANGE_IN_REPO']; + case CiProviders.luci: + return 'HEAD'; // TODO(dnfield): Set this in the env for LUCI. + } + return ''; +} + +/// Checks the given file's contents to determine if they match the allowed +/// pattern for version strings. +/// +/// Returns null if the contents are good. Returns a string if they are bad. +/// The string is an error message. +Future verifyVersion(File file) async { + final RegExp pattern = RegExp(r'^\d+\.\d+\.\d+(\+hotfix\.\d+)?(-pre\.\d+)?$'); + final String version = await file.readAsString(); + if (!file.existsSync()) + return 'The version logic failed to create the Flutter version file.'; + if (version == '0.0.0-unknown') + return 'The version logic failed to determine the Flutter version.'; + if (!version.contains(pattern)) + return 'The version logic generated an invalid version string: "$version".'; + return null; +} + +/// If the CIRRUS_TASK_NAME environment variable exists, we use that to determine +/// the shard and subshard (parsing it in the form shard-subshard-platform, ignoring +/// the platform). +/// +/// However, for local testing you can just set the SHARD and SUBSHARD +/// environment variables. For example, to run all the framework tests you can +/// just set SHARD=framework_tests. To run specifically the third subshard of +/// the Web tests you can set SHARD=web_tests SUBSHARD=2 (it's zero-based). +Future selectShard(Map shards) => _runFromList(shards, 'SHARD', 'shard', 0); +Future selectSubshard(Map subshards) => _runFromList(subshards, 'SUBSHARD', 'subshard', 1); + +const String CIRRUS_TASK_NAME = 'CIRRUS_TASK_NAME'; + +Future _runFromList(Map items, String key, String name, int positionInTaskName) async { + String item = Platform.environment[key]; + if (item == null && Platform.environment.containsKey(CIRRUS_TASK_NAME)) { + final List parts = Platform.environment[CIRRUS_TASK_NAME].split('-'); + assert(positionInTaskName < parts.length); + item = parts[positionInTaskName]; + } + if (item == null) { + for (String currentItem in items.keys) { + print('$bold$key=$currentItem$reset'); + await items[currentItem](); + print(''); + } + } else { if (!items.containsKey(item)) { print('${red}Invalid $name: $item$reset'); print('The available ${name}s are: ${items.keys.join(", ")}'); @@ -987,11 +996,5 @@ Future _runFromList(Map items, String key, String nam } print('$bold$key=$item$reset'); await items[item](); - } else { - for (String currentItem in items.keys) { - print('$bold$key=$currentItem$reset'); - await items[currentItem](); - print(''); - } } } diff --git a/dev/bots/test/bot_test.dart b/dev/bots/test/bot_test.dart new file mode 100644 index 00000000000..476280c414c --- /dev/null +++ b/dev/bots/test/bot_test.dart @@ -0,0 +1,13 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'common.dart'; + +void main() { + test('BOT variable is set on bots', () { + expect(Platform.environment['BOT'], 'true'); + }); +} diff --git a/dev/bots/test/sdk_directory_has_space_test.dart b/dev/bots/test/sdk_directory_has_space_test.dart new file mode 100644 index 00000000000..f48dbdbfdd5 --- /dev/null +++ b/dev/bots/test/sdk_directory_has_space_test.dart @@ -0,0 +1,21 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'common.dart'; + +void main() { + test('We are in a directory with a space in it', () async { + // The Flutter SDK should be in a directory with a space in it, to make sure + // our tools support that. + final String expectedName = Platform.environment['FLUTTER_SDK_PATH_WITH_SPACE']; + expect(expectedName, 'flutter sdk'); + expect(expectedName, contains(' ')); + final List parts = path.split(Directory.current.absolute.path); + expect(parts.reversed.take(3), ['bots', 'dev', expectedName]); + }); +} diff --git a/dev/bots/test/test_test.dart b/dev/bots/test/test_test.dart index a3abf6a1be8..31df83f6eed 100644 --- a/dev/bots/test/test_test.dart +++ b/dev/bots/test/test_test.dart @@ -29,7 +29,11 @@ void main() { ]; for (String version in valid_versions) { when(file.readAsString()).thenAnswer((Invocation invocation) => Future.value(version)); - expect(await verifyVersion(version, file), isTrue, reason: '$version is invalid'); + expect( + await verifyVersion(file), + isNull, + reason: '$version is valid but verifyVersionFile said it was bad', + ); } }); @@ -41,10 +45,15 @@ void main() { '1.2.3-pre', '1.2.3-pre.1+hotfix.1', ' 1.2.3', + '1.2.3-hotfix.1', ]; for (String version in invalid_versions) { when(file.readAsString()).thenAnswer((Invocation invocation) => Future.value(version)); - expect(await verifyVersion(version, file), isFalse); + expect( + await verifyVersion(file), + 'The version logic generated an invalid version string: "$version".', + reason: '$version is invalid but verifyVersionFile said it was fine', + ); } }); }); diff --git a/packages/flutter_tools/dart_test.yaml b/packages/flutter_tools/dart_test.yaml index cd8c697cba8..789a2554668 100644 --- a/packages/flutter_tools/dart_test.yaml +++ b/packages/flutter_tools/dart_test.yaml @@ -1,5 +1,9 @@ -tags: - "no_coverage": - "create": - "integration": - +# package:test configuration +# https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md + +# Some of our tests take an absurdly long time to run, and on some +# hosts they can take even longer due to the host suddenly being +# overloaded. For this reason, we set the test timeout to +# significantly more than it would be by default, and we never set the +# timeouts in the tests themselves. +timeout: 15m diff --git a/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart index abad5430c34..4cf3b1763f6 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart @@ -135,7 +135,7 @@ void main() { return _updateIdeConfig( expectedContents: expectedContents, ); - }, timeout: const Timeout.factor(2.0)); + }); testUsingContext('creates non-existent files', () async { final Map templateManifest = _getManifest( @@ -155,7 +155,7 @@ void main() { return _updateIdeConfig( expectedContents: expectedContents, ); - }, timeout: const Timeout.factor(2.0)); + }); testUsingContext('overwrites existing files with --overwrite', () async { final Map templateManifest = _getManifest( @@ -181,7 +181,7 @@ void main() { args: ['--overwrite'], expectedContents: expectedContents, ); - }, timeout: const Timeout.factor(2.0)); + }); testUsingContext('only adds new templates without --overwrite', () async { final Map templateManifest = _getManifest( @@ -212,7 +212,7 @@ void main() { args: ['--update-templates'], expectedContents: expectedContents, ); - }, timeout: const Timeout.factor(2.0)); + }); testUsingContext('update all templates with --overwrite', () async { final Map templateManifest = _getManifest( @@ -239,7 +239,7 @@ void main() { args: ['--update-templates', '--overwrite'], expectedContents: expectedContents, ); - }, timeout: const Timeout.factor(2.0)); + }); testUsingContext('removes deleted imls with --overwrite', () async { final Map templateManifest = _getManifest( @@ -275,7 +275,7 @@ void main() { args: ['--update-templates', '--overwrite'], expectedContents: expectedContents, ); - }, timeout: const Timeout.factor(2.0)); + }); testUsingContext('removes deleted imls with --overwrite, including empty parent dirs', () async { final Map templateManifest = _getManifest( @@ -316,7 +316,7 @@ void main() { args: ['--update-templates', '--overwrite'], expectedContents: expectedContents, ); - }, timeout: const Timeout.factor(2.0)); + }); }); } diff --git a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart index 38b89803827..b1c2a8a526c 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart @@ -16,9 +16,6 @@ import 'package:flutter_tools/src/runner/flutter_command.dart'; import '../../src/common.dart'; import '../../src/context.dart'; -/// Test case timeout for tests involving project analysis. -const Timeout allowForSlowAnalyzeTests = Timeout.factor(5.0); - final Generator _kNoColorTerminalPlatform = () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false; final Map noColorTerminalOverride = { Platform: _kNoColorTerminalPlatform, @@ -54,7 +51,7 @@ void main() { ], ); expect(libMain.existsSync(), isTrue); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -65,7 +62,7 @@ void main() { arguments: ['analyze'], statusTextContains: ['No issues found!'], ); - }, timeout: allowForSlowAnalyzeTests, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -80,6 +77,7 @@ void main() { }, overrides: { Pub: () => const Pub(), }); + // Analyze in the current directory - no arguments testUsingContext('working directory with errors', () async { // Break the code to produce the "The parameter 'onPressed' is required" hint @@ -110,7 +108,7 @@ void main() { exitMessageContains: '2 issues found.', toolExit: true, ); - }, timeout: allowForSlowAnalyzeTests, overrides: { + }, overrides: { Pub: () => const Pub(), ...noColorTerminalOverride, }); @@ -140,7 +138,7 @@ void main() { exitMessageContains: '3 issues found.', toolExit: true, ); - }, timeout: allowForSlowAnalyzeTests, overrides: { + }, overrides: { Pub: () => const Pub(), ...noColorTerminalOverride }); diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart index 167326b25e1..f732171bcf6 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart @@ -75,7 +75,7 @@ void main() { expect(await command.usageValues, containsPair(CustomDimensions.commandBuildBundleIsModule, 'true')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('bundle getUsage indicate that project is not a module', () async { final String projectPath = await createProject(tempDir, @@ -85,7 +85,7 @@ void main() { expect(await command.usageValues, containsPair(CustomDimensions.commandBuildBundleIsModule, 'false')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('bundle getUsage indicate the target platform', () async { final String projectPath = await createProject(tempDir, @@ -95,7 +95,7 @@ void main() { expect(await command.usageValues, containsPair(CustomDimensions.commandBuildBundleTargetPlatform, 'android-arm')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('bundle fails to build for Windows if feature is disabled', () async { fs.file('lib/main.dart').createSync(recursive: true); diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart index 2056e490078..61658955bc0 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -78,7 +78,7 @@ void main() { ], ); return _runFlutterTest(projectDir); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -96,7 +96,7 @@ void main() { 'ios/Runner/GeneratedPluginRegistrant.h', ], ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -118,11 +118,9 @@ void main() { 'ios/', ]); return _runFlutterTest(projectDir); - }, - timeout: allowForRemotePubInvocation, overrides: { - Pub: () => const Pub(), - }, - ); + }, overrides: { + Pub: () => const Pub(), + }); testUsingContext('cannot create a project if non-empty non-project directory exists with .metadata', () async { await projectDir.absolute.childDirectory('blag').create(recursive: true); @@ -138,7 +136,7 @@ void main() { '.ios/', ]), throwsToolExit(message: 'Sorry, unable to detect the type of project to recreate')); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), ...noColorTerminalOverride, }); @@ -164,7 +162,7 @@ void main() { '.ios/', ], ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -189,7 +187,7 @@ void main() { '.ios/', ], ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -214,7 +212,7 @@ void main() { 'lib/flutter_project.dart', ], ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -245,7 +243,7 @@ void main() { 'test/widget_test.dart', ], ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -267,7 +265,7 @@ void main() { 'ios/Runner/main.m', ], ); - }, timeout: allowForCreateFlutterProject, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -297,7 +295,7 @@ void main() { ], ); return _runFlutterTest(projectDir); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -319,7 +317,7 @@ void main() { ], ); return _runFlutterTest(projectDir.childDirectory('example')); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -346,7 +344,7 @@ void main() { 'example/ios/Runner/main.m', ], ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('plugin project with custom org', () async { return _createProject( @@ -366,7 +364,7 @@ void main() { 'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java', ], ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('plugin project with valid custom project name', () async { return _createProject( @@ -386,7 +384,7 @@ void main() { 'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java', ], ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('plugin project with invalid custom project name', () async { expect( @@ -396,7 +394,7 @@ void main() { ), throwsToolExit(message: '"xyz.xyz" is not a valid Dart package name.'), ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('legacy app project with-driver-test', () async { return _createAndAnalyzeProject( @@ -404,7 +402,7 @@ void main() { ['--with-driver-test', '--template=app'], ['lib/main.dart'], ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -439,7 +437,7 @@ void main() { 'android/', 'ios/', ]); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -463,7 +461,7 @@ void main() { final String actualContents = await fs.file(projectDir.path + '/android/gradle.properties').readAsString(); expect(actualContents.contains('useAndroidX'), true); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('non androidx app project', () async { Cache.flutterRoot = '../..'; @@ -484,7 +482,7 @@ void main() { final String actualContents = await fs.file(projectDir.path + '/android/gradle.properties').readAsString(); expect(actualContents.contains('useAndroidX'), false); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('androidx is used by default in a module project', () async { Cache.flutterRoot = '../..'; @@ -501,7 +499,7 @@ void main() { project.usesAndroidX, true, ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('non androidx module', () async { Cache.flutterRoot = '../..'; @@ -518,7 +516,7 @@ void main() { project.usesAndroidX, false, ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('androidx is used by default in a plugin project', () async { Cache.flutterRoot = '../..'; @@ -539,7 +537,7 @@ void main() { final String actualContents = await fs.file(projectDir.path + '/android/gradle.properties').readAsString(); expect(actualContents.contains('useAndroidX'), true); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('non androidx plugin project', () async { Cache.flutterRoot = '../..'; @@ -560,7 +558,7 @@ void main() { final String actualContents = await fs.file(projectDir.path + '/android/gradle.properties').readAsString(); expect(actualContents.contains('useAndroidX'), false); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('app supports macOS if requested', () async { Cache.flutterRoot = '../..'; @@ -573,7 +571,7 @@ void main() { await runner.run(['create', '--no-pub', '--macos', projectDir.path]); expect(projectDir.childDirectory('macos').childDirectory('Runner.xcworkspace').existsSync(), true); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('app does not include macOS by default', () async { Cache.flutterRoot = '../..'; @@ -586,7 +584,7 @@ void main() { await runner.run(['create', '--no-pub', projectDir.path]); expect(projectDir.childDirectory('macos').childDirectory('Runner.xcworkspace').existsSync(), false); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('plugin supports macOS if requested', () async { Cache.flutterRoot = '../..'; @@ -599,7 +597,7 @@ void main() { await runner.run(['create', '--no-pub', '--template=plugin', '--macos', projectDir.path]); expect(projectDir.childDirectory('macos').childFile('flutter_project.podspec').existsSync(), true); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('plugin does not include macOS by default', () async { Cache.flutterRoot = '../..'; @@ -612,7 +610,7 @@ void main() { await runner.run(['create', '--no-pub', '--template=plugin', projectDir.path]); expect(projectDir.childDirectory('macos').childFile('flutter_project.podspec').existsSync(), false); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('has correct content and formatting with module template', () async { Cache.flutterRoot = '../..'; @@ -708,7 +706,7 @@ void main() { }, overrides: { FlutterVersion: () => mockFlutterVersion, Platform: _kNoColorTerminalPlatform, - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('has correct content and formatting with app template', () async { Cache.flutterRoot = '../..'; @@ -779,7 +777,7 @@ void main() { }, overrides: { FlutterVersion: () => mockFlutterVersion, Platform: _kNoColorTerminalPlatform, - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('has correct application id for android and bundle id for ios', () async { Cache.flutterRoot = '../..'; @@ -827,7 +825,7 @@ void main() { }, overrides: { FlutterVersion: () => mockFlutterVersion, Platform: _kNoColorTerminalPlatform, - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen default template over existing project', () async { Cache.flutterRoot = '../..'; @@ -841,7 +839,7 @@ void main() { final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync(); expect(metadata, contains('project_type: app\n')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen default template over existing app project with no metadta and detect the type', () async { Cache.flutterRoot = '../..'; @@ -858,7 +856,7 @@ void main() { final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync(); expect(metadata, contains('project_type: app\n')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen app template over existing app project and detect the type', () async { Cache.flutterRoot = '../..'; @@ -872,7 +870,7 @@ void main() { final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync(); expect(metadata, contains('project_type: app\n')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen template over existing module project and detect the type', () async { Cache.flutterRoot = '../..'; @@ -886,7 +884,7 @@ void main() { final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync(); expect(metadata, contains('project_type: module\n')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen default template over existing plugin project and detect the type', () async { Cache.flutterRoot = '../..'; @@ -900,7 +898,7 @@ void main() { final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync(); expect(metadata, contains('project_type: plugin')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen default template over existing package project and detect the type', () async { Cache.flutterRoot = '../..'; @@ -914,7 +912,7 @@ void main() { final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync(); expect(metadata, contains('project_type: package')); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen module .android/ folder, reusing custom org', () async { await _createProject( @@ -930,7 +928,7 @@ void main() { '.android/app/src/main/java/com/bar/foo/flutter_project/host/MainActivity.java', ], ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -947,7 +945,7 @@ void main() { await project.ios.productBundleIdentifier, 'com.bar.foo.flutterProject', ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -974,7 +972,7 @@ void main() { 'android/app/src/main/java/com/example/flutter_project/MainActivity.java', ], ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen app ios/ folder, reusing custom org', () async { await _createProject( @@ -989,7 +987,7 @@ void main() { await project.ios.productBundleIdentifier, 'com.bar.foo.flutterProject', ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('can re-gen plugin ios/ and example/ folders, reusing custom org', () async { await _createProject( @@ -1022,7 +1020,7 @@ void main() { await project.example.ios.productBundleIdentifier, 'com.bar.foo.flutterProjectExample', ); - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('fails to re-gen without specified org when org is ambiguous', () async { await _createProject( @@ -1040,7 +1038,7 @@ void main() { () => _createProject(projectDir, [], []), throwsToolExit(message: 'Ambiguous organization'), ); - }, timeout: allowForCreateFlutterProject); + }); // Verify that we help the user correct an option ordering issue testUsingContext('produces sensible error message', () async { @@ -1102,7 +1100,7 @@ void main() { 'ios/Runner/GeneratedPluginRegistrant.h', ], ); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -1128,7 +1126,6 @@ void main() { expect(loggingProcessManager.commands.first, contains(matches(r'dart-sdk[\\/]bin[\\/]pub'))); expect(loggingProcessManager.commands.first, contains('--offline')); }, - timeout: allowForCreateFlutterProject, overrides: { ProcessManager: () => loggingProcessManager, Pub: () => const Pub(), @@ -1147,7 +1144,6 @@ void main() { expect(loggingProcessManager.commands.first, contains(matches(r'dart-sdk[\\/]bin[\\/]pub'))); expect(loggingProcessManager.commands.first, isNot(contains('--offline'))); }, - timeout: allowForCreateFlutterProject, overrides: { ProcessManager: () => loggingProcessManager, Pub: () => const Pub(), @@ -1168,7 +1164,7 @@ void main() { ); expect(projectDir.childDirectory('lib').childFile('main.dart').readAsStringSync(), contains('void main() {}')); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { HttpClientFactory: () => () => MockHttpClient(200, result: 'void main() {}'), }); diff --git a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart index 07da8237cfd..a9197ed4131 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart @@ -207,7 +207,7 @@ void main() { expectDependenciesResolved(projectPath); expectZeroPluginsInjected(projectPath); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -220,7 +220,7 @@ void main() { expectDependenciesResolved(projectPath); expectZeroPluginsInjected(projectPath); - }, timeout: allowForCreateFlutterProject, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -234,7 +234,7 @@ void main() { expect(await getCommand.usageValues, containsPair(CustomDimensions.commandPackagesNumberPlugins, '0')); - }, timeout: allowForCreateFlutterProject, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -248,7 +248,7 @@ void main() { expect(await getCommand.usageValues, containsPair(CustomDimensions.commandPackagesProjectModule, 'false')); - }, timeout: allowForCreateFlutterProject, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -262,7 +262,7 @@ void main() { expect(await getCommand.usageValues, containsPair(CustomDimensions.commandPackagesProjectModule, 'true')); - }, timeout: allowForCreateFlutterProject, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -275,7 +275,7 @@ void main() { expectDependenciesResolved(projectPath); expectZeroPluginsInjected(projectPath); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -288,7 +288,7 @@ void main() { expectDependenciesResolved(projectPath); expectModulePluginInjected(projectPath); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); @@ -309,7 +309,7 @@ void main() { expectDependenciesResolved(exampleProjectPath); expectPluginInjected(exampleProjectPath); - }, timeout: allowForRemotePubInvocation, overrides: { + }, overrides: { Pub: () => const Pub(), }); }); diff --git a/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart b/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart index 1591e75da52..a93b50e2f3d 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart @@ -57,7 +57,7 @@ void main() { }, overrides: { AndroidBuilder: () => FakeAndroidBuilder(), - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('indicate that project is a plugin', () async { final String projectPath = await createProject(tempDir, @@ -69,7 +69,7 @@ void main() { }, overrides: { AndroidBuilder: () => FakeAndroidBuilder(), - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('indicate the target platform', () async { final String projectPath = await createProject(tempDir, @@ -82,7 +82,7 @@ void main() { }, overrides: { AndroidBuilder: () => FakeAndroidBuilder(), - }, timeout: allowForCreateFlutterProject); + }); }); group('Gradle', () { diff --git a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart index a67c9fa6ffd..05fa1ff5d12 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart @@ -48,7 +48,7 @@ void main() { }, overrides: { AndroidBuilder: () => FakeAndroidBuilder(), - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('split per abi', () async { final String projectPath = await createProject(tempDir, @@ -65,7 +65,7 @@ void main() { }, overrides: { AndroidBuilder: () => FakeAndroidBuilder(), - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('build type', () async { final String projectPath = await createProject(tempDir, @@ -92,7 +92,7 @@ void main() { }, overrides: { AndroidBuilder: () => FakeAndroidBuilder(), - }, timeout: allowForCreateFlutterProject); + }); }); group('Gradle', () { @@ -223,8 +223,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), GradleUtils: () => GradleUtils(), ProcessManager: () => mockProcessManager, - }, - timeout: allowForCreateFlutterProject); + }); testUsingContext('shrinking is disabled when --no-shrink is passed', () async { final String projectPath = await createProject(tempDir, @@ -255,8 +254,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), GradleUtils: () => GradleUtils(), ProcessManager: () => mockProcessManager, - }, - timeout: allowForCreateFlutterProject); + }); testUsingContext('guides the user when the shrinker fails', () async { final String projectPath = await createProject(tempDir, @@ -312,8 +310,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), ProcessManager: () => mockProcessManager, Usage: () => mockUsage, - }, - timeout: allowForCreateFlutterProject); + }); testUsingContext('reports when the app isn\'t using AndroidX', () async { final String projectPath = await createProject(tempDir, @@ -366,8 +363,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), ProcessManager: () => mockProcessManager, Usage: () => mockUsage, - }, - timeout: allowForCreateFlutterProject); + }); testUsingContext('reports when the app is using AndroidX', () async { final String projectPath = await createProject(tempDir, @@ -422,8 +418,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), ProcessManager: () => mockProcessManager, Usage: () => mockUsage, - }, - timeout: allowForCreateFlutterProject); + }); }); } diff --git a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart index a00d279a219..7663156ec33 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart @@ -48,7 +48,7 @@ void main() { }, overrides: { AndroidBuilder: () => FakeAndroidBuilder(), - }, timeout: allowForCreateFlutterProject); + }); testUsingContext('build type', () async { final String projectPath = await createProject(tempDir, @@ -75,7 +75,7 @@ void main() { }, overrides: { AndroidBuilder: () => FakeAndroidBuilder(), - }, timeout: allowForCreateFlutterProject); + }); }); group('Gradle', () { @@ -212,8 +212,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), GradleUtils: () => GradleUtils(), ProcessManager: () => mockProcessManager, - }, - timeout: allowForCreateFlutterProject); + }); testUsingContext('shrinking is disabled when --no-shrink is passed', () async { final String projectPath = await createProject( @@ -246,8 +245,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), GradleUtils: () => GradleUtils(), ProcessManager: () => mockProcessManager, - }, - timeout: allowForCreateFlutterProject); + }); testUsingContext('guides the user when the shrinker fails', () async { final String projectPath = await createProject(tempDir, @@ -303,8 +301,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), ProcessManager: () => mockProcessManager, Usage: () => mockUsage, - }, - timeout: allowForCreateFlutterProject); + }); testUsingContext('reports when the app isn\'t using AndroidX', () async { final String projectPath = await createProject(tempDir, @@ -357,8 +354,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), ProcessManager: () => mockProcessManager, Usage: () => mockUsage, - }, - timeout: allowForCreateFlutterProject); + }); testUsingContext('reports when the app is using AndroidX', () async { final String projectPath = await createProject(tempDir, @@ -413,8 +409,7 @@ flutter: FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), ProcessManager: () => mockProcessManager, Usage: () => mockUsage, - }, - timeout: allowForCreateFlutterProject); + }); }); } diff --git a/packages/flutter_tools/test/integration.shard/daemon_mode_test.dart b/packages/flutter_tools/test/integration.shard/daemon_mode_test.dart index f1ce5bab5f2..aee21d01469 100644 --- a/packages/flutter_tools/test/integration.shard/daemon_mode_test.dart +++ b/packages/flutter_tools/test/integration.shard/daemon_mode_test.dart @@ -2,13 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Integration tests which invoke flutter instead of unit testing the code -// will not produce meaningful coverage information - we can measure coverage -// from the isolate running the test, but not from the isolate started via -// the command line process. -@Tags(['no_coverage']) import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:file/file.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -21,79 +17,58 @@ import 'test_driver.dart'; import 'test_utils.dart'; void main() { - group('daemon_mode', () { - Directory tempDir; + test('device.getDevices', () async { + final Directory tempDir = createResolvedTempDirectorySync('daemon_mode_test.'); + final BasicProject _project = BasicProject(); - Process process; + await _project.setUpIn(tempDir); - setUp(() async { - tempDir = createResolvedTempDirectorySync('daemon_mode_test.'); - await _project.setUpIn(tempDir); - }); + final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter'); - tearDown(() async { - tryToDelete(tempDir); - process?.kill(); - }); + const ProcessManager processManager = LocalProcessManager(); + final Process process = await processManager.start( + [flutterBin, '--show-test-device', 'daemon'], + workingDirectory: tempDir.path, + ); - test('device.getDevices', () async { - final String flutterBin = - fs.path.join(getFlutterRoot(), 'bin', 'flutter'); + final StreamController stdout = StreamController.broadcast(); + transformToLines(process.stdout).listen((String line) => stdout.add(line)); + final Stream> stream = stdout + .stream + .map>(parseFlutterResponse) + .where((Map value) => value != null); - const ProcessManager processManager = LocalProcessManager(); - process = await processManager.start( - [flutterBin, '--show-test-device', 'daemon'], - workingDirectory: tempDir.path); + Map response = await stream.first; + expect(response['event'], 'daemon.connected'); - final StreamController stdout = - StreamController.broadcast(); + // start listening for devices + process.stdin.writeln('[${jsonEncode({ + 'id': 1, + 'method': 'device.enable', + })}]'); + response = await stream.first; + expect(response['id'], 1); + expect(response['error'], isNull); - transformToLines(process.stdout) - .listen((String line) => stdout.add(line)); + // [{"event":"device.added","params":{"id":"flutter-tester","name": + // "Flutter test device","platform":"flutter-tester","emulator":false}}] + response = await stream.first; + expect(response['event'], 'device.added'); - final Stream> stream = - stdout.stream.where((String line) { - final Map response = parseFlutterResponse(line); - // ignore 'Starting device daemon...' - if (response == null) { - return false; - } - // TODO(devoncarew): Remove this after #25440 lands. - if (response['event'] == 'daemon.showMessage') { - return false; - } - return true; - }).map(parseFlutterResponse); + // get the list of all devices + process.stdin.writeln('[${jsonEncode({ + 'id': 2, + 'method': 'device.getDevices', + })}]'); + response = await stream.first; + expect(response['id'], 2); + expect(response['error'], isNull); - Map response = await stream.first; - expect(response['event'], 'daemon.connected'); + final dynamic result = response['result']; + expect(result, isList); + expect(result, isNotEmpty); - // start listening for devices - process.stdin.writeln('[${jsonEncode({ - 'id': 1, - 'method': 'device.enable', - })}]'); - response = await stream.first; - expect(response['id'], 1); - expect(response['error'], isNull); - - // [{"event":"device.added","params":{"id":"flutter-tester","name": - // "Flutter test device","platform":"flutter-tester","emulator":false}}] - response = await stream.first; - expect(response['event'], 'device.added'); - - // get the list of all devices - process.stdin.writeln('[${jsonEncode({ - 'id': 2, - 'method': 'device.getDevices', - })}]'); - response = await stream.first; - expect(response['id'], 2); - expect(response['error'], isNull); - - final dynamic result = response['result']; - expect(result, isList); - expect(result, isNotEmpty); - }); - }, timeout: const Timeout.factor(10), tags: ['integration']); // This test uses the `flutter` tool, which could be blocked behind the startup lock for a long time. + tryToDelete(tempDir); + process.kill(); + }); } diff --git a/packages/flutter_tools/test/integration.shard/debugger_stepping_test.dart b/packages/flutter_tools/test/integration.shard/debugger_stepping_test.dart index a53cff9e412..e6a743ce0c9 100644 --- a/packages/flutter_tools/test/integration.shard/debugger_stepping_test.dart +++ b/packages/flutter_tools/test/integration.shard/debugger_stepping_test.dart @@ -2,11 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Integration tests which invoke flutter instead of unit testing the code -// will not produce meaningful coverage information - we can measure coverage -// from the isolate running the test, but not from the isolate started via -// the command line process. -@Tags(['no_coverage']) +import 'dart:io'; + import 'package:file/file.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -16,43 +13,37 @@ import 'test_driver.dart'; import 'test_utils.dart'; void main() { - group('debugger', () { - Directory tempDir; + test('can step over statements', () async { + final Directory tempDir = createResolvedTempDirectorySync('debugger_stepping_test.'); + final SteppingProject _project = SteppingProject(); - FlutterRunTestDriver _flutter; + await _project.setUpIn(tempDir); - setUp(() async { - tempDir = createResolvedTempDirectorySync('debugger_stepping_test.'); - await _project.setUpIn(tempDir); - _flutter = FlutterRunTestDriver(tempDir); - }); + final FlutterRunTestDriver _flutter = FlutterRunTestDriver(tempDir); - tearDown(() async { - await _flutter.stop(); - tryToDelete(tempDir); - }); + await _flutter.run(withDebugger: true, startPaused: true); + await _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine); + await _flutter.resume(); + await _flutter.waitForPause(); // Now we should be on the breakpoint. - test('can step over statements', () async { - await _flutter.run(withDebugger: true, startPaused: true); - await _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine); - await _flutter.resume(); - await _flutter.waitForPause(); // Now we should be on the breakpoint. + expect((await _flutter.getSourceLocation()).line, equals(_project.breakpointLine)); - expect((await _flutter.getSourceLocation()).line, equals(_project.breakpointLine)); + // Issue 5 steps, ensuring that we end up on the annotated lines each time. + for (int i = 1; i <= _project.numberOfSteps; i += 1) { + await _flutter.stepOverOrOverAsyncSuspension(); + final SourcePosition location = await _flutter.getSourceLocation(); + final int actualLine = location.line; - // Issue 5 steps, ensuring that we end up on the annotated lines each time. - for (int i = 1; i <= _project.numberOfSteps; i += 1) { - await _flutter.stepOverOrOverAsyncSuspension(); - final SourcePosition location = await _flutter.getSourceLocation(); - final int actualLine = location.line; + // Get the line we're expected to stop at by searching for the comment + // within the source code. + final int expectedLine = _project.lineForStep(i); - // Get the line we're expected to stop at by searching for the comment - // within the source code. - final int expectedLine = _project.lineForStep(i); + expect(actualLine, equals(expectedLine), + reason: 'After $i steps, debugger should stop at $expectedLine but stopped at $actualLine' + ); + } - expect(actualLine, equals(expectedLine), - reason: 'After $i steps, debugger should stop at $expectedLine but stopped at $actualLine'); - } - }); - }, timeout: const Timeout.factor(10), tags: ['integration']); // The DevFS sync takes a really long time, so these tests can be slow. + await _flutter.stop(); + tryToDelete(tempDir); + }); } diff --git a/packages/flutter_tools/test/integration.shard/expression_evaluation_test.dart b/packages/flutter_tools/test/integration.shard/expression_evaluation_test.dart index a15dad4d30a..00038a8732f 100644 --- a/packages/flutter_tools/test/integration.shard/expression_evaluation_test.dart +++ b/packages/flutter_tools/test/integration.shard/expression_evaluation_test.dart @@ -2,12 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Integration tests which invoke flutter instead of unit testing the code -// will not produce meaningful coverage information - we can measure coverage -// from the isolate running the test, but not from the isolate started via -// the command line process. -@Tags(['no_coverage']) import 'dart:async'; +import 'dart:io'; import 'package:file/file.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -20,120 +16,133 @@ import 'test_data/tests_project.dart'; import 'test_driver.dart'; import 'test_utils.dart'; -void main() { - group('flutter run expression evaluation', () { - Directory tempDir; - final BasicProject _project = BasicProject(); - FlutterRunTestDriver _flutter; +void batch1() { + final BasicProject _project = BasicProject(); + Directory tempDir; + FlutterRunTestDriver _flutter; - setUp(() async { - tempDir = createResolvedTempDirectorySync('run_expression_eval_test.'); - await _project.setUpIn(tempDir); - _flutter = FlutterRunTestDriver(tempDir); - }); + Future initProject() async { + tempDir = createResolvedTempDirectorySync('run_expression_eval_test.'); + await _project.setUpIn(tempDir); + _flutter = FlutterRunTestDriver(tempDir); + } - tearDown(() async { - await _flutter.stop(); - tryToDelete(tempDir); - }); + Future cleanProject() async { + await _flutter.stop(); + tryToDelete(tempDir); + } - Future breakInBuildMethod(FlutterTestDriver flutter) async { - await _flutter.breakAt( - _project.buildMethodBreakpointUri, - _project.buildMethodBreakpointLine, - ); - } + Future breakInBuildMethod(FlutterTestDriver flutter) async { + await _flutter.breakAt( + _project.buildMethodBreakpointUri, + _project.buildMethodBreakpointLine, + ); + } - Future breakInTopLevelFunction(FlutterTestDriver flutter) async { - await _flutter.breakAt( - _project.topLevelFunctionBreakpointUri, - _project.topLevelFunctionBreakpointLine, - ); - } + Future breakInTopLevelFunction(FlutterTestDriver flutter) async { + await _flutter.breakAt( + _project.topLevelFunctionBreakpointUri, + _project.topLevelFunctionBreakpointLine, + ); + } - test('can evaluate trivial expressions in top level function', () async { - await _flutter.run(withDebugger: true); - await breakInTopLevelFunction(_flutter); - await evaluateTrivialExpressions(_flutter); - }); + test('flutter run expression evaluation - can evaluate trivial expressions in top level function', () async { + await initProject(); + await _flutter.run(withDebugger: true); + await breakInTopLevelFunction(_flutter); + await evaluateTrivialExpressions(_flutter); + await cleanProject(); + }); - test('can evaluate trivial expressions in build method', () async { - await _flutter.run(withDebugger: true); - await breakInBuildMethod(_flutter); - await evaluateTrivialExpressions(_flutter); - }); + test('flutter run expression evaluation - can evaluate trivial expressions in build method', () async { + await initProject(); + await _flutter.run(withDebugger: true); + await breakInBuildMethod(_flutter); + await evaluateTrivialExpressions(_flutter); + await cleanProject(); + }); - test('can evaluate complex expressions in top level function', () async { - await _flutter.run(withDebugger: true); - await breakInTopLevelFunction(_flutter); - await evaluateComplexExpressions(_flutter); - }); + test('flutter run expression evaluation - can evaluate complex expressions in top level function', () async { + await initProject(); + await _flutter.run(withDebugger: true); + await breakInTopLevelFunction(_flutter); + await evaluateComplexExpressions(_flutter); + await cleanProject(); + }); - test('can evaluate complex expressions in build method', () async { - await _flutter.run(withDebugger: true); - await breakInBuildMethod(_flutter); - await evaluateComplexExpressions(_flutter); - }); + test('flutter run expression evaluation - can evaluate complex expressions in build method', () async { + await initProject(); + await _flutter.run(withDebugger: true); + await breakInBuildMethod(_flutter); + await evaluateComplexExpressions(_flutter); + await cleanProject(); + }); - test('can evaluate expressions returning complex objects in top level function', () async { - await _flutter.run(withDebugger: true); - await breakInTopLevelFunction(_flutter); - await evaluateComplexReturningExpressions(_flutter); - }); + test('flutter run expression evaluation - can evaluate expressions returning complex objects in top level function', () async { + await initProject(); + await _flutter.run(withDebugger: true); + await breakInTopLevelFunction(_flutter); + await evaluateComplexReturningExpressions(_flutter); + await cleanProject(); + }); - test('can evaluate expressions returning complex objects in build method', () async { - await _flutter.run(withDebugger: true); - await breakInBuildMethod(_flutter); - await evaluateComplexReturningExpressions(_flutter); - }); - }, timeout: const Timeout.factor(10), tags: ['integration']); // The DevFS sync takes a really long time, so these tests can be slow. + test('flutter run expression evaluation - can evaluate expressions returning complex objects in build method', () async { + await initProject(); + await _flutter.run(withDebugger: true); + await breakInBuildMethod(_flutter); + await evaluateComplexReturningExpressions(_flutter); + await cleanProject(); + }); +} - group('flutter test expression evaluation', () { - Directory tempDir; - final TestsProject _project = TestsProject(); - FlutterTestTestDriver _flutter; +void batch2() { + final TestsProject _project = TestsProject(); + Directory tempDir; + FlutterTestTestDriver _flutter; - setUp(() async { - tempDir = createResolvedTempDirectorySync('test_expression_eval_test.'); - await _project.setUpIn(tempDir); - _flutter = FlutterTestTestDriver(tempDir); - }); + Future initProject() async { + tempDir = createResolvedTempDirectorySync('test_expression_eval_test.'); + await _project.setUpIn(tempDir); + _flutter = FlutterTestTestDriver(tempDir); + } - tearDown(() async { - await _flutter.quit(); - tryToDelete(tempDir); - }); + Future cleanProject() async { + await _flutter?.quit(); + tryToDelete(tempDir); + } - test('can evaluate trivial expressions in a test', () async { - await _flutter.test( - withDebugger: true, - beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine), - ); - await _flutter.waitForPause(); - await evaluateTrivialExpressions(_flutter); - await _flutter.resume(); - }); + test('flutter test expression evaluation - can evaluate trivial expressions in a test', () async { + await initProject(); + await _flutter.test( + withDebugger: true, + beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine), + ); + await _flutter.waitForPause(); + await evaluateTrivialExpressions(_flutter); + await cleanProject(); + }); - test('can evaluate complex expressions in a test', () async { - await _flutter.test( - withDebugger: true, - beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine), - ); - await _flutter.waitForPause(); - await evaluateComplexExpressions(_flutter); - await _flutter.resume(); - }); + test('flutter test expression evaluation - can evaluate complex expressions in a test', () async { + await initProject(); + await _flutter.test( + withDebugger: true, + beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine), + ); + await _flutter.waitForPause(); + await evaluateComplexExpressions(_flutter); + await cleanProject(); + }); - test('can evaluate expressions returning complex objects in a test', () async { - await _flutter.test( - withDebugger: true, - beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine), - ); - await _flutter.waitForPause(); - await evaluateComplexReturningExpressions(_flutter); - await _flutter.resume(); - }); - }, timeout: const Timeout.factor(10), tags: ['integration']); // The DevFS sync takes a really long time, so these tests can be slow. + test('flutter test expression evaluation - can evaluate expressions returning complex objects in a test', () async { + await initProject(); + await _flutter.test( + withDebugger: true, + beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine), + ); + await _flutter.waitForPause(); + await evaluateComplexReturningExpressions(_flutter); + await cleanProject(); + }); } Future evaluateTrivialExpressions(FlutterTestDriver flutter) async { @@ -164,3 +173,8 @@ Future evaluateComplexReturningExpressions(FlutterTestDriver flutter) asyn final InstanceRef res = await flutter.evaluate(resp.id, r'"$year-$month-$day"'); expect(res.valueAsString, equals('${now.year}-${now.month}-${now.day}')); } + +void main() { + batch1(); + batch2(); +} diff --git a/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart b/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart index b5592c1a2d3..2d868b3feee 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart @@ -2,11 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Integration tests which invoke flutter instead of unit testing the code -// will not produce meaningful coverage information - we can measure coverage -// from the isolate running the test, but not from the isolate started via -// the command line process. -@Tags(['no_coverage']) +import 'dart:io'; + import 'package:file/file.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -33,35 +30,36 @@ void main() { tryToDelete(tempDir); }); - group('attached process', () { - test('writes pid-file', () async { - final File pidFile = tempDir.childFile('test.pid'); - await _flutterRun.run(withDebugger: true); - await _flutterAttach.attach( - _flutterRun.vmServicePort, - pidFile: pidFile, - ); - expect(pidFile.existsSync(), isTrue); - }); - test('can hot reload', () async { - await _flutterRun.run(withDebugger: true); - await _flutterAttach.attach(_flutterRun.vmServicePort); - await _flutterAttach.hotReload(); - }); - test('can detach, reattach, hot reload', () async { - await _flutterRun.run(withDebugger: true); - await _flutterAttach.attach(_flutterRun.vmServicePort); - await _flutterAttach.detach(); - await _flutterAttach.attach(_flutterRun.vmServicePort); - await _flutterAttach.hotReload(); - }); - test('killing process behaves the same as detach ', () async { - await _flutterRun.run(withDebugger: true); - await _flutterAttach.attach(_flutterRun.vmServicePort); - await _flutterAttach.quit(); - _flutterAttach = FlutterRunTestDriver(tempDir, logPrefix: 'ATTACH-2'); - await _flutterAttach.attach(_flutterRun.vmServicePort); - await _flutterAttach.hotReload(); - }); - }, timeout: const Timeout.factor(10), tags: ['integration']); // The DevFS sync takes a really long time, so these tests can be slow. + test('writes pid-file', () async { + final File pidFile = tempDir.childFile('test.pid'); + await _flutterRun.run(withDebugger: true); + await _flutterAttach.attach( + _flutterRun.vmServicePort, + pidFile: pidFile, + ); + expect(pidFile.existsSync(), isTrue); + }); + + test('can hot reload', () async { + await _flutterRun.run(withDebugger: true); + await _flutterAttach.attach(_flutterRun.vmServicePort); + await _flutterAttach.hotReload(); + }); + + test('can detach, reattach, hot reload', () async { + await _flutterRun.run(withDebugger: true); + await _flutterAttach.attach(_flutterRun.vmServicePort); + await _flutterAttach.detach(); + await _flutterAttach.attach(_flutterRun.vmServicePort); + await _flutterAttach.hotReload(); + }); + + test('killing process behaves the same as detach ', () async { + await _flutterRun.run(withDebugger: true); + await _flutterAttach.attach(_flutterRun.vmServicePort); + await _flutterAttach.quit(); + _flutterAttach = FlutterRunTestDriver(tempDir, logPrefix: 'ATTACH-2'); + await _flutterAttach.attach(_flutterRun.vmServicePort); + await _flutterAttach.hotReload(); + }); } diff --git a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart index cc833231eb0..32f50e1393d 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart @@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Integration tests which invoke flutter instead of unit testing the code -// will not produce meaningful coverage information - we can measure coverage -// from the isolate running the test, but not from the isolate started via -// the command line process. -@Tags(['no_coverage']) import 'package:file/file.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; @@ -18,47 +13,45 @@ import 'test_driver.dart'; import 'test_utils.dart'; void main() { - group('flutter_run', () { - Directory tempDir; - final BasicProject _project = BasicProject(); - FlutterRunTestDriver _flutter; + Directory tempDir; + final BasicProject _project = BasicProject(); + FlutterRunTestDriver _flutter; - setUp(() async { - tempDir = createResolvedTempDirectorySync('run_test.'); - await _project.setUpIn(tempDir); - _flutter = FlutterRunTestDriver(tempDir); - }); + setUp(() async { + tempDir = createResolvedTempDirectorySync('run_test.'); + await _project.setUpIn(tempDir); + _flutter = FlutterRunTestDriver(tempDir); + }); - tearDown(() async { - await _flutter.stop(); - tryToDelete(tempDir); - }); + tearDown(() async { + await _flutter.stop(); + tryToDelete(tempDir); + }); - test('reports an error if an invalid device is supplied', () async { - // This test forces flutter to check for all possible devices to catch issues - // like https://github.com/flutter/flutter/issues/21418 which were skipped - // over because other integration tests run using flutter-tester which short-cuts - // some of the checks for devices. - final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter'); + test('flutter run reports an error if an invalid device is supplied', () async { + // This test forces flutter to check for all possible devices to catch issues + // like https://github.com/flutter/flutter/issues/21418 which were skipped + // over because other integration tests run using flutter-tester which short-cuts + // some of the checks for devices. + final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter'); - const ProcessManager _processManager = LocalProcessManager(); - final ProcessResult _proc = await _processManager.run( - [flutterBin, 'run', '-d', 'invalid-device-id'], - workingDirectory: tempDir.path, - ); + const ProcessManager _processManager = LocalProcessManager(); + final ProcessResult _proc = await _processManager.run( + [flutterBin, 'run', '-d', 'invalid-device-id'], + workingDirectory: tempDir.path, + ); - expect(_proc.stdout, isNot(contains('flutter has exited unexpectedly'))); - expect(_proc.stderr, isNot(contains('flutter has exited unexpectedly'))); - if (!_proc.stderr.toString().contains('Unable to locate a development') - && !_proc.stdout.toString().contains('No devices found with name or id matching')) { - fail("'flutter run -d invalid-device-id' did not produce the expected error"); - } - }); + expect(_proc.stdout, isNot(contains('flutter has exited unexpectedly'))); + expect(_proc.stderr, isNot(contains('flutter has exited unexpectedly'))); + if (!_proc.stderr.toString().contains('Unable to locate a development') + && !_proc.stdout.toString().contains('No devices found with name or id matching')) { + fail("'flutter run -d invalid-device-id' did not produce the expected error"); + } + }); - test('writes pid-file', () async { - final File pidFile = tempDir.childFile('test.pid'); - await _flutter.run(pidFile: pidFile); - expect(pidFile.existsSync(), isTrue); - }); - }, timeout: const Timeout.factor(10), tags: ['integration']); // The DevFS sync takes a really long time, so these tests can be slow. + test('flutter run writes pid-file', () async { + final File pidFile = tempDir.childFile('test.pid'); + await _flutter.run(pidFile: pidFile); + expect(pidFile.existsSync(), isTrue); + }); } diff --git a/packages/flutter_tools/test/integration.shard/hot_reload_test.dart b/packages/flutter_tools/test/integration.shard/hot_reload_test.dart index a65129497b8..2d7d3cd7aaf 100644 --- a/packages/flutter_tools/test/integration.shard/hot_reload_test.dart +++ b/packages/flutter_tools/test/integration.shard/hot_reload_test.dart @@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Integration tests which invoke flutter instead of unit testing the code -// will not produce meaningful coverage information - we can measure coverage -// from the isolate running the test, but not from the isolate started via -// the command line process. -@Tags(['no_coverage']) import 'dart:async'; import 'package:file/file.dart'; @@ -20,139 +15,137 @@ import 'test_driver.dart'; import 'test_utils.dart'; void main() { - group('hot reload tests', () { - Directory tempDir; - final HotReloadProject _project = HotReloadProject(); - FlutterRunTestDriver _flutter; + Directory tempDir; + final HotReloadProject _project = HotReloadProject(); + FlutterRunTestDriver _flutter; - setUp(() async { - tempDir = createResolvedTempDirectorySync('hot_reload_test.'); - await _project.setUpIn(tempDir); - _flutter = FlutterRunTestDriver(tempDir); - }); + setUp(() async { + tempDir = createResolvedTempDirectorySync('hot_reload_test.'); + await _project.setUpIn(tempDir); + _flutter = FlutterRunTestDriver(tempDir); + }); - tearDown(() async { - await _flutter?.stop(); - tryToDelete(tempDir); - }); + tearDown(() async { + await _flutter?.stop(); + tryToDelete(tempDir); + }); - test('hot reload works without error', () async { - await _flutter.run(); + test('hot reload works without error', () async { + await _flutter.run(); + await _flutter.hotReload(); + }); + + test('newly added code executes during hot reload', () async { + await _flutter.run(); + _project.uncommentHotReloadPrint(); + final StringBuffer stdout = StringBuffer(); + final StreamSubscription subscription = _flutter.stdout.listen(stdout.writeln); + try { await _flutter.hotReload(); - }); - - test('newly added code executes during hot reload', () async { - await _flutter.run(); - _project.uncommentHotReloadPrint(); - final StringBuffer stdout = StringBuffer(); - final StreamSubscription subscription = _flutter.stdout.listen(stdout.writeln); - try { - await _flutter.hotReload(); - expect(stdout.toString(), contains('(((((RELOAD WORKED)))))')); - } finally { - await subscription.cancel(); - } - }); - - test('hot restart works without error', () async { - await _flutter.run(); - await _flutter.hotRestart(); - }); - - test('breakpoints are hit after hot reload', () async { - Isolate isolate; - await _flutter.run(withDebugger: true, startPaused: true); - final Completer sawTick1 = Completer(); - final Completer sawTick3 = Completer(); - final Completer sawDebuggerPausedMessage = Completer(); - final StreamSubscription subscription = _flutter.stdout.listen( - (String line) { - if (line.contains('((((TICK 1))))')) { - expect(sawTick1.isCompleted, isFalse); - sawTick1.complete(); - } - if (line.contains('((((TICK 3))))')) { - expect(sawTick3.isCompleted, isFalse); - sawTick3.complete(); - } - if (line.contains('The application is paused in the debugger on a breakpoint.')) { - expect(sawDebuggerPausedMessage.isCompleted, isFalse); - sawDebuggerPausedMessage.complete(); - } - }, - ); - await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts - unawaited(sawTick1.future.timeout( - const Duration(seconds: 5), - onTimeout: () { print('The test app is taking longer than expected to print its synchronization line...'); }, - )); - await sawTick1.future; // after this, app is in steady state - await _flutter.addBreakpoint( - _project.scheduledBreakpointUri, - _project.scheduledBreakpointLine, - ); - await _flutter.hotReload(); // reload triggers code which eventually hits the breakpoint - isolate = await _flutter.waitForPause(); - expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); - await _flutter.resume(); - await _flutter.addBreakpoint( - _project.buildBreakpointUri, - _project.buildBreakpointLine, - ); - bool reloaded = false; - final Future reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; }); - await sawTick3.future; // this should happen before it pauses - isolate = await _flutter.waitForPause(); - expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); - await sawDebuggerPausedMessage.future; - expect(reloaded, isFalse); - await _flutter.resume(); - await reloadFuture; - expect(reloaded, isTrue); - reloaded = false; + expect(stdout.toString(), contains('(((((RELOAD WORKED)))))')); + } finally { await subscription.cancel(); - }); + } + }); - test('hot reload doesn\'t reassemble if paused', () async { - await _flutter.run(withDebugger: true); - final Completer sawTick2 = Completer(); - final Completer sawTick3 = Completer(); - final Completer sawDebuggerPausedMessage1 = Completer(); - final Completer sawDebuggerPausedMessage2 = Completer(); - final StreamSubscription subscription = _flutter.stdout.listen( - (String line) { - if (line.contains('((((TICK 2))))')) { - expect(sawTick2.isCompleted, isFalse); - sawTick2.complete(); - } - if (line.contains('The application is paused in the debugger on a breakpoint.')) { - expect(sawDebuggerPausedMessage1.isCompleted, isFalse); - sawDebuggerPausedMessage1.complete(); - } - if (line.contains('The application is paused in the debugger on a breakpoint; interface might not update.')) { - expect(sawDebuggerPausedMessage2.isCompleted, isFalse); - sawDebuggerPausedMessage2.complete(); - } - }, - ); - await _flutter.addBreakpoint( - _project.buildBreakpointUri, - _project.buildBreakpointLine, - ); - bool reloaded = false; - final Future reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; }); - await sawTick2.future; // this should happen before it pauses - final Isolate isolate = await _flutter.waitForPause(); - expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); - expect(reloaded, isFalse); - await sawDebuggerPausedMessage1.future; // this is the one where it say "uh, you broke into the debugger while reloading" - await reloadFuture; // this is the one where it times out because you're in the debugger - expect(reloaded, isTrue); - await _flutter.hotReload(); // now we're already paused - expect(sawTick3.isCompleted, isFalse); - await sawDebuggerPausedMessage2.future; // so we just get told that nothing is going to happen - await _flutter.resume(); - await subscription.cancel(); - }); - }, timeout: const Timeout.factor(10), tags: ['integration']); // The DevFS sync takes a really long time, so these tests can be slow. + test('hot restart works without error', () async { + await _flutter.run(); + await _flutter.hotRestart(); + }); + + test('breakpoints are hit after hot reload', () async { + Isolate isolate; + await _flutter.run(withDebugger: true, startPaused: true); + final Completer sawTick1 = Completer(); + final Completer sawTick3 = Completer(); + final Completer sawDebuggerPausedMessage = Completer(); + final StreamSubscription subscription = _flutter.stdout.listen( + (String line) { + if (line.contains('((((TICK 1))))')) { + expect(sawTick1.isCompleted, isFalse); + sawTick1.complete(); + } + if (line.contains('((((TICK 3))))')) { + expect(sawTick3.isCompleted, isFalse); + sawTick3.complete(); + } + if (line.contains('The application is paused in the debugger on a breakpoint.')) { + expect(sawDebuggerPausedMessage.isCompleted, isFalse); + sawDebuggerPausedMessage.complete(); + } + }, + ); + await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts + unawaited(sawTick1.future.timeout( + const Duration(seconds: 5), + onTimeout: () { print('The test app is taking longer than expected to print its synchronization line...'); }, + )); + await sawTick1.future; // after this, app is in steady state + await _flutter.addBreakpoint( + _project.scheduledBreakpointUri, + _project.scheduledBreakpointLine, + ); + await _flutter.hotReload(); // reload triggers code which eventually hits the breakpoint + isolate = await _flutter.waitForPause(); + expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); + await _flutter.resume(); + await _flutter.addBreakpoint( + _project.buildBreakpointUri, + _project.buildBreakpointLine, + ); + bool reloaded = false; + final Future reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; }); + await sawTick3.future; // this should happen before it pauses + isolate = await _flutter.waitForPause(); + expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); + await sawDebuggerPausedMessage.future; + expect(reloaded, isFalse); + await _flutter.resume(); + await reloadFuture; + expect(reloaded, isTrue); + reloaded = false; + await subscription.cancel(); + }); + + test('hot reload doesn\'t reassemble if paused', () async { + await _flutter.run(withDebugger: true); + final Completer sawTick2 = Completer(); + final Completer sawTick3 = Completer(); + final Completer sawDebuggerPausedMessage1 = Completer(); + final Completer sawDebuggerPausedMessage2 = Completer(); + final StreamSubscription subscription = _flutter.stdout.listen( + (String line) { + if (line.contains('((((TICK 2))))')) { + expect(sawTick2.isCompleted, isFalse); + sawTick2.complete(); + } + if (line.contains('The application is paused in the debugger on a breakpoint.')) { + expect(sawDebuggerPausedMessage1.isCompleted, isFalse); + sawDebuggerPausedMessage1.complete(); + } + if (line.contains('The application is paused in the debugger on a breakpoint; interface might not update.')) { + expect(sawDebuggerPausedMessage2.isCompleted, isFalse); + sawDebuggerPausedMessage2.complete(); + } + }, + ); + await _flutter.addBreakpoint( + _project.buildBreakpointUri, + _project.buildBreakpointLine, + ); + bool reloaded = false; + final Future reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; }); + await sawTick2.future; // this should happen before it pauses + final Isolate isolate = await _flutter.waitForPause(); + expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint)); + expect(reloaded, isFalse); + await sawDebuggerPausedMessage1.future; // this is the one where it say "uh, you broke into the debugger while reloading" + await reloadFuture; // this is the one where it times out because you're in the debugger + expect(reloaded, isTrue); + await _flutter.hotReload(); // now we're already paused + expect(sawTick3.isCompleted, isFalse); + await sawDebuggerPausedMessage2.future; // so we just get told that nothing is going to happen + await _flutter.resume(); + await subscription.cancel(); + }); } diff --git a/packages/flutter_tools/test/integration.shard/lifetime_test.dart b/packages/flutter_tools/test/integration.shard/lifetime_test.dart index 9e5284acc0f..1ff31528e8c 100644 --- a/packages/flutter_tools/test/integration.shard/lifetime_test.dart +++ b/packages/flutter_tools/test/integration.shard/lifetime_test.dart @@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Integration tests which invoke flutter instead of unit testing the code -// will not produce meaningful coverage information - we can measure coverage -// from the isolate running the test, but not from the isolate started via -// the command line process. -@Tags(['no_coverage']) import 'dart:async'; import 'package:file/file.dart'; @@ -23,32 +18,30 @@ import 'test_utils.dart'; const Duration requiredLifespan = Duration(seconds: 5); void main() { - group('flutter run', () { - final BasicProject _project = BasicProject(); - FlutterRunTestDriver _flutter; - Directory tempDir; + final BasicProject _project = BasicProject(); + FlutterRunTestDriver _flutter; + Directory tempDir; - setUp(() async { - tempDir = createResolvedTempDirectorySync('lifetime_test.'); - await _project.setUpIn(tempDir); - _flutter = FlutterRunTestDriver(tempDir); - }); + setUp(() async { + tempDir = createResolvedTempDirectorySync('lifetime_test.'); + await _project.setUpIn(tempDir); + _flutter = FlutterRunTestDriver(tempDir); + }); - tearDown(() async { - await _flutter.stop(); - tryToDelete(tempDir); - }); + tearDown(() async { + await _flutter.stop(); + tryToDelete(tempDir); + }); - test('does not terminate when a debugger is attached', () async { - await _flutter.run(withDebugger: true); - await Future.delayed(requiredLifespan); - expect(_flutter.hasExited, equals(false)); - }); + test('flutter run does not terminate when a debugger is attached', () async { + await _flutter.run(withDebugger: true); + await Future.delayed(requiredLifespan); + expect(_flutter.hasExited, equals(false)); + }); - test('does not terminate when a debugger is attached and pause-on-exceptions', () async { - await _flutter.run(withDebugger: true, pauseOnExceptions: true); - await Future.delayed(requiredLifespan); - expect(_flutter.hasExited, equals(false)); - }); - }, timeout: const Timeout.factor(10), tags: ['integration']); // The DevFS sync takes a really long time, so these tests can be slow. + test('fluter run does not terminate when a debugger is attached and pause-on-exceptions', () async { + await _flutter.run(withDebugger: true, pauseOnExceptions: true); + await Future.delayed(requiredLifespan); + expect(_flutter.hasExited, equals(false)); + }); } diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index 7741daf52d0..00de957fe00 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -127,13 +127,6 @@ Future createProject(Directory temp, { List arguments }) async { return projectPath; } -/// Test case timeout for tests involving remote calls to `pub get` or similar. -const Timeout allowForRemotePubInvocation = Timeout.factor(10.0); - -/// Test case timeout for tests involving creating a Flutter project with -/// `--no-pub`. Use [allowForRemotePubInvocation] when creation involves `pub`. -const Timeout allowForCreateFlutterProject = Timeout.factor(3.0); - Future expectToolExitLater(Future future, Matcher messageMatcher) async { try { await future; diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart index 57e44655836..7f1fdff79da 100644 --- a/packages/flutter_tools/test/src/context.dart +++ b/packages/flutter_tools/test/src/context.dart @@ -48,7 +48,6 @@ typedef ContextInitializer = void Function(AppContext testContext); void testUsingContext( String description, dynamic testMethod(), { - Timeout timeout, Map overrides = const {}, bool initializeFlutterRoot = true, String testOn, @@ -137,8 +136,7 @@ void testUsingContext( }, ); }); - }, timeout: timeout ?? const Timeout(Duration(seconds: 60)), - testOn: testOn, skip: skip); + }, testOn: testOn, skip: skip); } void _printBufferedErrors(AppContext testContext) {