Merge branch 'master' into merge-test_descriptor-package
diff --git a/.github/ISSUE_TEMPLATE/test_process.md b/.github/ISSUE_TEMPLATE/test_process.md
new file mode 100644
index 0000000..9f492b8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/test_process.md
@@ -0,0 +1,5 @@
+---
+name: "package:test_process"
+about: "Create a bug or file a feature request against package:test_process."
+labels: "package:test_process"
+---
\ No newline at end of file
diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml
index 9a3cd79..8cda374 100644
--- a/.github/workflows/dart.yml
+++ b/.github/workflows/dart.yml
@@ -70,36 +70,6 @@
if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'"
working-directory: pkgs/fake_async
job_003:
- name: "analyze_and_format; linux; Dart 3.4.0; PKG: pkgs/matcher; `dart analyze`"
- runs-on: ubuntu-latest
- steps:
- - name: Cache Pub hosted dependencies
- uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
- with:
- path: "~/.pub-cache/hosted"
- key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.4.0;packages:pkgs/matcher;commands:analyze_1"
- restore-keys: |
- os:ubuntu-latest;pub-cache-hosted;sdk:3.4.0;packages:pkgs/matcher
- os:ubuntu-latest;pub-cache-hosted;sdk:3.4.0
- os:ubuntu-latest;pub-cache-hosted
- os:ubuntu-latest
- - name: Setup Dart SDK
- uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
- with:
- sdk: "3.4.0"
- - id: checkout
- name: Checkout repository
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- - id: pkgs_matcher_pub_upgrade
- name: pkgs/matcher; dart pub upgrade
- run: dart pub upgrade
- if: "always() && steps.checkout.conclusion == 'success'"
- working-directory: pkgs/matcher
- - name: pkgs/matcher; dart analyze
- run: dart analyze
- if: "always() && steps.pkgs_matcher_pub_upgrade.conclusion == 'success'"
- working-directory: pkgs/matcher
- job_004:
name: "analyze_and_format; linux; Dart 3.5.0; PKGS: integration_tests/regression, integration_tests/wasm; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`"
runs-on: ubuntu-latest
steps:
@@ -146,17 +116,17 @@
run: dart analyze --fatal-infos
if: "always() && steps.integration_tests_wasm_pub_upgrade.conclusion == 'success'"
working-directory: integration_tests/wasm
- job_005:
- name: "analyze_and_format; linux; Dart 3.5.0; PKGS: pkgs/checks, pkgs/test_core; `dart analyze`"
+ job_004:
+ name: "analyze_and_format; linux; Dart 3.5.0; PKGS: pkgs/checks, pkgs/matcher, pkgs/test_core; `dart analyze`"
runs-on: ubuntu-latest
steps:
- name: Cache Pub hosted dependencies
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
with:
path: "~/.pub-cache/hosted"
- key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/checks-pkgs/test_core;commands:analyze_1"
+ key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/checks-pkgs/matcher-pkgs/test_core;commands:analyze_1"
restore-keys: |
- os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/checks-pkgs/test_core
+ os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/checks-pkgs/matcher-pkgs/test_core
os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0
os:ubuntu-latest;pub-cache-hosted
os:ubuntu-latest
@@ -176,6 +146,15 @@
run: dart analyze
if: "always() && steps.pkgs_checks_pub_upgrade.conclusion == 'success'"
working-directory: pkgs/checks
+ - id: pkgs_matcher_pub_upgrade
+ name: pkgs/matcher; dart pub upgrade
+ run: dart pub upgrade
+ if: "always() && steps.checkout.conclusion == 'success'"
+ working-directory: pkgs/matcher
+ - name: pkgs/matcher; dart analyze
+ run: dart analyze
+ if: "always() && steps.pkgs_matcher_pub_upgrade.conclusion == 'success'"
+ working-directory: pkgs/matcher
- id: pkgs_test_core_pub_upgrade
name: pkgs/test_core; dart pub upgrade
run: dart pub upgrade
@@ -185,7 +164,7 @@
run: dart analyze
if: "always() && steps.pkgs_test_core_pub_upgrade.conclusion == 'success'"
working-directory: pkgs/test_core
- job_006:
+ job_005:
name: "analyze_and_format; linux; Dart dev; PKGS: integration_tests/regression, integration_tests/spawn_hybrid, integration_tests/wasm, pkgs/checks, pkgs/fake_async, pkgs/matcher, pkgs/test, pkgs/test_api, pkgs/test_core; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`"
runs-on: ubuntu-latest
steps:
@@ -323,7 +302,7 @@
run: dart analyze --fatal-infos
if: "always() && steps.pkgs_test_core_pub_upgrade.conclusion == 'success'"
working-directory: pkgs/test_core
- job_007:
+ job_006:
name: "analyze_and_format; windows; Dart 3.5.0; PKG: integration_tests/wasm; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`"
runs-on: windows-latest
steps:
@@ -347,7 +326,7 @@
run: dart analyze --fatal-infos
if: "always() && steps.integration_tests_wasm_pub_upgrade.conclusion == 'success'"
working-directory: integration_tests/wasm
- job_008:
+ job_007:
name: "analyze_and_format; windows; Dart dev; PKG: integration_tests/wasm; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`"
runs-on: windows-latest
steps:
@@ -371,7 +350,7 @@
run: dart analyze --fatal-infos
if: "always() && steps.integration_tests_wasm_pub_upgrade.conclusion == 'success'"
working-directory: integration_tests/wasm
- job_009:
+ job_008:
name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/fake_async; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -409,47 +388,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_010:
- name: "unit_test; linux; Dart 3.4.0; PKG: pkgs/matcher; `dart test`"
- runs-on: ubuntu-latest
- steps:
- - name: Cache Pub hosted dependencies
- uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
- with:
- path: "~/.pub-cache/hosted"
- key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.4.0;packages:pkgs/matcher;commands:command_00"
- restore-keys: |
- os:ubuntu-latest;pub-cache-hosted;sdk:3.4.0;packages:pkgs/matcher
- os:ubuntu-latest;pub-cache-hosted;sdk:3.4.0
- os:ubuntu-latest;pub-cache-hosted
- os:ubuntu-latest
- - name: Setup Dart SDK
- uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
- with:
- sdk: "3.4.0"
- - id: checkout
- name: Checkout repository
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- - id: pkgs_matcher_pub_upgrade
- name: pkgs/matcher; dart pub upgrade
- run: dart pub upgrade
- if: "always() && steps.checkout.conclusion == 'success'"
- working-directory: pkgs/matcher
- - name: pkgs/matcher; dart test
- run: dart test
- if: "always() && steps.pkgs_matcher_pub_upgrade.conclusion == 'success'"
- working-directory: pkgs/matcher
- needs:
- - job_001
- - job_002
- - job_003
- - job_004
- - job_005
- - job_006
- - job_007
- - job_008
- job_011:
+ job_009:
name: "unit_test; linux; Dart 3.5.0; PKG: integration_tests/regression; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -487,8 +426,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_012:
+ job_010:
name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/checks; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -526,8 +464,45 @@
- job_005
- job_006
- job_007
- - job_008
- job_013:
+ job_011:
+ name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/matcher; `dart test`"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Cache Pub hosted dependencies
+ uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a
+ with:
+ path: "~/.pub-cache/hosted"
+ key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/matcher;commands:command_00"
+ restore-keys: |
+ os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/matcher
+ os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0
+ os:ubuntu-latest;pub-cache-hosted
+ os:ubuntu-latest
+ - name: Setup Dart SDK
+ uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
+ with:
+ sdk: "3.5.0"
+ - id: checkout
+ name: Checkout repository
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+ - id: pkgs_matcher_pub_upgrade
+ name: pkgs/matcher; dart pub upgrade
+ run: dart pub upgrade
+ if: "always() && steps.checkout.conclusion == 'success'"
+ working-directory: pkgs/matcher
+ - name: pkgs/matcher; dart test
+ run: dart test
+ if: "always() && steps.pkgs_matcher_pub_upgrade.conclusion == 'success'"
+ working-directory: pkgs/matcher
+ needs:
+ - job_001
+ - job_002
+ - job_003
+ - job_004
+ - job_005
+ - job_006
+ - job_007
+ job_012:
name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/test_core; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -565,8 +540,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_014:
+ job_013:
name: "unit_test; linux; Dart 3.5.0; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`"
runs-on: ubuntu-latest
steps:
@@ -604,8 +578,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_015:
+ job_014:
name: "unit_test; linux; Dart 3.5.0; PKG: integration_tests/wasm; `dart test --timeout=60s`"
runs-on: ubuntu-latest
steps:
@@ -643,8 +616,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_016:
+ job_015:
name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 0`"
runs-on: ubuntu-latest
steps:
@@ -682,8 +654,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_017:
+ job_016:
name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 1`"
runs-on: ubuntu-latest
steps:
@@ -721,8 +692,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_018:
+ job_017:
name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 2`"
runs-on: ubuntu-latest
steps:
@@ -760,8 +730,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_019:
+ job_018:
name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 3`"
runs-on: ubuntu-latest
steps:
@@ -799,8 +768,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_020:
+ job_019:
name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 4`"
runs-on: ubuntu-latest
steps:
@@ -838,8 +806,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_021:
+ job_020:
name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/test_api; `dart test --preset travis -x browser`"
runs-on: ubuntu-latest
steps:
@@ -877,8 +844,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_022:
+ job_021:
name: "unit_test; linux; Dart dev; PKG: integration_tests/regression; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -916,8 +882,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_023:
+ job_022:
name: "unit_test; linux; Dart dev; PKG: pkgs/checks; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -955,8 +920,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_024:
+ job_023:
name: "unit_test; linux; Dart dev; PKG: pkgs/fake_async; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -994,8 +958,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_025:
+ job_024:
name: "unit_test; linux; Dart dev; PKG: pkgs/matcher; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -1033,8 +996,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_026:
+ job_025:
name: "unit_test; linux; Dart dev; PKG: pkgs/test_core; `dart test`"
runs-on: ubuntu-latest
steps:
@@ -1072,8 +1034,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_027:
+ job_026:
name: "unit_test; linux; Dart dev; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`"
runs-on: ubuntu-latest
steps:
@@ -1111,8 +1072,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_028:
+ job_027:
name: "unit_test; linux; Dart dev; PKG: integration_tests/wasm; `dart test --timeout=60s`"
runs-on: ubuntu-latest
steps:
@@ -1150,8 +1110,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_029:
+ job_028:
name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 0`"
runs-on: ubuntu-latest
steps:
@@ -1189,8 +1148,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_030:
+ job_029:
name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 1`"
runs-on: ubuntu-latest
steps:
@@ -1228,8 +1186,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_031:
+ job_030:
name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 2`"
runs-on: ubuntu-latest
steps:
@@ -1267,8 +1224,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_032:
+ job_031:
name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 3`"
runs-on: ubuntu-latest
steps:
@@ -1306,8 +1262,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_033:
+ job_032:
name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 4`"
runs-on: ubuntu-latest
steps:
@@ -1345,8 +1300,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_034:
+ job_033:
name: "unit_test; linux; Dart dev; PKG: pkgs/test_api; `dart test --preset travis -x browser`"
runs-on: ubuntu-latest
steps:
@@ -1384,8 +1338,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_035:
+ job_034:
name: "unit_test; osx; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 0`"
runs-on: macos-latest
steps:
@@ -1423,8 +1376,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_036:
+ job_035:
name: "unit_test; osx; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 1`"
runs-on: macos-latest
steps:
@@ -1462,8 +1414,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_037:
+ job_036:
name: "unit_test; osx; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 2`"
runs-on: macos-latest
steps:
@@ -1501,8 +1452,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_038:
+ job_037:
name: "unit_test; osx; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 3`"
runs-on: macos-latest
steps:
@@ -1540,8 +1490,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_039:
+ job_038:
name: "unit_test; osx; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 4`"
runs-on: macos-latest
steps:
@@ -1579,8 +1528,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_040:
+ job_039:
name: "unit_test; windows; Dart 3.5.0; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`"
runs-on: windows-latest
steps:
@@ -1608,8 +1556,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_041:
+ job_040:
name: "unit_test; windows; Dart 3.5.0; PKG: integration_tests/wasm; `dart test --timeout=60s`"
runs-on: windows-latest
steps:
@@ -1637,8 +1584,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_042:
+ job_041:
name: "unit_test; windows; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 0`"
runs-on: windows-latest
steps:
@@ -1666,8 +1612,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_043:
+ job_042:
name: "unit_test; windows; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 1`"
runs-on: windows-latest
steps:
@@ -1695,8 +1640,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_044:
+ job_043:
name: "unit_test; windows; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 2`"
runs-on: windows-latest
steps:
@@ -1724,8 +1668,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_045:
+ job_044:
name: "unit_test; windows; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 3`"
runs-on: windows-latest
steps:
@@ -1753,8 +1696,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_046:
+ job_045:
name: "unit_test; windows; Dart 3.5.0; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 4`"
runs-on: windows-latest
steps:
@@ -1782,8 +1724,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_047:
+ job_046:
name: "unit_test; windows; Dart dev; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`"
runs-on: windows-latest
steps:
@@ -1811,8 +1752,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_048:
+ job_047:
name: "unit_test; windows; Dart dev; PKG: integration_tests/wasm; `dart test --timeout=60s`"
runs-on: windows-latest
steps:
@@ -1840,8 +1780,7 @@
- job_005
- job_006
- job_007
- - job_008
- job_049:
+ job_048:
name: Notify failure
runs-on: ubuntu-latest
if: "(github.event_name == 'push' || github.event_name == 'schedule') && failure()"
@@ -1900,4 +1839,3 @@
- job_045
- job_046
- job_047
- - job_048
diff --git a/.github/workflows/test_process.yaml b/.github/workflows/test_process.yaml
new file mode 100644
index 0000000..2c0da6d
--- /dev/null
+++ b/.github/workflows/test_process.yaml
@@ -0,0 +1,72 @@
+name: package:test_process
+
+on:
+ # Run on PRs and pushes to the default branch.
+ push:
+ branches: [ master ]
+ paths:
+ - '.github/workflows/test_process.yaml'
+ - 'pkgs/test_process/**'
+ pull_request:
+ branches: [ master ]
+ paths:
+ - '.github/workflows/test_process.yaml'
+ - 'pkgs/test_process/**'
+ schedule:
+ - cron: "0 0 * * 0"
+
+env:
+ PUB_ENVIRONMENT: bot.github
+
+
+defaults:
+ run:
+ working-directory: pkgs/test_process/
+
+jobs:
+ # Check code formatting and static analysis on a single OS (linux)
+ # against Dart dev.
+ analyze:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ sdk: [dev]
+ steps:
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+ - uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
+ with:
+ sdk: ${{ matrix.sdk }}
+ - id: install
+ name: Install dependencies
+ run: dart pub get
+ - name: Check formatting
+ run: dart format --output=none --set-exit-if-changed .
+ if: always() && steps.install.outcome == 'success'
+ - name: Analyze code
+ run: dart analyze --fatal-infos
+ if: always() && steps.install.outcome == 'success'
+
+ # Run tests on a matrix consisting of two dimensions:
+ # 1. OS: ubuntu-latest, (macos-latest, windows-latest)
+ # 2. release: dev
+ test:
+ needs: analyze
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ # Add macos-latest and/or windows-latest if relevant for this package.
+ os: [ubuntu-latest]
+ sdk: [3.1, dev]
+ steps:
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+ - uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94
+ with:
+ sdk: ${{ matrix.sdk }}
+ - id: install
+ name: Install dependencies
+ run: dart pub get
+ - name: Run VM tests
+ run: dart test --platform vm
+ if: always() && steps.install.outcome == 'success'
diff --git a/README.md b/README.md
index 9ff29e9..f178ccb 100644
--- a/README.md
+++ b/README.md
@@ -23,3 +23,4 @@
| [test_api](pkgs/test_api/) | | [](https://pub.dev/packages/test_api) |
| [test_core](pkgs/test_core/) | | [](https://pub.dev/packages/test_core) |
| [test_descriptor](pkgs/test_descriptor/) | An API for defining and verifying files and directory structures. | [](https://pub.dev/packages/test_descriptor) |
+| [test_process](pkgs/test_process/) | Test processes: starting; validating stdout and stderr; checking exit code | [](https://pub.dev/packages/test_process) |
diff --git a/pkgs/matcher/CHANGELOG.md b/pkgs/matcher/CHANGELOG.md
index 0522c4c..614e35a 100644
--- a/pkgs/matcher/CHANGELOG.md
+++ b/pkgs/matcher/CHANGELOG.md
@@ -1,6 +1,8 @@
## 0.12.18-wip
* Remove some dynamic invocations.
+* Add explicit casts from `dynamic` values.
+* Require Dart 3.5
## 0.12.17
diff --git a/pkgs/matcher/analysis_options.yaml b/pkgs/matcher/analysis_options.yaml
index d183f7b..5564cc8 100644
--- a/pkgs/matcher/analysis_options.yaml
+++ b/pkgs/matcher/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:lints/recommended.yaml
+include: package:dart_flutter_team_lints/analysis_options.yaml
linter:
rules:
diff --git a/pkgs/matcher/lib/expect.dart b/pkgs/matcher/lib/expect.dart
index c842d30..113f240 100644
--- a/pkgs/matcher/lib/expect.dart
+++ b/pkgs/matcher/lib/expect.dart
@@ -38,14 +38,14 @@
export 'src/expect/stream_matcher.dart' show StreamMatcher;
export 'src/expect/stream_matchers.dart'
show
- emitsDone,
emits,
- emitsError,
- mayEmit,
emitsAnyOf,
- emitsInOrder,
+ emitsDone,
+ emitsError,
emitsInAnyOrder,
+ emitsInOrder,
emitsThrough,
+ mayEmit,
mayEmitMultiple,
neverEmits;
export 'src/expect/throws_matcher.dart' show Throws, throws, throwsA;
diff --git a/pkgs/matcher/lib/mirror_matchers.dart b/pkgs/matcher/lib/mirror_matchers.dart
index 5b2f4b6..cf85e23 100644
--- a/pkgs/matcher/lib/mirror_matchers.dart
+++ b/pkgs/matcher/lib/mirror_matchers.dart
@@ -73,7 +73,7 @@
.add('has property "$_name" with value ')
.addDescriptionOf(matchState['value']);
var innerDescription = StringDescription();
- matchState['state'] ??= {};
+ matchState['state'] ??= <Object?, Object?>{};
_matcher?.describeMismatch(matchState['value'], innerDescription,
matchState['state'] as Map, verbose);
if (innerDescription.length > 0) {
diff --git a/pkgs/matcher/lib/src/core_matchers.dart b/pkgs/matcher/lib/src/core_matchers.dart
index afb835b..936149e 100644
--- a/pkgs/matcher/lib/src/core_matchers.dart
+++ b/pkgs/matcher/lib/src/core_matchers.dart
@@ -14,7 +14,8 @@
const _Empty();
@override
- bool matches(Object? item, Map matchState) => (item as dynamic).isEmpty;
+ bool matches(Object? item, Map matchState) =>
+ (item as dynamic).isEmpty as bool;
@override
Description describe(Description description) => description.add('empty');
@@ -27,7 +28,8 @@
const _NotEmpty();
@override
- bool matches(Object? item, Map matchState) => (item as dynamic).isNotEmpty;
+ bool matches(Object? item, Map matchState) =>
+ (item as dynamic).isNotEmpty as bool;
@override
Description describe(Description description) => description.add('non-empty');
diff --git a/pkgs/matcher/lib/src/equals_matcher.dart b/pkgs/matcher/lib/src/equals_matcher.dart
index 5c4f4c5..36b3bf5 100644
--- a/pkgs/matcher/lib/src/equals_matcher.dart
+++ b/pkgs/matcher/lib/src/equals_matcher.dart
@@ -167,7 +167,7 @@
Object? expected, Object? actual, String location, int depth) {
// If the expected value is a matcher, try to match it.
if (expected is Matcher) {
- var matchState = {};
+ var matchState = <Object?, Object?>{};
if (expected.matches(actual, matchState)) return null;
return _Mismatch(location, actual, (description, verbose) {
var oldLength = description.length;
diff --git a/pkgs/matcher/lib/src/expect/expect.dart b/pkgs/matcher/lib/src/expect/expect.dart
index 8dd8cae..d8110d6 100644
--- a/pkgs/matcher/lib/src/expect/expect.dart
+++ b/pkgs/matcher/lib/src/expect/expect.dart
@@ -74,7 +74,10 @@
/// The implementation of [expect] and [expectLater].
Future _expect(Object? actual, Object? matcher,
- {String? reason, skip, bool verbose = false, ErrorFormatter? formatter}) {
+ {String? reason,
+ Object? skip,
+ bool verbose = false,
+ ErrorFormatter? formatter}) {
final test = TestHandle.current;
formatter ??= (actual, matcher, reason, matchState, verbose) {
var mismatchDescription = StringDescription();
@@ -133,7 +136,7 @@
return Future.sync(() {});
}
- var matchState = {};
+ var matchState = <Object?, Object?>{};
try {
if ((matcher as Matcher).matches(actual, matchState)) {
return Future.sync(() {});
diff --git a/pkgs/matcher/lib/src/expect/expect_async.dart b/pkgs/matcher/lib/src/expect/expect_async.dart
index 88cf6f2..a659c81 100644
--- a/pkgs/matcher/lib/src/expect/expect_async.dart
+++ b/pkgs/matcher/lib/src/expect/expect_async.dart
@@ -124,15 +124,17 @@
/// Returns a function that has the same number of positional arguments as the
/// wrapped function (up to a total of 6).
Function get func {
- if (_callback is Function(Never, Never, Never, Never, Never, Never)) {
+ if (_callback is void Function(Never, Never, Never, Never, Never, Never)) {
return max6;
}
- if (_callback is Function(Never, Never, Never, Never, Never)) return max5;
- if (_callback is Function(Never, Never, Never, Never)) return max4;
- if (_callback is Function(Never, Never, Never)) return max3;
- if (_callback is Function(Never, Never)) return max2;
- if (_callback is Function(Never)) return max1;
- if (_callback is Function()) return max0;
+ if (_callback is void Function(Never, Never, Never, Never, Never)) {
+ return max5;
+ }
+ if (_callback is void Function(Never, Never, Never, Never)) return max4;
+ if (_callback is void Function(Never, Never, Never)) return max3;
+ if (_callback is void Function(Never, Never)) return max2;
+ if (_callback is void Function(Never)) return max1;
+ if (_callback is void Function()) return max0;
_outstandingWork?.complete();
throw ArgumentError(
@@ -219,7 +221,8 @@
@Deprecated('Will be removed in 0.13.0')
Function expectAsync(Function callback,
{int count = 1, int max = 0, String? id, String? reason}) =>
- _ExpectedFunction(callback, count, max, id: id, reason: reason).func;
+ _ExpectedFunction<Object?>(callback, count, max, id: id, reason: reason)
+ .func;
/// Informs the framework that the given [callback] of arity 0 is expected to be
/// called [count] number of times (by default 1).
@@ -415,7 +418,8 @@
@Deprecated('Will be removed in 0.13.0')
Function expectAsyncUntil(Function callback, bool Function() isDone,
{String? id, String? reason}) =>
- _ExpectedFunction(callback, 0, -1, id: id, reason: reason, isDone: isDone)
+ _ExpectedFunction<Object?>(callback, 0, -1,
+ id: id, reason: reason, isDone: isDone)
.func;
/// Informs the framework that the given [callback] of arity 0 is expected to be
diff --git a/pkgs/matcher/lib/src/expect/future_matchers.dart b/pkgs/matcher/lib/src/expect/future_matchers.dart
index 407b9b8..75655f0 100644
--- a/pkgs/matcher/lib/src/expect/future_matchers.dart
+++ b/pkgs/matcher/lib/src/expect/future_matchers.dart
@@ -58,7 +58,7 @@
result = await _matcher.matchAsync(value) as String?;
if (result == null) return null;
} else {
- var matchState = {};
+ var matchState = <Object?, Object?>{};
if (_matcher.matches(value, matchState)) return null;
result = _matcher
.describeMismatch(value, StringDescription(), matchState, false)
diff --git a/pkgs/matcher/lib/src/expect/prints_matcher.dart b/pkgs/matcher/lib/src/expect/prints_matcher.dart
index 57ae95e..495d22c 100644
--- a/pkgs/matcher/lib/src/expect/prints_matcher.dart
+++ b/pkgs/matcher/lib/src/expect/prints_matcher.dart
@@ -33,7 +33,7 @@
// synchronous.
@override
dynamic /*FutureOr<String>*/ matchAsync(Object? item) {
- if (item is! Function()) return 'was not a unary Function';
+ if (item is! Object? Function()) return 'was not a unary Function';
var buffer = StringBuffer();
var result = runZoned(item,
@@ -53,7 +53,7 @@
/// Verifies that [actual] matches [_matcher] and returns a [String]
/// description of the failure if it doesn't.
String? _check(String actual) {
- var matchState = {};
+ var matchState = <Object?, Object?>{};
if (_matcher.matches(actual, matchState)) return null;
var result = _matcher
diff --git a/pkgs/matcher/lib/src/expect/stream_matcher.dart b/pkgs/matcher/lib/src/expect/stream_matcher.dart
index 0c1d852..253b6bf 100644
--- a/pkgs/matcher/lib/src/expect/stream_matcher.dart
+++ b/pkgs/matcher/lib/src/expect/stream_matcher.dart
@@ -155,7 +155,7 @@
.listen(events.add, onDone: () => events.add(null));
// Wait on a timer tick so all buffered events are emitted.
- await Future.delayed(Duration.zero);
+ await Future<void>.delayed(Duration.zero);
_unawaited(subscription.cancel());
var eventsString = events.map((event) {
diff --git a/pkgs/matcher/lib/src/expect/stream_matchers.dart b/pkgs/matcher/lib/src/expect/stream_matchers.dart
index 02efff3..cff7f9f 100644
--- a/pkgs/matcher/lib/src/expect/stream_matchers.dart
+++ b/pkgs/matcher/lib/src/expect/stream_matchers.dart
@@ -34,7 +34,7 @@
return StreamMatcher((queue) async {
if (!await queue.hasNext) return '';
- var matchState = {};
+ var matchState = <Object?, Object?>{};
var actual = await queue.next;
if (wrapped.matches(actual, matchState)) return null;
@@ -138,7 +138,7 @@
if (consumedMost == null) {
transaction.reject();
if (firstError != null) {
- await Future.error(firstError!, firstStackTrace);
+ await Future<Never>.error(firstError!, firstStackTrace);
}
var failureMessages = <String>[];
@@ -368,7 +368,9 @@
if (consumedMost == null) {
transaction.reject();
- if (firstError != null) await Future.error(firstError!, firstStackTrace);
+ if (firstError != null) {
+ await Future<Never>.error(firstError!, firstStackTrace);
+ }
return false;
} else {
transaction.commit(consumedMost!);
diff --git a/pkgs/matcher/lib/src/expect/throws_matcher.dart b/pkgs/matcher/lib/src/expect/throws_matcher.dart
index 37676ef..17a8c9e 100644
--- a/pkgs/matcher/lib/src/expect/throws_matcher.dart
+++ b/pkgs/matcher/lib/src/expect/throws_matcher.dart
@@ -116,10 +116,10 @@
/// Verifies that [error] matches [_matcher] and returns a [String]
/// description of the failure if it doesn't.
- String? _check(error, StackTrace? trace) {
+ String? _check(Object error, StackTrace? trace) {
if (_matcher == null) return null;
- var matchState = {};
+ var matchState = <Object?, Object?>{};
if (_matcher.matches(error, matchState)) return null;
var result = _matcher
diff --git a/pkgs/matcher/lib/src/map_matchers.dart b/pkgs/matcher/lib/src/map_matchers.dart
index 4476d06..9bc9eb5 100644
--- a/pkgs/matcher/lib/src/map_matchers.dart
+++ b/pkgs/matcher/lib/src/map_matchers.dart
@@ -16,7 +16,7 @@
@override
bool matches(Object? item, Map matchState) =>
// ignore: avoid_dynamic_calls
- (item as dynamic).containsValue(_value);
+ (item as dynamic).containsValue(_value) as bool;
@override
Description describe(Description description) =>
description.add('contains value ').addDescriptionOf(_value);
@@ -36,7 +36,7 @@
@override
bool matches(Object? item, Map matchState) =>
// ignore: avoid_dynamic_calls
- (item as dynamic).containsKey(_key) &&
+ ((item as dynamic).containsKey(_key) as bool) &&
_valueMatcher.matches((item as dynamic)[_key], matchState);
@override
diff --git a/pkgs/matcher/lib/src/operator_matchers.dart b/pkgs/matcher/lib/src/operator_matchers.dart
index 15e50ff..18f9537 100644
--- a/pkgs/matcher/lib/src/operator_matchers.dart
+++ b/pkgs/matcher/lib/src/operator_matchers.dart
@@ -59,7 +59,7 @@
Map matchState, bool verbose) {
var matcher = matchState['matcher'] as Matcher;
matcher.describeMismatch(
- item, mismatchDescription, matchState['state'], verbose);
+ item, mismatchDescription, matchState['state'] as Map, verbose);
return mismatchDescription;
}
diff --git a/pkgs/matcher/lib/src/order_matchers.dart b/pkgs/matcher/lib/src/order_matchers.dart
index 6fe7c76..7dc9e4f 100644
--- a/pkgs/matcher/lib/src/order_matchers.dart
+++ b/pkgs/matcher/lib/src/order_matchers.dart
@@ -78,9 +78,9 @@
bool matches(Object? item, Map matchState) {
if (item == _value) {
return _equalValue;
- } else if ((item as dynamic) < _value) {
+ } else if ((item as dynamic) < _value as bool) {
return _lessThanValue;
- } else if ((item as dynamic) > _value) {
+ } else if ((item as dynamic) > _value as bool) {
return _greaterThanValue;
} else {
return false;
diff --git a/pkgs/matcher/lib/src/string_matchers.dart b/pkgs/matcher/lib/src/string_matchers.dart
index b819fa5..4e7dda5 100644
--- a/pkgs/matcher/lib/src/string_matchers.dart
+++ b/pkgs/matcher/lib/src/string_matchers.dart
@@ -61,7 +61,7 @@
description.addDescriptionOf(_matchValue).add(' ignoring whitespace');
@override
- Description describeTypedMismatch(dynamic item,
+ Description describeTypedMismatch(String item,
Description mismatchDescription, Map matchState, bool verbose) {
return mismatchDescription
.add('is ')
@@ -152,7 +152,7 @@
: throw ArgumentError('matches requires a regexp or string');
@override
- bool typedMatches(dynamic item, Map matchState) => _regexp.hasMatch(item);
+ bool typedMatches(String item, Map matchState) => _regexp.hasMatch(item);
@override
Description describe(Description description) =>
diff --git a/pkgs/matcher/lib/src/util.dart b/pkgs/matcher/lib/src/util.dart
index af0ba2c..511831e 100644
--- a/pkgs/matcher/lib/src/util.dart
+++ b/pkgs/matcher/lib/src/util.dart
@@ -43,8 +43,7 @@
} else if (valueOrMatcher is bool Function(Never)) {
// unary predicate, but expects a specific type
// so wrap it.
- // ignore: unnecessary_lambdas
- return predicate((a) => (valueOrMatcher as dynamic)(a));
+ return predicate((a) => (valueOrMatcher as dynamic)(a) as bool);
} else {
return equals(valueOrMatcher);
}
diff --git a/pkgs/matcher/pubspec.yaml b/pkgs/matcher/pubspec.yaml
index 237e559..d29e1ab 100644
--- a/pkgs/matcher/pubspec.yaml
+++ b/pkgs/matcher/pubspec.yaml
@@ -6,7 +6,7 @@
repository: https://github.com/dart-lang/test/tree/master/pkgs/matcher
environment:
- sdk: ^3.4.0
+ sdk: ^3.5.0
dependencies:
async: ^2.10.0
@@ -16,8 +16,8 @@
test_api: ">=0.5.0 <0.8.0"
dev_dependencies:
+ dart_flutter_team_lints: ^3.2.0
fake_async: ^1.3.0
- lints: ^3.0.0
test: ^1.23.0
dependency_overrides:
diff --git a/pkgs/matcher/test/core_matchers_test.dart b/pkgs/matcher/test/core_matchers_test.dart
index 04fc8b3..b20f31f 100644
--- a/pkgs/matcher/test/core_matchers_test.dart
+++ b/pkgs/matcher/test/core_matchers_test.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:matcher/matcher.dart';
-import 'package:test/test.dart' show test, group;
+import 'package:test/test.dart' show group, test;
import 'test_utils.dart';
@@ -42,22 +42,22 @@
});
test('same', () {
- var a = {};
- var b = {};
+ var a = <Object?, Object?>{};
+ var b = <Object?, Object?>{};
shouldPass(a, same(a));
shouldFail(b, same(a), 'Expected: same instance as {} Actual: {}');
});
test('equals', () {
- var a = {};
- var b = {};
+ var a = <Object?, Object?>{};
+ var b = <Object?, Object?>{};
shouldPass(a, equals(a));
shouldPass(a, equals(b));
});
test('equals with null', () {
Object? a; // null
- var b = {};
+ var b = <Object?, Object?>{};
shouldPass(a, equals(a));
shouldFail(
a, equals(b), 'Expected: {} Actual: <null> Which: expected a map');
@@ -87,7 +87,7 @@
});
test('anything', () {
- var a = {};
+ var a = <Object?, Object?>{};
shouldPass(0, anything);
shouldPass(null, anything);
shouldPass(a, anything);
@@ -107,8 +107,8 @@
});
test('hasLength', () {
- var a = {};
- var b = [];
+ var a = <Object?, Object?>{};
+ var b = <Object?>[];
shouldPass(a, hasLength(0));
shouldPass(b, hasLength(0));
shouldPass('a', hasLength(1));
@@ -173,13 +173,13 @@
['foo', 'bar'],
['foo'],
3,
- []
+ <Object?>[]
];
var expected1 = [
['foo', 'bar'],
['foo'],
4,
- []
+ <Object?>[]
];
var reason1 = "Expected: [['foo', 'bar'], ['foo'], 4, []] "
"Actual: [['foo', 'bar'], ['foo'], 3, []] "
@@ -189,13 +189,13 @@
['foo', 'barry'],
['foo'],
4,
- []
+ <Object?>[]
];
var expected2 = [
['foo', 'bar'],
['foo'],
4,
- []
+ <Object?>[]
];
var reason2 = "Expected: [['foo', 'bar'], ['foo'], 4, []] "
"Actual: [['foo', 'barry'], ['foo'], 4, []] "
diff --git a/pkgs/matcher/test/expect_test.dart b/pkgs/matcher/test/expect_test.dart
index e2ef497..70ce579 100644
--- a/pkgs/matcher/test/expect_test.dart
+++ b/pkgs/matcher/test/expect_test.dart
@@ -18,7 +18,7 @@
});
test('contains an async error', () {
- expect(expectLater(Future.error('oh no'), completion(isFalse)),
+ expect(expectLater(Future<Never>.error('oh no'), completion(isFalse)),
throwsA('oh no'));
});
});
diff --git a/pkgs/matcher/test/having_test.dart b/pkgs/matcher/test/having_test.dart
index ddada77..26592cd 100644
--- a/pkgs/matcher/test/having_test.dart
+++ b/pkgs/matcher/test/having_test.dart
@@ -5,7 +5,7 @@
// ignore_for_file: lines_longer_than_80_chars
import 'package:matcher/matcher.dart';
-import 'package:test/test.dart' show test, expect, throwsA, group;
+import 'package:test/test.dart' show expect, group, test, throwsA;
import 'test_utils.dart';
diff --git a/pkgs/matcher/test/iterable_matchers_test.dart b/pkgs/matcher/test/iterable_matchers_test.dart
index 7607d18..3cd78d7 100644
--- a/pkgs/matcher/test/iterable_matchers_test.dart
+++ b/pkgs/matcher/test/iterable_matchers_test.dart
@@ -83,13 +83,13 @@
var d = [
['foo', 'bar'],
['foo'],
- []
+ <Object?>[]
];
var e = [
['foo', 'bar'],
['foo'],
3,
- []
+ <Object?>[]
];
shouldPass(d, everyElement(anyOf(isEmpty, contains('foo'))));
shouldFail(
@@ -196,7 +196,7 @@
equals(2), // 2
allOf([lessThan(3), isNot(0)]), // 1
equals(0), // 0
- predicate((int v) => v % 2 == 1), // 3
+ predicate((int v) => v.isOdd), // 3
equals(5), // 5
]));
shouldFail(
diff --git a/pkgs/matcher/test/map_matchers_test.dart b/pkgs/matcher/test/map_matchers_test.dart
index 4c699ab..f7dbafe 100644
--- a/pkgs/matcher/test/map_matchers_test.dart
+++ b/pkgs/matcher/test/map_matchers_test.dart
@@ -1,5 +1,5 @@
import 'package:matcher/matcher.dart'
- show contains, containsValue, containsPair;
+ show contains, containsPair, containsValue;
import 'package:test/test.dart' show test;
import 'test_utils.dart';
diff --git a/pkgs/matcher/test/matcher/completion_test.dart b/pkgs/matcher/test/matcher/completion_test.dart
index 9259cd1..ef8288e 100644
--- a/pkgs/matcher/test/matcher/completion_test.dart
+++ b/pkgs/matcher/test/matcher/completion_test.dart
@@ -20,13 +20,13 @@
});
test('succeeds when a future does not complete', () {
- var completer = Completer();
+ var completer = Completer<Never>();
expect(completer.future, doesNotComplete);
});
test('fails when a future does complete', () async {
var monitor = await TestCaseMonitor.run(() {
- var completer = Completer();
+ var completer = Completer<void>();
completer.complete(null);
expect(completer.future, doesNotComplete);
});
@@ -39,7 +39,7 @@
test('fails when a future completes after the expect', () async {
var monitor = await TestCaseMonitor.run(() {
- var completer = Completer();
+ var completer = Completer<void>();
expect(completer.future, doesNotComplete);
completer.complete(null);
});
@@ -52,7 +52,7 @@
test('fails when a future eventually completes', () async {
var monitor = await TestCaseMonitor.run(() {
- var completer = Completer();
+ var completer = Completer<void>();
expect(completer.future, doesNotComplete);
Future(() async {
await pumpEventQueue(times: 10);
@@ -80,7 +80,7 @@
test('with an error', () async {
var monitor = await TestCaseMonitor.run(() {
- expect(Future.error('X'), completes);
+ expect(Future<Never>.error('X'), completes);
});
expect(monitor.state, equals(State.failed));
@@ -89,7 +89,7 @@
test('with a failure', () async {
var monitor = await TestCaseMonitor.run(() {
- expect(Future.error(TestFailure('oh no')), completes);
+ expect(Future<Never>.error(TestFailure('oh no')), completes);
});
expectTestFailed(monitor, 'oh no');
@@ -127,7 +127,7 @@
test('with an error', () async {
var monitor = await TestCaseMonitor.run(() {
- expect(Future.error('X'), completion(isNull));
+ expect(Future<Never>.error('X'), completion(isNull));
});
expect(monitor.state, equals(State.failed));
@@ -136,7 +136,7 @@
test('with a failure', () async {
var monitor = await TestCaseMonitor.run(() {
- expect(Future.error(TestFailure('oh no')), completion(isNull));
+ expect(Future<Never>.error(TestFailure('oh no')), completion(isNull));
});
expectTestFailed(monitor, 'oh no');
@@ -175,7 +175,7 @@
});
test("blocks expectLater's Future", () async {
- var completer = Completer();
+ var completer = Completer<int>();
var fired = false;
unawaited(expectLater(completer.future, completion(equals(1))).then((_) {
fired = true;
diff --git a/pkgs/matcher/test/matcher/prints_test.dart b/pkgs/matcher/test/matcher/prints_test.dart
index cbdb12a..a681413 100644
--- a/pkgs/matcher/test/matcher/prints_test.dart
+++ b/pkgs/matcher/test/matcher/prints_test.dart
@@ -158,7 +158,7 @@
});
test('describes a failure with no text nicely', () async {
- void local() => Future.value();
+ void local() => Future<void>.value();
var monitor = await TestCaseMonitor.run(() {
expect(local, prints(contains('Goodbye')));
});
@@ -186,7 +186,7 @@
});
test("blocks expectLater's Future", () async {
- var completer = Completer();
+ var completer = Completer<void>();
var fired = false;
unawaited(expectLater(() {
diff --git a/pkgs/matcher/test/matcher/throws_test.dart b/pkgs/matcher/test/matcher/throws_test.dart
index 25b93a9..ff401b0 100644
--- a/pkgs/matcher/test/matcher/throws_test.dart
+++ b/pkgs/matcher/test/matcher/throws_test.dart
@@ -115,13 +115,13 @@
group('[throws]', () {
test('with a Future that throws an error', () {
// ignore: deprecated_member_use_from_same_package
- expect(Future.error('oh no'), throws);
+ expect(Future<Never>.error('oh no'), throws);
});
test("with a Future that doesn't throw", () async {
var monitor = await TestCaseMonitor.run(() {
// ignore: deprecated_member_use_from_same_package
- expect(Future.value(), throws);
+ expect(Future<void>.value(), throws);
});
expectTestFailed(
@@ -136,7 +136,7 @@
test('with a closure that returns a Future that throws an error', () {
// ignore: deprecated_member_use_from_same_package
- expect(() => Future.error('oh no'), throws);
+ expect(() => Future<Never>.error('oh no'), throws);
});
test("with a closure that returns a Future that doesn't throw", () async {
@@ -173,17 +173,17 @@
group('[throwsA]', () {
test('with a Future that throws an identical error', () {
- expect(Future.error('oh no'), throwsA('oh no'));
+ expect(Future<Never>.error('oh no'), throwsA('oh no'));
});
test('with a Future that throws a matching error', () {
- expect(Future.error(const FormatException('bad')),
+ expect(Future<Never>.error(const FormatException('bad')),
throwsA(isFormatException));
});
test("with a Future that doesn't throw", () async {
var monitor = await TestCaseMonitor.run(() {
- expect(Future.value(), throwsA('oh no'));
+ expect(Future<void>.value(), throwsA('oh no'));
});
expectTestFailed(
@@ -198,7 +198,7 @@
test('with a Future that throws the wrong error', () async {
var monitor = await TestCaseMonitor.run(() {
- expect(Future.error('aw dang'), throwsA('oh no'));
+ expect(Future<Never>.error('aw dang'), throwsA('oh no'));
});
expectTestFailed(
@@ -213,7 +213,7 @@
test('with a closure that returns a Future that throws a matching error',
() {
- expect(() => Future.error(const FormatException('bad')),
+ expect(() => Future<Never>.error(const FormatException('bad')),
throwsA(isFormatException));
});
@@ -235,7 +235,7 @@
test('with closure that returns a Future that throws the wrong error',
() async {
var monitor = await TestCaseMonitor.run(() {
- expect(() => Future.error('aw dang'), throwsA('oh no'));
+ expect(() => Future<Never>.error('aw dang'), throwsA('oh no'));
});
expectTestFailed(
@@ -264,7 +264,7 @@
});
test("blocks expectLater's Future", () async {
- var completer = Completer();
+ var completer = Completer<Never>();
var fired = false;
unawaited(expectLater(completer.future, throwsArgumentError).then((_) {
fired = true;
diff --git a/pkgs/matcher/test/mirror_matchers_test.dart b/pkgs/matcher/test/mirror_matchers_test.dart
index 06f0df4..18a9e8d 100644
--- a/pkgs/matcher/test/mirror_matchers_test.dart
+++ b/pkgs/matcher/test/mirror_matchers_test.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
// ignore_for_file: deprecated_member_use_from_same_package
+// ignore_for_file: unreachable_from_main
@TestOn('vm')
library;
diff --git a/pkgs/matcher/test/never_called_test.dart b/pkgs/matcher/test/never_called_test.dart
index 4c83e39..39e09da 100644
--- a/pkgs/matcher/test/never_called_test.dart
+++ b/pkgs/matcher/test/never_called_test.dart
@@ -15,7 +15,7 @@
test("doesn't throw if it isn't called", () async {
var monitor = await TestCaseMonitor.run(() {
- const Stream.empty().listen(neverCalled);
+ const Stream<Never>.empty().listen(neverCalled);
});
expectTestPassed(monitor);
diff --git a/pkgs/matcher/test/operator_matchers_test.dart b/pkgs/matcher/test/operator_matchers_test.dart
index f4b6d3a..46151e0 100644
--- a/pkgs/matcher/test/operator_matchers_test.dart
+++ b/pkgs/matcher/test/operator_matchers_test.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:matcher/matcher.dart';
-import 'package:test/test.dart' show test, expect, throwsArgumentError;
+import 'package:test/test.dart' show expect, test, throwsArgumentError;
import 'test_utils.dart';
diff --git a/pkgs/matcher/test/pretty_print_test.dart b/pkgs/matcher/test/pretty_print_test.dart
index 184704b..62f3ab1 100644
--- a/pkgs/matcher/test/pretty_print_test.dart
+++ b/pkgs/matcher/test/pretty_print_test.dart
@@ -6,7 +6,7 @@
import 'package:matcher/matcher.dart';
import 'package:matcher/src/pretty_print.dart';
-import 'package:test/test.dart' show group, test, expect;
+import 'package:test/test.dart' show expect, group, test;
class DefaultToString {}
diff --git a/pkgs/matcher/test/stream_matcher_test.dart b/pkgs/matcher/test/stream_matcher_test.dart
index c4af666..66e79c6 100644
--- a/pkgs/matcher/test/stream_matcher_test.dart
+++ b/pkgs/matcher/test/stream_matcher_test.dart
@@ -71,7 +71,7 @@
test('rejects an empty stream', () {
expect(
- expectLater(const Stream.empty(), emits(1)),
+ expectLater(const Stream<Never>.empty(), emits(1)),
throwsTestFailure(allOf([
startsWith('Expected: should emit an event that <1>\n'),
endsWith(' Which: emitted x Stream closed.\n')
@@ -96,7 +96,7 @@
group('emitsDone', () {
test('succeeds for an empty stream', () {
- expect(const Stream.empty(), emitsDone);
+ expect(const Stream<Never>.empty(), emitsDone);
});
test('fails for a stream with events', () {
diff --git a/pkgs/matcher/test/string_matchers_test.dart b/pkgs/matcher/test/string_matchers_test.dart
index be9e768..0820015 100644
--- a/pkgs/matcher/test/string_matchers_test.dart
+++ b/pkgs/matcher/test/string_matchers_test.dart
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:matcher/matcher.dart';
-import 'package:test/test.dart' show test, expect;
+import 'package:test/test.dart' show expect, test;
import 'test_utils.dart';
diff --git a/pkgs/matcher/test/type_matcher_test.dart b/pkgs/matcher/test/type_matcher_test.dart
index 99d4459..e0ca3d8 100644
--- a/pkgs/matcher/test/type_matcher_test.dart
+++ b/pkgs/matcher/test/type_matcher_test.dart
@@ -4,13 +4,13 @@
// ignore_for_file: deprecated_member_use_from_same_package
import 'package:matcher/matcher.dart';
-import 'package:test/test.dart' show test, group;
+import 'package:test/test.dart' show group, test;
import 'test_utils.dart';
void main() {
- _test(isMap, {}, name: 'Map');
- _test(isList, [], name: 'List');
+ _test(isMap, <Object?, Object?>{}, name: 'Map');
+ _test(isList, <Object?>[], name: 'List');
_test(isArgumentError, ArgumentError());
_test(isCastError, TypeError());
_test<Exception>(isException, const FormatException());
diff --git a/pkgs/test/CHANGELOG.md b/pkgs/test/CHANGELOG.md
index afe8dec..ed12636 100644
--- a/pkgs/test/CHANGELOG.md
+++ b/pkgs/test/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.25.14
+
+* Use secure random for url secrets.
+
+## 1.25.13
+
+* Allow the latest version of `package:matcher`.
+
## 1.25.12
* Fix hang when running multiple precompiled browser tests.
diff --git a/pkgs/test/lib/src/util/math.dart b/pkgs/test/lib/src/util/math.dart
index d6bbbc7..f0c340c 100644
--- a/pkgs/test/lib/src/util/math.dart
+++ b/pkgs/test/lib/src/util/math.dart
@@ -2,19 +2,11 @@
// for details. 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:convert';
import 'dart:math' as math;
-final _rand = math.Random();
+final _rand = math.Random.secure();
-/// Returns a random 32 character alphanumeric string ([a-zA-Z0-9]), which is
-/// suitable as a url secret.
-String randomUrlSecret() {
- var buffer = StringBuffer();
- while (buffer.length < 32) {
- buffer.write(_alphaChars[_rand.nextInt(_alphaChars.length)]);
- }
- return buffer.toString();
-}
-
-const _alphaChars =
- '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+/// Returns a random 64 bit token suitable as a url secret.
+String randomUrlSecret() =>
+ base64Url.encode(List.generate(8, (_) => _rand.nextInt(256)));
diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml
index 0ab65ad..1c79652 100644
--- a/pkgs/test/pubspec.yaml
+++ b/pkgs/test/pubspec.yaml
@@ -1,5 +1,5 @@
name: test
-version: 1.25.12
+version: 1.25.14
description: >-
A full featured library for writing and running Dart tests across platforms.
repository: https://github.com/dart-lang/test/tree/master/pkgs/test
@@ -20,7 +20,7 @@
# Use a tight version constraint to ensure that a constraint on matcher
# properly constrains all features it provides.
- matcher: '>=0.12.16 <0.12.17'
+ matcher: '>=0.12.16 <0.12.18'
node_preamble: ^2.0.0
package_config: ^2.0.0
diff --git a/pkgs/test_process/.gitignore b/pkgs/test_process/.gitignore
new file mode 100644
index 0000000..0659a33
--- /dev/null
+++ b/pkgs/test_process/.gitignore
@@ -0,0 +1,9 @@
+.buildlog
+.DS_Store
+.idea
+.settings/
+build/
+packages
+.packages
+pubspec.lock
+.dart_tool/
diff --git a/pkgs/test_process/AUTHORS b/pkgs/test_process/AUTHORS
new file mode 100644
index 0000000..e8063a8
--- /dev/null
+++ b/pkgs/test_process/AUTHORS
@@ -0,0 +1,6 @@
+# Below is a list of people and organizations that have contributed
+# to the project. Names should be added to the list like so:
+#
+# Name/Organization <email address>
+
+Google Inc.
diff --git a/pkgs/test_process/CHANGELOG.md b/pkgs/test_process/CHANGELOG.md
new file mode 100644
index 0000000..b267290
--- /dev/null
+++ b/pkgs/test_process/CHANGELOG.md
@@ -0,0 +1,67 @@
+## 2.1.1
+
+* Require Dart 3.1.
+* Move to `dart-lang/test` monorepo.
+
+## 2.1.0
+
+- Remove the expectation that the process exits during the normal test body.
+ The process will still be killed during teardown if it has not exited. The
+ check can be manually restored with `shouldExit()`.
+
+## 2.0.3
+
+- Populate the pubspec `repository` field.
+- Fixed examples in `readme.md`.
+- Added `example/example.dart`
+- Require Dart >=2.17
+
+## 2.0.2
+
+- Reverted `meta` constraint to `^1.3.0`.
+
+## 2.0.1
+
+- Update `meta` constraint to `>=1.3.0 <3.0.0`.
+
+## 2.0.0
+
+- Migrate to null safety.
+
+## 1.0.6
+
+- Require Dart >=2.1
+
+## 1.0.5
+
+- Don't allow the test to time out as long as the process is emitting output.
+
+## 1.0.4
+
+- Set max SDK version to `<3.0.0`, and adjust other dependencies.
+
+## 1.0.3
+
+- Support test `1.x.x`.
+
+## 1.0.2
+
+- Update SDK version to 2.0.0-dev.17.0
+
+## 1.0.1
+
+- Declare support for `async` 2.0.0.
+
+## 1.0.0
+
+- Added `pid` and `exitCode` getters to `TestProcess`.
+
+## 1.0.0-rc.2
+
+- Subclassed `TestProcess`es now emit log output based on the superclass's
+ standard IO streams rather than the subclass's. This matches the documented
+ behavior.
+
+## 1.0.0-rc.1
+
+- Initial release candidate.
diff --git a/pkgs/test_process/LICENSE b/pkgs/test_process/LICENSE
new file mode 100644
index 0000000..aa86769
--- /dev/null
+++ b/pkgs/test_process/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2017, the Dart project authors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google LLC nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pkgs/test_process/README.md b/pkgs/test_process/README.md
new file mode 100644
index 0000000..0d4e5f5
--- /dev/null
+++ b/pkgs/test_process/README.md
@@ -0,0 +1,123 @@
+[](https://github.com/dart-lang/test/actions/workflows/test_process.yaml)
+[](https://pub.dev/packages/test_process)
+[](https://pub.dev/packages/test_process/publisher)
+
+A package for testing subprocesses.
+
+This exposes a [`TestProcess`][TestProcess] class that wraps `dart:io`'s
+[`Process`][Process] class and makes it easy to read standard output
+line-by-line. `TestProcess` works the same as `Process` in many ways, but there
+are a few major differences.
+
+[TestProcess]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess-class.html
+[Process]: https://api.dart.dev/stable/dart-io/Process-class.html
+
+## Standard Output
+
+`Process.stdout` and `Process.stderr` are binary streams, which is the most
+general API but isn't the most helpful when working with a program that produces
+plain text. Instead, [`TestProcess.stdout`][stdout] and
+[`TestProcess.stderr`][stderr] emit a string for each line of output the process
+produces. What's more, they're [`StreamQueue`][StreamQueue]s, which means
+they provide a *pull-based API*. For example:
+
+[stdout]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stdout.html
+[stderr]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stderr.html
+[StreamQueue]: https://pub.dev/documentation/async/latest/async/StreamQueue-class.html
+
+```dart
+import 'package:test/test.dart';
+import 'package:test_process/test_process.dart';
+
+void main() {
+ test('pub get gets dependencies', () async {
+ // TestProcess.start() works just like Process.start() from dart:io.
+ var process = await TestProcess.start('dart', ['pub', 'get']);
+
+ // StreamQueue.next returns the next line emitted on standard out.
+ var firstLine = await process.stdout.next;
+ expect(firstLine, equals('Resolving dependencies...'));
+
+ // Each call to StreamQueue.next moves one line further.
+ String next;
+ do {
+ next = await process.stdout.next;
+ } while (next != 'Got dependencies!');
+
+ // Assert that the process exits with code 0.
+ await process.shouldExit(0);
+ });
+}
+```
+
+The `test` package's [stream matchers][] have built-in support for
+`StreamQueues`, which makes them perfect for making assertions about a process's
+output. We can use this to clean up the previous example:
+
+[stream matchers]: https://github.com/dart-lang/test#stream-matchers
+
+```dart
+import 'package:test/test.dart';
+import 'package:test_process/test_process.dart';
+
+void main() {
+ test('pub get gets dependencies', () async {
+ var process = await TestProcess.start('dart', ['pub', 'get']);
+
+ // Each stream matcher will consume as many lines as it matches from a
+ // StreamQueue, and no more, so it's safe to use them in sequence.
+ await expectLater(process.stdout, emits('Resolving dependencies...'));
+
+ // The emitsThrough matcher matches and consumes any number of lines, as
+ // long as they end with one matching the argument.
+ await expectLater(process.stdout, emitsThrough('Got dependencies!'));
+
+ await process.shouldExit(0);
+ });
+}
+```
+
+If you want to access the standard output streams without consuming any values
+from the queues, you can use the [`stdoutStream()`][stdoutStream] and
+[`stderrStream()`][stderrStream] methods. Each time you call one of these, it
+produces an entirely new stream that replays the corresponding output stream
+from the beginning, regardless of what's already been produced by `stdout`,
+`stderr`, or other calls to the stream method.
+
+[stdoutStream]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stdoutStream.html
+[stderrStream]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/stderrStream.html
+
+## Signals and Termination
+
+The way signaling works is different from `dart:io` as well. `TestProcess` still
+has a [`kill()`][kill] method, but it defaults to `SIGKILL` on Mac OS and Linux
+to ensure (as best as possible) that processes die without leaving behind
+zombies. If you want to send a particular signal (which is unsupported on
+Windows), you can do so by explicitly calling [`signal()`][signal].
+
+[kill]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/kill.html
+[signal]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/signal.html
+
+In addition to [`exitCode`][exitCode], which works the same as in `dart:io`,
+`TestProcess` also adds a new method named [`shouldExit()`][shouldExit]. This
+lets tests wait for a process to exit, and (if desired) assert what particular
+exit code it produced.
+
+[exitCode]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/exitCode.html
+[shouldExit]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/shouldExit.html
+
+## Debugging Output
+
+When a test using `TestProcess` fails, it will print all the output produced by
+that process. This makes it much easier to figure out what went wrong and why.
+The debugging output uses a header based on the process's invocation by
+default, but you can pass in custom `description` parameters to
+[`TestProcess.start()`][start] to control the headers.
+
+[start]: https://pub.dev/documentation/test_process/latest/test_process/TestProcess/start.html
+
+`TestProcess` will also produce debugging output as the test runs if you pass
+`forwardStdio: true` to `TestProcess.start()`. This can be particularly useful
+when you're using an interactive debugger and you want to figure out what a
+process is doing before the test finishes and the normal debugging output is
+printed.
diff --git a/pkgs/test_process/analysis_options.yaml b/pkgs/test_process/analysis_options.yaml
new file mode 100644
index 0000000..5607754
--- /dev/null
+++ b/pkgs/test_process/analysis_options.yaml
@@ -0,0 +1,18 @@
+# https://dart.dev/tools/analysis#the-analysis-options-file
+include: package:dart_flutter_team_lints/analysis_options.yaml
+
+analyzer:
+ language:
+ strict-casts: true
+ strict-inference: true
+ strict-raw-types: true
+
+linter:
+ rules:
+ - avoid_unused_constructor_parameters
+ - cancel_subscriptions
+ - literal_only_boolean_expressions
+ - missing_whitespace_between_adjacent_strings
+ - no_adjacent_strings_in_list
+ - no_runtimeType_toString
+ - unnecessary_await_in_return
diff --git a/pkgs/test_process/example/example.dart b/pkgs/test_process/example/example.dart
new file mode 100644
index 0000000..22175f4
--- /dev/null
+++ b/pkgs/test_process/example/example.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+import 'package:test_process/test_process.dart';
+
+void main() {
+ test('pub get gets dependencies', () async {
+ // TestProcess.start() works just like Process.start() from dart:io.
+ var process = await TestProcess.start('dart', ['pub', 'get']);
+
+ // StreamQueue.next returns the next line emitted on standard out.
+ var firstLine = await process.stdout.next;
+ expect(firstLine, equals('Resolving dependencies...'));
+
+ // Each call to StreamQueue.next moves one line further.
+ String next;
+ do {
+ next = await process.stdout.next;
+ } while (next != 'Got dependencies!');
+
+ // Assert that the process exits with code 0.
+ await process.shouldExit(0);
+ });
+}
diff --git a/pkgs/test_process/lib/test_process.dart b/pkgs/test_process/lib/test_process.dart
new file mode 100644
index 0000000..0441fb1
--- /dev/null
+++ b/pkgs/test_process/lib/test_process.dart
@@ -0,0 +1,239 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// for details. 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:convert';
+import 'dart:io';
+
+import 'package:async/async.dart';
+import 'package:meta/meta.dart';
+import 'package:path/path.dart' as p;
+import 'package:test/test.dart';
+
+/// A wrapper for [Process] that provides a convenient API for testing its
+/// standard IO and interacting with it from a test.
+///
+/// If the test fails, this will automatically print out any stdout and stderr
+/// from the process to aid debugging.
+///
+/// This may be extended to provide custom implementations of [stdoutStream] and
+/// [stderrStream]. These will automatically be picked up by the [stdout] and
+/// [stderr] queues, but the debug log will still contain the original output.
+class TestProcess {
+ /// The underlying process.
+ final Process _process;
+
+ /// A human-friendly description of this process.
+ final String description;
+
+ /// A [StreamQueue] that emits each line of stdout from the process.
+ ///
+ /// A copy of the underlying stream can be retrieved using [stdoutStream].
+ late final StreamQueue<String> stdout = StreamQueue(stdoutStream());
+
+ /// A [StreamQueue] that emits each line of stderr from the process.
+ ///
+ /// A copy of the underlying stream can be retrieved using [stderrStream].
+ late final StreamQueue<String> stderr = StreamQueue(stderrStream());
+
+ /// A splitter that can emit new copies of [stdout].
+ final StreamSplitter<String> _stdoutSplitter;
+
+ /// A splitter that can emit new copies of [stderr].
+ final StreamSplitter<String> _stderrSplitter;
+
+ /// The standard input sink for this process.
+ IOSink get stdin => _process.stdin;
+
+ /// A buffer of mixed stdout and stderr lines.
+ final List<String> _log = <String>[];
+
+ /// Whether [_log] has been passed to [printOnFailure] yet.
+ bool _loggedOutput = false;
+
+ /// Returns a [Future] which completes to the exit code of the process, once
+ /// it completes.
+ Future<int> get exitCode => _process.exitCode;
+
+ /// The process ID of the process.
+ int get pid => _process.pid;
+
+ /// Completes to [_process]'s exit code if it's exited, otherwise completes to
+ /// `null` immediately.
+ Future<int?> get _exitCodeOrNull => exitCode
+ .then<int?>((value) => value)
+ .timeout(Duration.zero, onTimeout: () => null);
+
+ /// Starts a process.
+ ///
+ /// [executable], [arguments], [workingDirectory], and [environment] have the
+ /// same meaning as for [Process.start].
+ ///
+ /// [description] is a string description of this process; it defaults to the
+ /// command-line invocation. [encoding] is the [Encoding] that will be used
+ /// for the process's input and output; it defaults to [utf8].
+ ///
+ /// If [forwardStdio] is `true`, the process's stdout and stderr will be
+ /// printed to the console as they appear. This is only intended to be set
+ /// temporarily to help when debugging test failures.
+ static Future<TestProcess> start(
+ String executable, Iterable<String> arguments,
+ {String? workingDirectory,
+ Map<String, String>? environment,
+ bool includeParentEnvironment = true,
+ bool runInShell = false,
+ String? description,
+ Encoding encoding = utf8,
+ bool forwardStdio = false}) async {
+ var process = await Process.start(executable, arguments.toList(),
+ workingDirectory: workingDirectory,
+ environment: environment,
+ includeParentEnvironment: includeParentEnvironment,
+ runInShell: runInShell);
+
+ if (description == null) {
+ var humanExecutable = p.isWithin(p.current, executable)
+ ? p.relative(executable)
+ : executable;
+ description = "$humanExecutable ${arguments.join(" ")}";
+ }
+
+ return TestProcess(process, description,
+ encoding: encoding, forwardStdio: forwardStdio);
+ }
+
+ /// Creates a [TestProcess] for [process].
+ ///
+ /// The [description], [encoding], and [forwardStdio] are the same as those to
+ /// [start].
+ ///
+ /// This is protected, which means it should only be called by subclasses.
+ @protected
+ TestProcess(Process process, this.description,
+ {Encoding encoding = utf8, bool forwardStdio = false})
+ : _process = process,
+ _stdoutSplitter = StreamSplitter(process.stdout
+ .transform(encoding.decoder)
+ .transform(const LineSplitter())),
+ _stderrSplitter = StreamSplitter(process.stderr
+ .transform(encoding.decoder)
+ .transform(const LineSplitter())) {
+ addTearDown(_tearDown);
+
+ _process.exitCode.whenComplete(_logOutput);
+
+ // Listen eagerly so that the lines are interleaved properly between the two
+ // streams.
+ //
+ // Call [split] explicitly because we don't want to log overridden
+ // [stdoutStream] or [stderrStream] output.
+ _stdoutSplitter.split().listen((line) {
+ _heartbeat();
+ if (forwardStdio) print(line);
+ _log.add(' $line');
+ });
+
+ _stderrSplitter.split().listen((line) {
+ _heartbeat();
+ if (forwardStdio) print(line);
+ _log.add('[e] $line');
+ });
+ }
+
+ /// A callback that's run when the test completes.
+ Future<void> _tearDown() async {
+ // If the process is already dead, do nothing.
+ if (await _exitCodeOrNull != null) return;
+
+ _process.kill(ProcessSignal.sigkill);
+
+ // Log output now rather than waiting for the exitCode callback so that
+ // it's visible even if we time out waiting for the process to die.
+ await _logOutput();
+ }
+
+ /// Formats the contents of [_log] and passes them to [printOnFailure].
+ Future<void> _logOutput() async {
+ if (_loggedOutput) return;
+ _loggedOutput = true;
+
+ var exitCodeOrNull = await _exitCodeOrNull;
+
+ // Wait a timer tick to ensure that all available lines have been flushed to
+ // [_log].
+ await Future<void>.delayed(Duration.zero);
+
+ var buffer = StringBuffer();
+ buffer.write('Process `$description` ');
+ if (exitCodeOrNull == null) {
+ buffer.writeln('was killed with SIGKILL in a tear-down. Output:');
+ } else {
+ buffer.writeln('exited with exitCode $exitCodeOrNull. Output:');
+ }
+
+ buffer.writeln(_log.join('\n'));
+ printOnFailure(buffer.toString());
+ }
+
+ /// Returns a copy of [stdout] as a single-subscriber stream.
+ ///
+ /// Each time this is called, it will return a separate copy that will start
+ /// from the beginning of the process.
+ ///
+ /// This can be overridden by subclasses to return a derived standard output
+ /// stream. This stream will then be used for [stdout].
+ Stream<String> stdoutStream() => _stdoutSplitter.split();
+
+ /// Returns a copy of [stderr] as a single-subscriber stream.
+ ///
+ /// Each time this is called, it will return a separate copy that will start
+ /// from the beginning of the process.
+ ///
+ /// This can be overridden by subclasses to return a derived standard output
+ /// stream. This stream will then be used for [stderr].
+ Stream<String> stderrStream() => _stderrSplitter.split();
+
+ /// Sends [signal] to the process.
+ ///
+ /// This is meant for sending specific signals. If you just want to kill the
+ /// process, use [kill] instead.
+ ///
+ /// Throws an [UnsupportedError] on Windows.
+ void signal(ProcessSignal signal) {
+ if (Platform.isWindows) {
+ throw UnsupportedError(
+ "TestProcess.signal() isn't supported on Windows.");
+ }
+
+ _process.kill(signal);
+ }
+
+ /// Kills the process (with SIGKILL on POSIX operating systems), and returns a
+ /// future that completes once it's dead.
+ ///
+ /// If this is called after the process is already dead, it does nothing.
+ Future<void> kill() async {
+ _process.kill(ProcessSignal.sigkill);
+ await exitCode;
+ }
+
+ /// Waits for the process to exit, and verifies that the exit code matches
+ /// [expectedExitCode] (if given).
+ ///
+ /// If this is called after the process is already dead, it verifies its
+ /// existing exit code.
+ Future<void> shouldExit([Object? expectedExitCode]) async {
+ var exitCode = await this.exitCode;
+ if (expectedExitCode == null) return;
+ expect(exitCode, expectedExitCode,
+ reason: 'Process `$description` had an unexpected exit code.');
+ }
+
+ /// Signal to the test runner that the test is still making progress and
+ /// shouldn't time out.
+ void _heartbeat() {
+ // Interacting with the test runner's asynchronous expectation logic will
+ // notify it that the test is alive.
+ expectAsync0(() {})();
+ }
+}
diff --git a/pkgs/test_process/pubspec.yaml b/pkgs/test_process/pubspec.yaml
new file mode 100644
index 0000000..60158d4
--- /dev/null
+++ b/pkgs/test_process/pubspec.yaml
@@ -0,0 +1,18 @@
+name: test_process
+version: 2.1.1
+description: |
+ Test processes: starting; validating stdout and stderr; checking exit code
+repository: https://github.com/dart-lang/test/tree/master/pkgs/test_process
+
+environment:
+ sdk: ^3.1.0
+
+dependencies:
+ async: ^2.5.0
+ meta: ^1.3.0
+ path: ^1.8.0
+ test: ^1.16.6
+
+dev_dependencies:
+ dart_flutter_team_lints: ^3.0.0
+ test_descriptor: ^2.0.0
diff --git a/pkgs/test_process/test/test_process_test.dart b/pkgs/test_process/test/test_process_test.dart
new file mode 100644
index 0000000..9cfb779
--- /dev/null
+++ b/pkgs/test_process/test/test_process_test.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// for details. 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 p;
+import 'package:test/test.dart';
+import 'package:test_descriptor/test_descriptor.dart' as d;
+import 'package:test_process/test_process.dart';
+
+final throwsTestFailure = throwsA(isA<TestFailure>());
+
+void main() {
+ group('shouldExit()', () {
+ test('succeeds when the process exits with the given exit code', () async {
+ var process = await startDartProcess('exitCode = 42;');
+ expect(process.exitCode, completion(equals(42)));
+ await process.shouldExit(greaterThan(12));
+ });
+
+ test('fails when the process exits with a different exit code', () async {
+ var process = await startDartProcess('exitCode = 1;');
+ expect(process.exitCode, completion(equals(1)));
+ expect(process.shouldExit(greaterThan(12)), throwsTestFailure);
+ });
+
+ test('allows any exit code without an assertion', () async {
+ var process = await startDartProcess('exitCode = 1;');
+ expect(process.exitCode, completion(equals(1)));
+ await process.shouldExit();
+ });
+ });
+
+ test('kill() stops the process', () async {
+ var process = await startDartProcess('while (true);');
+
+ // Should terminate.
+ await process.kill();
+ });
+
+ group('stdout and stderr', () {
+ test("expose the process's standard io", () async {
+ var process = await startDartProcess(r'''
+ print("hello");
+ stderr.writeln("hi");
+ print("\nworld");
+ ''');
+
+ expect(process.stdout, emitsInOrder(['hello', '', 'world', emitsDone]));
+ expect(process.stderr, emitsInOrder(['hi', emitsDone]));
+ await process.shouldExit(0);
+ });
+
+ test('close when the process exits', () async {
+ var process = await startDartProcess('');
+ expect(expectLater(process.stdout, emits('hello')), throwsTestFailure);
+ expect(expectLater(process.stderr, emits('world')), throwsTestFailure);
+ await process.shouldExit(0);
+ });
+ });
+
+ test("stdoutStream() and stderrStream() copy the process's standard io",
+ () async {
+ var process = await startDartProcess(r'''
+ print("hello");
+ stderr.writeln("hi");
+ print("\nworld");
+ ''');
+
+ expect(process.stdoutStream(),
+ emitsInOrder(['hello', '', 'world', emitsDone]));
+ expect(process.stdoutStream(),
+ emitsInOrder(['hello', '', 'world', emitsDone]));
+
+ expect(process.stderrStream(), emitsInOrder(['hi', emitsDone]));
+ expect(process.stderrStream(), emitsInOrder(['hi', emitsDone]));
+
+ await process.shouldExit(0);
+
+ expect(process.stdoutStream(),
+ emitsInOrder(['hello', '', 'world', emitsDone]));
+ expect(process.stderrStream(), emitsInOrder(['hi', emitsDone]));
+ });
+
+ test('stdin writes to the process', () async {
+ var process = await startDartProcess(r'''
+ stdinLines.listen((line) => print("> $line"));
+ ''');
+
+ process.stdin.writeln('hello');
+ await expectLater(process.stdout, emits('> hello'));
+ process.stdin.writeln('world');
+ await expectLater(process.stdout, emits('> world'));
+ await process.kill();
+ });
+
+ test('signal sends a signal to the process', () async {
+ var process = await startDartProcess(r'''
+ ProcessSignal.sighup.watch().listen((_) => print("HUP"));
+ print("ready");
+ ''');
+
+ await expectLater(process.stdout, emits('ready'));
+ process.signal(ProcessSignal.sighup);
+ await expectLater(process.stdout, emits('HUP'));
+ await process.kill();
+ }, testOn: '!windows');
+
+ test('allows a long-running process', () async {
+ await startDartProcess(r'''
+ await Future.delayed(Duration(minutes: 10));
+ ''');
+ // Test should not time out.
+ });
+}
+
+/// Starts a Dart process running [script] in a main method.
+Future<TestProcess> startDartProcess(String script) {
+ var dartPath = p.join(d.sandbox, 'test.dart');
+ File(dartPath).writeAsStringSync('''
+ import 'dart:async';
+ import 'dart:convert';
+ import 'dart:io';
+
+ var stdinLines = stdin
+ .transform(utf8.decoder)
+ .transform(new LineSplitter());
+
+ void main() {
+ $script
+ }
+ ''');
+
+ return TestProcess.start(Platform.executable, ['--enable-asserts', dartPath]);
+}