From 3149329872ec4361eb7a7bf35f0aceb5290fd5a5 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Thu, 25 Sep 2025 16:42:07 +0200 Subject: [PATCH 1/2] Run end to end tests on macos. --- .github/workflows/dart.yml | 144 +++++++++++++++++++++++++++++++++++-- build_runner/mono_pkg.yaml | 4 ++ 2 files changed, 144 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 9d4cc823e1..411ca4037d 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -736,6 +736,142 @@ jobs: - job_002 - job_003 job_018: + name: "test; macos; PKG: build_runner; `dart test -t integration1 --test-randomize-ordering-seed=random`" + runs-on: macos-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 + with: + path: "~/.pub-cache/hosted" + key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:build_runner;commands:test_4" + restore-keys: | + os:macos-latest;pub-cache-hosted;sdk:dev;packages:build_runner + os:macos-latest;pub-cache-hosted;sdk:dev + os:macos-latest;pub-cache-hosted + os:macos-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + with: + sdk: dev + - id: checkout + name: Checkout repository + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - id: build_runner_pub_upgrade + name: build_runner; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: build_runner + - name: "build_runner; dart test -t integration1 --test-randomize-ordering-seed=random" + run: "dart test -t integration1 --test-randomize-ordering-seed=random" + if: "always() && steps.build_runner_pub_upgrade.conclusion == 'success'" + working-directory: build_runner + needs: + - job_001 + - job_002 + - job_003 + job_019: + name: "test; macos; PKG: build_runner; `dart test -t integration2 --test-randomize-ordering-seed=random`" + runs-on: macos-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 + with: + path: "~/.pub-cache/hosted" + key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:build_runner;commands:test_5" + restore-keys: | + os:macos-latest;pub-cache-hosted;sdk:dev;packages:build_runner + os:macos-latest;pub-cache-hosted;sdk:dev + os:macos-latest;pub-cache-hosted + os:macos-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + with: + sdk: dev + - id: checkout + name: Checkout repository + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - id: build_runner_pub_upgrade + name: build_runner; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: build_runner + - name: "build_runner; dart test -t integration2 --test-randomize-ordering-seed=random" + run: "dart test -t integration2 --test-randomize-ordering-seed=random" + if: "always() && steps.build_runner_pub_upgrade.conclusion == 'success'" + working-directory: build_runner + needs: + - job_001 + - job_002 + - job_003 + job_020: + name: "test; macos; PKG: build_runner; `dart test -t integration3 --test-randomize-ordering-seed=random`" + runs-on: macos-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 + with: + path: "~/.pub-cache/hosted" + key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:build_runner;commands:test_6" + restore-keys: | + os:macos-latest;pub-cache-hosted;sdk:dev;packages:build_runner + os:macos-latest;pub-cache-hosted;sdk:dev + os:macos-latest;pub-cache-hosted + os:macos-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + with: + sdk: dev + - id: checkout + name: Checkout repository + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - id: build_runner_pub_upgrade + name: build_runner; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: build_runner + - name: "build_runner; dart test -t integration3 --test-randomize-ordering-seed=random" + run: "dart test -t integration3 --test-randomize-ordering-seed=random" + if: "always() && steps.build_runner_pub_upgrade.conclusion == 'success'" + working-directory: build_runner + needs: + - job_001 + - job_002 + - job_003 + job_021: + name: "test; macos; PKG: build_runner; `dart test -t integration4 --test-randomize-ordering-seed=random`" + runs-on: macos-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 + with: + path: "~/.pub-cache/hosted" + key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:build_runner;commands:test_7" + restore-keys: | + os:macos-latest;pub-cache-hosted;sdk:dev;packages:build_runner + os:macos-latest;pub-cache-hosted;sdk:dev + os:macos-latest;pub-cache-hosted + os:macos-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + with: + sdk: dev + - id: checkout + name: Checkout repository + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - id: build_runner_pub_upgrade + name: build_runner; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: build_runner + - name: "build_runner; dart test -t integration4 --test-randomize-ordering-seed=random" + run: "dart test -t integration4 --test-randomize-ordering-seed=random" + if: "always() && steps.build_runner_pub_upgrade.conclusion == 'success'" + working-directory: build_runner + needs: + - job_001 + - job_002 + - job_003 + job_022: name: "test; windows; PKG: build_daemon; `dart test --test-randomize-ordering-seed=random`" runs-on: windows-latest steps: @@ -759,7 +895,7 @@ jobs: - job_001 - job_002 - job_003 - job_019: + job_023: name: "test; windows; PKG: build_runner; `dart test -t integration1 --test-randomize-ordering-seed=random`" runs-on: windows-latest steps: @@ -783,7 +919,7 @@ jobs: - job_001 - job_002 - job_003 - job_020: + job_024: name: "test; windows; PKG: build_runner; `dart test -t integration2 --test-randomize-ordering-seed=random`" runs-on: windows-latest steps: @@ -807,7 +943,7 @@ jobs: - job_001 - job_002 - job_003 - job_021: + job_025: name: "test; windows; PKG: build_runner; `dart test -t integration3 --test-randomize-ordering-seed=random`" runs-on: windows-latest steps: @@ -831,7 +967,7 @@ jobs: - job_001 - job_002 - job_003 - job_022: + job_026: name: "test; windows; PKG: build_runner; `dart test -t integration4 --test-randomize-ordering-seed=random`" runs-on: windows-latest steps: diff --git a/build_runner/mono_pkg.yaml b/build_runner/mono_pkg.yaml index 58feb2451b..5230e86690 100644 --- a/build_runner/mono_pkg.yaml +++ b/build_runner/mono_pkg.yaml @@ -14,17 +14,21 @@ stages: - test: -t integration1 --test-randomize-ordering-seed=random os: - linux + - macos - windows - test: -t integration2 --test-randomize-ordering-seed=random os: - linux + - macos - windows - test: -t integration3 --test-randomize-ordering-seed=random os: - linux + - macos - windows - test: -t integration4 --test-randomize-ordering-seed=random os: - linux + - macos - windows - command: ../tool/leak_check.sh From 82b8c9c5d6ee2221c1a7e427bea45fafc604ba4e Mon Sep 17 00:00:00 2001 From: David Morgan Date: Thu, 25 Sep 2025 17:45:32 +0200 Subject: [PATCH 2/2] Fix `build_daemon` workspace finding with symlinks. --- build_daemon/CHANGELOG.md | 2 ++ build_daemon/lib/constants.dart | 11 ++++++++ build_daemon/test/daemon_test.dart | 28 +++++++++---------- build_daemon/test/uuid.dart | 15 ++++------ .../daemon_command_test.dart | 4 +++ 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/build_daemon/CHANGELOG.md b/build_daemon/CHANGELOG.md index 3a330fdb39..12282dabb2 100644 --- a/build_daemon/CHANGELOG.md +++ b/build_daemon/CHANGELOG.md @@ -1,5 +1,7 @@ ## 4.0.5-wip +- Bug fix: resolve symlinks when identifying workspaces, so symlinks can't + cause the same workspace to be treated as a different workspace. - Bump the min SDK to 3.7.0. - Remove unused dep: `analyzer`. - Add `connectUnchecked` for use in tests. diff --git a/build_daemon/lib/constants.dart b/build_daemon/lib/constants.dart index ece0e31064..caea0ac989 100644 --- a/build_daemon/lib/constants.dart +++ b/build_daemon/lib/constants.dart @@ -44,6 +44,17 @@ var _username = Platform.environment['USER'] ?? ''; String daemonWorkspace(String workingDirectory) { final segments = [Directory.systemTemp.path]; if (_username.isNotEmpty) segments.add(_username); + + // Canonicalize the working directory so it can be used as a key. First + // `p.canonicalize` which makes it absolute but does not resolve symlinks. + workingDirectory = p.canonicalize(workingDirectory); + // Now resolve symlinks. + try { + workingDirectory = Directory(workingDirectory).resolveSymbolicLinksSync(); + } on FileSystemException catch (_) { + // Probably the directory does not exist because this is a test, ignore. + } + final workingDirHash = base64UrlEncode( md5.convert(workingDirectory.codeUnits).bytes, ); diff --git a/build_daemon/test/daemon_test.dart b/build_daemon/test/daemon_test.dart index 1008ebb2ac..11aefce631 100644 --- a/build_daemon/test/daemon_test.dart +++ b/build_daemon/test/daemon_test.dart @@ -41,7 +41,7 @@ void main() { } }); test('can be stopped', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); final daemon = Daemon(workspace); await daemon.start({}, FakeDaemonBuilder(), FakeChangeProvider()); @@ -49,13 +49,13 @@ void main() { await daemon.stop(); }); test('can run if no other daemon is running', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); final daemon = await _runDaemon(workspace); testDaemons.add(daemon); expect(await _statusOf(daemon), 'RUNNING'); }); test('shuts down if no client connects', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); final daemon = await _runDaemon(workspace, timeout: 1); testDaemons.add(daemon); expect(await daemon.exitCode, isNotNull); @@ -63,7 +63,7 @@ void main() { test( 'can not run if another daemon is running in the same workspace', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); final daemonOne = await _runDaemon( workspace, @@ -79,8 +79,8 @@ void main() { test( 'can run if another daemon is running in a different workspace', () async { - final workspace1 = generateV4UUID(); - final workspace2 = generateV4UUID(); + final workspace1 = randomWorkspace(); + final workspace2 = randomWorkspace(); testWorkspaces.addAll([workspace1, workspace2]); final daemonOne = await _runDaemon(workspace1); expect(await _statusOf(daemonOne), 'RUNNING'); @@ -91,7 +91,7 @@ void main() { timeout: const Timeout.factor(2), ); test('can start two daemons at the same time', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); final daemonOne = await _runDaemon(workspace); expect(await _statusOf(daemonOne), 'RUNNING'); @@ -100,7 +100,7 @@ void main() { testDaemons.addAll([daemonOne, daemonTwo]); }, timeout: const Timeout.factor(2)); test('logs the version when running', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); final daemon = await _runDaemon(workspace); testDaemons.add(daemon); @@ -108,12 +108,12 @@ void main() { expect(await Daemon(workspace).runningVersion(), currentVersion); }); test('does not set the current version if not running', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); expect(await Daemon(workspace).runningVersion(), null); }); test('logs the options when running', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); final daemon = await _runDaemon(workspace); testDaemons.add(daemon); @@ -124,12 +124,12 @@ void main() { ); }); test('does not log the options if not running', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); expect((await Daemon(workspace).currentOptions()).isEmpty, isTrue); }); test('cleans up after itself', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); final daemon = await _runDaemon(workspace); // Wait for the daemon to be running before checking the workspace exits. @@ -141,7 +141,7 @@ void main() { expect(Directory(daemonWorkspace(workspace)).existsSync(), isFalse); }); test('daemon stops after file changes stream has error', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); final daemon = await _runDaemon( workspace, @@ -152,7 +152,7 @@ void main() { expect(Directory(daemonWorkspace(workspace)).existsSync(), isFalse); }); test('daemon stops after file changes stream is closed', () async { - final workspace = generateV4UUID(); + final workspace = randomWorkspace(); testWorkspaces.add(workspace); final daemon = await _runDaemon( workspace, diff --git a/build_daemon/test/uuid.dart b/build_daemon/test/uuid.dart index 3e77d8db9e..b84d395f60 100644 --- a/build_daemon/test/uuid.dart +++ b/build_daemon/test/uuid.dart @@ -6,19 +6,14 @@ import 'dart:math' show Random; -/// A UUID generator. +/// A random workspace path. /// -/// The generated values are 128 bit numbers encoded in a specific string -/// format. -/// -/// Generate a version 4 (random) uuid. This is a uuid scheme that only uses -/// random numbers as the source of the generated uuid. -// TODO: replace with a MUCH more simple, random string that matches -// the use case. -String generateV4UUID() { +/// It's an absolute path, so it won't be interpreted differently based on the +/// current working directory. +String randomWorkspace() { final special = 8 + _random.nextInt(4); - return '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-' + return '/${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-' '${_bitsDigits(16, 4)}-' '4${_bitsDigits(12, 3)}-' '${_printDigits(special, 1)}${_bitsDigits(12, 3)}-' diff --git a/build_runner/test/integration_tests/daemon_command_test.dart b/build_runner/test/integration_tests/daemon_command_test.dart index dbe70c74d3..b465df60ff 100644 --- a/build_runner/test/integration_tests/daemon_command_test.dart +++ b/build_runner/test/integration_tests/daemon_command_test.dart @@ -88,6 +88,7 @@ void main() { // Start client. var client = await BuildDaemonClient.connectUnchecked( p.join(tester.tempDirectory.path, 'root_pkg'), + logHandler: (event) => printOnFailure('(0) ${event.message}'), ); addTearDown(client.close); @@ -134,6 +135,7 @@ void main() { // Builds. client = await BuildDaemonClient.connectUnchecked( p.join(tester.tempDirectory.path, 'root_pkg'), + logHandler: (event) => printOnFailure('(1) ${event.message}'), ); addTearDown(client.close); client.registerBuildTarget(webTarget); @@ -170,12 +172,14 @@ void main() { await daemon.expect(readyToConnectLog); client = await BuildDaemonClient.connectUnchecked( p.join(tester.tempDirectory.path, 'root_pkg'), + logHandler: (event) => printOnFailure('(2) ${event.message}'), ); results = StreamQueue(client.buildResults); addTearDown(client.close); // Connect to it twice to check both clients are notified later. final client2 = await BuildDaemonClient.connectUnchecked( p.join(tester.tempDirectory.path, 'root_pkg'), + logHandler: (event) => printOnFailure('(3) ${event.message}'), ); final results2 = StreamQueue(client2.buildResults); addTearDown(client2.close);