Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 140 additions & 4 deletions .github/workflows/dart.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions build_daemon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
11 changes: 11 additions & 0 deletions build_daemon/lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
Expand Down
28 changes: 14 additions & 14 deletions build_daemon/test/daemon_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,29 +41,29 @@ void main() {
}
});
test('can be stopped', () async {
final workspace = generateV4UUID();
final workspace = randomWorkspace();
testWorkspaces.add(workspace);
final daemon = Daemon(workspace);
await daemon.start(<String>{}, FakeDaemonBuilder(), FakeChangeProvider());
expect(daemon.onDone, completes);
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);
});
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,
Expand All @@ -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');
Expand All @@ -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');
Expand All @@ -100,20 +100,20 @@ 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);
expect(await _statusOf(daemon), 'RUNNING');
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);
Expand All @@ -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.
Expand All @@ -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,
Expand All @@ -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,
Expand Down
15 changes: 5 additions & 10 deletions build_daemon/test/uuid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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)}-'
Expand Down
4 changes: 4 additions & 0 deletions build_runner/mono_pkg.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions build_runner/test/integration_tests/daemon_command_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading