94894b1796
This CL adds three new commands to `dart`: ``` Global install Install a Dart CLI tool for global use. installed List globally installed Dart CLI tools. uninstall Remove a globally installed Dart CLI tool. ``` These commands are intended to replace `dart pub global` subcommands while adding support for `hook/build.dart` and building packages in AOT instead of running them with JIT. Internal design doc: http://go/dart-install-cli. Implementation details: * The source of truth is the state of the file system. These commands write and read directories, files, and symlinks. * App bundles and symlinks are placed in `DART_DATA_HOME` as per http://go/dart-data-home. * On Unix systems we use symlinks and on Windows batchfiles to place executables in the bin directory that point to an application bundle. (These OS differences have been encapsulated in a single class.) * On Windows, when an application is running, trying to re-install it will fail. Test coverage: * Installing from hosted, git, and local paths. * Installing a package with hooks. * Installing a package with hooks and user-defines. * Surfacing build hook failures during install. * Installing packages with conflicting executables names. This tests `--overwrite` flag behavior. * Installing a new or the same version, this should simply succeed. * A warning is shown if the bin directory is not on the `PATH`. * Running an installed app reports the correct exit code on exit. * Listing all installed versions, including the versions not on the`PATH`. * Uninstalling, which uninstalls all versions. * Re-installing while it is running. * Uninstalling while it is running. Out of scope for initial version: * Saving the SDK version (to display in `dart installed`). * Short-circuiting if re-installing an exactly installed version. Bug: https://github.com/dart-lang/sdk/issues/60889 Change-Id: I8f3a60d26e013957ce6fd7f52e564bcaaff30509 Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-arm64-try,pkg-win-release-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/441581 Reviewed-by: Sigurd Meldgaard <sigurdm@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Michael Goderbauer <goderbauer@google.com>
851 lines
25 KiB
Dart
851 lines
25 KiB
Dart
// Copyright (c) 2025, 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:meta/meta.dart';
|
|
import 'package:pub_formats/pub_formats.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import '../utils.dart';
|
|
import 'helpers.dart';
|
|
|
|
/// A package in the Dart SDK that we use for testing.
|
|
///
|
|
/// This package is on pub.dev, and on a Git repo, and on disk. So, this can be
|
|
/// used for testing hosted, git, and path installs.
|
|
///
|
|
/// Moreover it has an executables section in the pubspec.
|
|
const _packageForTest = 'vm_snapshot_analysis';
|
|
|
|
/// A valid version for [_packageForTest].
|
|
///
|
|
/// Not the newest version.
|
|
const _packageVersion = '0.7.5';
|
|
|
|
/// The name of an executable in [_packageForTest].
|
|
const _cliToolForTest = 'snapshot_analysis';
|
|
|
|
final _pathEnvVarSeparator = Platform.isWindows ? ';' : ':';
|
|
|
|
/// A package not in the Dart SDK repo. The Dart SDK repo takes too long to
|
|
/// clone.
|
|
const _gitPackageForTest = 'dart_app';
|
|
|
|
final _gitHttpsUrl = Uri.parse('https://dart.googlesource.com/native');
|
|
|
|
const _gitPath = 'pkgs/hooks_runner/test_data/dart_app/';
|
|
|
|
const _gitRef = '8ce789991cddb8864b0f3fa210c83d3d10e78316';
|
|
|
|
const String _dartDirectoryEnvKey = 'DART_DATA_HOME';
|
|
|
|
final _dartDevEntryScriptUri = resolveDartDevUri('bin/dartdev.dart');
|
|
|
|
final _sdkUri = resolveDartDevUri('.').resolve('../../');
|
|
|
|
final _packageRelativePath = Uri.directory('pkg/vm_snapshot_analysis/');
|
|
|
|
final _packageDir = Directory.fromUri(
|
|
_sdkUri.resolveUri(_packageRelativePath),
|
|
);
|
|
|
|
void main([List<String> args = const []]) async {
|
|
if (!nativeAssetsExperimentAvailableOnCurrentChannel) {
|
|
return;
|
|
}
|
|
|
|
final bool fromDartdevSource = args.contains('--source');
|
|
final errorExitCode = fromDartdevSource
|
|
? /* Dartdev doesn't exit the process, it sends a message to the VM.*/ 0
|
|
: 255;
|
|
final argsFiltered = args.where((e) => e != '--source').toList();
|
|
final testName = argsFiltered.isEmpty ? null : argsFiltered.join(' ');
|
|
|
|
@isTest
|
|
void skippableTest(
|
|
String description,
|
|
dynamic Function() body, {
|
|
Timeout? timeout,
|
|
}) {
|
|
test(
|
|
description,
|
|
skip: !(testName == null || description.contains(testName)),
|
|
timeout: timeout,
|
|
body,
|
|
);
|
|
}
|
|
|
|
final commandsHelpmessages = [
|
|
(
|
|
'install',
|
|
'''
|
|
Install or upgrade a Dart CLI tool for global use.
|
|
|
|
Install all executables specified in a package's pubspec.yaml executables
|
|
section (https://dart.dev/tools/pub/pubspec#executables) on the PATH. If the
|
|
executables section doesn't exist, installs all `bin/*.dart` entry points as
|
|
executables.
|
|
|
|
If the same package has been previously installed, it will be overwritten.
|
|
|
|
You can specify three different values for the <package> argument:
|
|
1. A package name. This will install the package from pub.dev. (hosted)
|
|
The [version-constraint] argument can only be passed to 'hosted'.
|
|
2. A git url. This will install the package from a git repository. (git)
|
|
3. A path on your machine. This will install the package from that path. (path)
|
|
|
|
Usage: dart install <package> [version-constraint]
|
|
-h, --help Print this usage information.
|
|
--git-path Path of git package in repository. Only applies when using a git url for <package>.
|
|
--git-ref Git branch or commit to be retrieved. Only applies when using a git url for <package>.
|
|
--overwrite Overwrite executables from other packages with the same name.
|
|
-u, --hosted-url A custom pub server URL for the package. Only applies when using a package name for <package>.
|
|
|
|
Run "dart help" to see global options.
|
|
'''
|
|
),
|
|
(
|
|
'installed',
|
|
'''
|
|
List globally installed Dart CLI tools.
|
|
|
|
Usage: dart installed [arguments]
|
|
-h, --help Print this usage information.
|
|
-a, --[no-]all Also list packages which are currently not active.
|
|
Active package have executables on `PATH`.
|
|
App bundles of packages on disk which have no executables
|
|
on `PATH` are non-active.
|
|
|
|
Run "dart help" to see global options.
|
|
'''
|
|
),
|
|
(
|
|
'uninstall',
|
|
'''
|
|
Remove a globally installed Dart CLI tool.
|
|
|
|
Completely deletes all installed versions of <package> and all executables from
|
|
<package> placed on PATH.
|
|
|
|
Usage: dart uninstall <package>
|
|
-h, --help Print this usage information.
|
|
|
|
Run "dart help" to see global options.
|
|
'''
|
|
),
|
|
];
|
|
for (final (command, helpMessage) in commandsHelpmessages) {
|
|
skippableTest('dart $command --help', timeout: longTimeout, () async {
|
|
final result = await _runDartdev(
|
|
fromDartdevSource,
|
|
command,
|
|
['--help'],
|
|
null,
|
|
{},
|
|
);
|
|
expect(result.stdout, contains(helpMessage));
|
|
});
|
|
}
|
|
|
|
final argumentss = [
|
|
(
|
|
null,
|
|
[_packageForTest],
|
|
),
|
|
(
|
|
null,
|
|
[_packageForTest, _packageVersion],
|
|
),
|
|
(
|
|
null,
|
|
[_packageForTest, _packageVersion, '--hosted-url', 'https://pub.dev/'],
|
|
),
|
|
(
|
|
null,
|
|
[_packageDir.path],
|
|
),
|
|
(
|
|
_sdkUri,
|
|
[_packageRelativePath.path],
|
|
),
|
|
(
|
|
_packageDir.uri,
|
|
['.'],
|
|
),
|
|
];
|
|
|
|
for (final (workingDirectory, arguments) in argumentss) {
|
|
var testName = arguments.join(' ');
|
|
if (workingDirectory != null) {
|
|
testName += ' in ${workingDirectory.toFilePath()}';
|
|
}
|
|
|
|
skippableTest('dart install $testName', timeout: longTimeout, () async {
|
|
await inTempDir((tempUri) async {
|
|
final binDir = Directory.fromUri(tempUri.resolve('install/bin'));
|
|
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
'PATH':
|
|
'${binDir.path}$_pathEnvVarSeparator${Platform.environment['PATH']!}',
|
|
};
|
|
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
arguments,
|
|
workingDirectory,
|
|
environment,
|
|
);
|
|
|
|
await _runToolForTest(environment);
|
|
|
|
final installedResult = await _runDartdev(
|
|
fromDartdevSource,
|
|
'installed',
|
|
[],
|
|
null,
|
|
environment,
|
|
);
|
|
final installedLines = installedResult.stdout.split('\n');
|
|
expect(installedLines.where((e) => e.isNotEmpty).length, equals(1));
|
|
final installedLine = installedLines.first;
|
|
expect(
|
|
installedLine,
|
|
startsWith(_packageForTest),
|
|
);
|
|
if (arguments.contains(_packageVersion)) {
|
|
expect(
|
|
installedLine,
|
|
equals('$_packageForTest $_packageVersion'),
|
|
);
|
|
}
|
|
if (arguments.contains(_packageRelativePath.toString())) {
|
|
expect(
|
|
installedLine,
|
|
stringContainsInOrder([_packageRelativePath.toString(), '" at 20']),
|
|
);
|
|
}
|
|
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'uninstall',
|
|
[_packageForTest],
|
|
null,
|
|
environment,
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
final argumentssGit = [
|
|
(
|
|
null,
|
|
[_gitHttpsUrl.toString(), '--git-path', _gitPath],
|
|
),
|
|
(
|
|
null,
|
|
[_gitHttpsUrl.toString(), '--git-path', _gitPath, '--git-ref', _gitRef],
|
|
),
|
|
];
|
|
|
|
for (final (workingDirectory, arguments) in argumentssGit) {
|
|
var testName = arguments.join(' ');
|
|
if (workingDirectory != null) {
|
|
testName += ' in ${workingDirectory.toFilePath()}';
|
|
}
|
|
|
|
skippableTest('dart install $testName', timeout: longTimeout, () async {
|
|
await inTempDir((tempUri) async {
|
|
final binDir = Directory.fromUri(tempUri.resolve('install/bin'));
|
|
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
'PATH':
|
|
'${binDir.path}$_pathEnvVarSeparator${Platform.environment['PATH']!}',
|
|
};
|
|
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
arguments,
|
|
workingDirectory,
|
|
environment,
|
|
);
|
|
|
|
final installedResult = await _runDartdev(
|
|
fromDartdevSource,
|
|
'installed',
|
|
[],
|
|
null,
|
|
environment,
|
|
);
|
|
final installedLines = installedResult.stdout.split('\n');
|
|
expect(installedLines.where((e) => e.isNotEmpty).length, equals(1));
|
|
final installedLine = installedLines.first;
|
|
expect(
|
|
installedLine,
|
|
startsWith(_gitPackageForTest),
|
|
);
|
|
expect(
|
|
installedLine,
|
|
contains(' from Git repository "${_gitHttpsUrl.toString()}"'),
|
|
);
|
|
if (arguments.contains('--git-ref')) {
|
|
expect(
|
|
installedLine,
|
|
contains(' at "${_gitRef.substring(0, 8)}"'),
|
|
);
|
|
}
|
|
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'uninstall',
|
|
[_gitPackageForTest],
|
|
null,
|
|
environment,
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
skippableTest('dart install ~/.dart/install/bin/ not on PATH', () async {
|
|
await inTempDir((tempUri) async {
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
};
|
|
|
|
await inTempDir((tempUri) async {
|
|
final installResult = await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[_packageForTest],
|
|
null,
|
|
environment,
|
|
);
|
|
if (Platform.isWindows) {
|
|
expect(
|
|
installResult.stdout,
|
|
stringContainsInOrder([
|
|
'Warning: Dart installs executables into ',
|
|
'which is not on your path.',
|
|
"You can fix that by adding that directory to your system's ",
|
|
'"Path" environment variable.',
|
|
'A web search for "configure windows path" will show you how.',
|
|
]),
|
|
);
|
|
} else {
|
|
expect(
|
|
installResult.stdout,
|
|
stringContainsInOrder([
|
|
'Warning: Dart installs executables into',
|
|
'You can fix that by adding this to your shell\'s config file ',
|
|
'export PATH="\$PATH":',
|
|
]),
|
|
);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
skippableTest('dart install dart_app (with build hooks and code assets)',
|
|
timeout: longTimeout, () async {
|
|
await inTempDir((tempUri) async {
|
|
final binDir = Directory.fromUri(tempUri.resolve('install/bin'));
|
|
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
'PATH':
|
|
'${binDir.path}$_pathEnvVarSeparator${Platform.environment['PATH']!}',
|
|
};
|
|
|
|
await nativeAssetsTest('dart_app', (dartAppUri) async {
|
|
// Add a second executable.
|
|
final entryPoint1 =
|
|
File.fromUri(dartAppUri.resolve('bin/dart_app.dart'));
|
|
final entryPoint2 =
|
|
File.fromUri(dartAppUri.resolve('bin/dart_app_copy.dart'));
|
|
final entryPoint1Contents = await entryPoint1.readAsString();
|
|
final entryPoint2Contents = entryPoint1Contents.replaceAll('5', '42');
|
|
await entryPoint2.writeAsString(entryPoint2Contents);
|
|
final pubspecFile = File.fromUri(dartAppUri.resolve('pubspec.yaml'));
|
|
final pubspecOld =
|
|
pubspecFile.readAsStringSync().replaceAll('\r\n', '\n');
|
|
final pubspecNew = pubspecOld.replaceAll(
|
|
'''executables:
|
|
dart_app:'''
|
|
.replaceAll('\r\n', '\n'),
|
|
'''executables:
|
|
dart_app:
|
|
dart_app_copy:'''
|
|
.replaceAll('\r\n', '\n'),
|
|
);
|
|
expect(pubspecNew, isNot(equals(pubspecOld)));
|
|
pubspecFile.writeAsStringSync(pubspecNew);
|
|
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[dartAppUri.toFilePath()],
|
|
null,
|
|
environment,
|
|
);
|
|
|
|
for (final (tool, someInt) in [
|
|
('dart_app', 5),
|
|
('dart_app_copy', 42)
|
|
]) {
|
|
final toolResult = await runProcess(
|
|
// Note this has `runInShell: true` under it to ensure PATHEXT is used on
|
|
// Windows so that invoking an executable without extension works.
|
|
executable: Uri.file(tool),
|
|
// Run in some unrelated directory ensuring PATH is picked up.
|
|
workingDirectory: Directory.systemTemp.uri,
|
|
logger: logger,
|
|
environment: environment,
|
|
);
|
|
expect(
|
|
toolResult.stdout,
|
|
stringContainsInOrder([
|
|
'add($someInt, 6) = ${someInt + 6}',
|
|
'subtract($someInt, 6) = ${someInt - 6}',
|
|
]),
|
|
);
|
|
expect(toolResult.exitCode, 0);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
skippableTest('dart install --overwrite', timeout: longTimeout, () async {
|
|
await inTempDir((tempUri) async {
|
|
final binDir = Directory.fromUri(tempUri.resolve('install/bin'));
|
|
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
'PATH':
|
|
'${binDir.path}$_pathEnvVarSeparator${Platform.environment['PATH']!}',
|
|
};
|
|
|
|
await nativeAssetsTest('dart_app', (dartAppUri) async {
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[dartAppUri.toFilePath()],
|
|
null,
|
|
environment,
|
|
);
|
|
|
|
// Not overwriting, but the same package is fine.
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[dartAppUri.toFilePath()],
|
|
null,
|
|
environment,
|
|
);
|
|
|
|
final pubspecFile = File.fromUri(dartAppUri.resolve('pubspec.yaml'));
|
|
final pubspecContents = await pubspecFile.readAsString();
|
|
final pubspecContentsNew =
|
|
pubspecContents.replaceFirst('dart_app', 'a_different_name');
|
|
await pubspecFile.writeAsString(pubspecContentsNew);
|
|
|
|
// Trying to install an executable with the same name from a different
|
|
// package should fail.
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[dartAppUri.toFilePath()],
|
|
null,
|
|
environment,
|
|
expectedExitCode: errorExitCode,
|
|
);
|
|
|
|
// Overwriting is fine.
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[dartAppUri.toFilePath(), '--overwrite'],
|
|
null,
|
|
environment,
|
|
);
|
|
|
|
// Using --overwrite leads to inactive versions.
|
|
// `dart installed --all` should also report the non-active versions.
|
|
for (final all in [true, false]) {
|
|
final installedResult = await _runDartdev(
|
|
fromDartdevSource,
|
|
'installed',
|
|
[if (all) '--all'],
|
|
null,
|
|
environment,
|
|
);
|
|
final installedLines = installedResult.stdout
|
|
.split('\n')
|
|
.where((e) => e.isNotEmpty)
|
|
.toList();
|
|
if (all) {
|
|
expect(installedLines, hasLength(2));
|
|
expect(
|
|
installedLines,
|
|
contains(startsWith('dart_app')),
|
|
);
|
|
} else {
|
|
expect(installedLines, hasLength(1));
|
|
expect(
|
|
installedLines,
|
|
isNot(contains(startsWith('dart_app'))),
|
|
);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
skippableTest('dart install check exit codes', timeout: longTimeout,
|
|
() async {
|
|
await inTempDir((tempUri) async {
|
|
final binDir = Directory.fromUri(tempUri.resolve('install/bin'));
|
|
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
'PATH':
|
|
'${binDir.path}$_pathEnvVarSeparator${Platform.environment['PATH']!}',
|
|
};
|
|
|
|
const appName = 'test_app';
|
|
final dartAppUri = tempUri.resolve('$appName/');
|
|
final pubspec = File.fromUri(dartAppUri.resolve('pubspec.yaml'));
|
|
await pubspec.create(recursive: true);
|
|
await pubspec.writeAsString(jsonEncode(PubspecYamlFileSyntax(
|
|
name: appName,
|
|
environment: EnvironmentSyntax(
|
|
sdk: '^${Platform.version.split(' ').first}',
|
|
),
|
|
executables: {
|
|
appName: appName,
|
|
},
|
|
).json));
|
|
final mainFile = File.fromUri(dartAppUri.resolve('bin/$appName.dart'));
|
|
await mainFile.create(recursive: true);
|
|
mainFile.writeAsString('''
|
|
import 'dart:io';
|
|
|
|
void main(List<String> args) {
|
|
exit(int.parse(args.first));
|
|
}
|
|
''');
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[dartAppUri.toFilePath()],
|
|
null,
|
|
environment,
|
|
);
|
|
|
|
const testExitCode = 55;
|
|
final toolResult = await runProcess(
|
|
// Note this has `runInShell: true` under it to ensure PATHEXT is used on
|
|
// Windows so that invoking an executable without extension works.
|
|
executable: Uri.file(appName),
|
|
// Run in some unrelated directory ensuring PATH is picked up.
|
|
workingDirectory: Directory.systemTemp.uri,
|
|
arguments: ['$testExitCode'],
|
|
logger: logger,
|
|
environment: environment,
|
|
expectedExitCode: testExitCode,
|
|
);
|
|
expect(toolResult.exitCode, testExitCode);
|
|
});
|
|
});
|
|
|
|
skippableTest('dart install hooks user-defines and failures',
|
|
timeout: longTimeout, () async {
|
|
await inTempDir((tempUri) async {
|
|
final binDir = Directory.fromUri(tempUri.resolve('install/bin'));
|
|
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
'PATH':
|
|
'${binDir.path}$_pathEnvVarSeparator${Platform.environment['PATH']!}',
|
|
};
|
|
|
|
const packageName = 'test_app';
|
|
final dartAppUri = tempUri.resolve('$packageName/');
|
|
final pubspec = File.fromUri(dartAppUri.resolve('pubspec.yaml'));
|
|
await pubspec.create(recursive: true);
|
|
final mainFile =
|
|
File.fromUri(dartAppUri.resolve('bin/$packageName.dart'));
|
|
await mainFile.create(recursive: true);
|
|
mainFile.writeAsString('''
|
|
void main(List<String> args) { }
|
|
''');
|
|
final buildHookFile = File.fromUri(dartAppUri.resolve('hook/build.dart'));
|
|
await buildHookFile.create(recursive: true);
|
|
buildHookFile.writeAsString('''
|
|
import 'package:hooks/hooks.dart';
|
|
|
|
void main(List<String> args) async {
|
|
await build(args, (input, output) async {
|
|
final myUserDefine = input.userDefines['my_user_define'];
|
|
if (myUserDefine == null) {
|
|
throw Exception('Expected a user define');
|
|
}
|
|
});
|
|
}
|
|
''');
|
|
for (final addUserDefine in [true, false]) {
|
|
await pubspec.writeAsString(jsonEncode(PubspecYamlFileSyntax(
|
|
name: packageName,
|
|
environment: EnvironmentSyntax(
|
|
sdk: '^${Platform.version.split(' ').first}',
|
|
),
|
|
executables: {
|
|
packageName: packageName,
|
|
},
|
|
dependencies: {
|
|
'hooks': PathDependencySourceSyntax(
|
|
path$: sdkRootUri
|
|
.resolve('third_party/pkg/native/pkgs/hooks/')
|
|
.toFilePath(),
|
|
),
|
|
},
|
|
hooks: HooksSyntax(
|
|
userDefines: {
|
|
packageName: {
|
|
if (addUserDefine) 'my_user_define': 'a_value,',
|
|
},
|
|
},
|
|
),
|
|
).json));
|
|
final installResult = await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[dartAppUri.toFilePath()],
|
|
null,
|
|
environment,
|
|
expectedExitCode: addUserDefine ? 0 : errorExitCode,
|
|
);
|
|
if (addUserDefine) {
|
|
expect(installResult.exitCode, equals(0));
|
|
expect(installResult.stderr, isEmpty);
|
|
} else {
|
|
// Check that build hook failures are surfaced and that error messages
|
|
// are visible.
|
|
expect(installResult.exitCode, equals(errorExitCode));
|
|
expect(installResult.stderr, contains('Expected a user define'));
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
skippableTest('dart install uninstalls old versions', timeout: longTimeout,
|
|
() async {
|
|
await inTempDir((tempUri) async {
|
|
final binDir = Directory.fromUri(tempUri.resolve('install/bin'));
|
|
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
'PATH':
|
|
'${binDir.path}$_pathEnvVarSeparator${Platform.environment['PATH']!}',
|
|
};
|
|
|
|
// Install two versions.
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
['.'],
|
|
_packageDir.uri,
|
|
environment,
|
|
);
|
|
final installResult = await _runDartdev(
|
|
fromDartdevSource,
|
|
'install',
|
|
[_packageForTest, _packageVersion],
|
|
null,
|
|
environment,
|
|
);
|
|
expect(
|
|
installResult.stdout,
|
|
stringContainsInOrder(['Uninstalling ', _packageForTest]),
|
|
);
|
|
|
|
// `--all` should also report the non-active versions.
|
|
Future<List<String>> runInstalled() async {
|
|
final installedResult = await _runDartdev(
|
|
fromDartdevSource,
|
|
'installed',
|
|
['--all'],
|
|
null,
|
|
environment,
|
|
);
|
|
final installedLines = installedResult.stdout
|
|
.split('\n')
|
|
.where((e) => e.isNotEmpty)
|
|
.toList();
|
|
return installedLines;
|
|
}
|
|
|
|
expect(await runInstalled(), hasLength(1));
|
|
|
|
// `uninstall` uninstalls all versions.
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'uninstall',
|
|
[_packageForTest],
|
|
null,
|
|
environment,
|
|
);
|
|
expect(await runInstalled(), hasLength(0));
|
|
});
|
|
});
|
|
|
|
skippableTest('dart uninstall', timeout: longTimeout, () async {
|
|
await inTempDir((tempUri) async {
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
};
|
|
|
|
// `uninstall` should have a non-zero exit if nothing was uninstalled.
|
|
await _runDartdev(
|
|
fromDartdevSource,
|
|
'uninstall',
|
|
[_packageForTest],
|
|
null,
|
|
environment,
|
|
expectedExitCode: errorExitCode,
|
|
);
|
|
});
|
|
});
|
|
|
|
skippableTest('dart uninstall while running', timeout: longTimeout, () async {
|
|
await inTempDir((tempUri) async {
|
|
final binDir = Directory.fromUri(tempUri.resolve('install/bin'));
|
|
|
|
final environment = {
|
|
_dartDirectoryEnvKey: tempUri.toFilePath(),
|
|
'PATH':
|
|
'${binDir.path}$_pathEnvVarSeparator${Platform.environment['PATH']!}',
|
|
};
|
|
|
|
const packageName = 'test_app';
|
|
final dartAppUri = tempUri.resolve('$packageName/');
|
|
final pubspec = File.fromUri(dartAppUri.resolve('pubspec.yaml'));
|
|
await pubspec.create(recursive: true);
|
|
await pubspec.writeAsString(jsonEncode(PubspecYamlFileSyntax(
|
|
name: packageName,
|
|
environment: EnvironmentSyntax(
|
|
sdk: '^${Platform.version.split(' ').first}',
|
|
),
|
|
executables: {
|
|
packageName: packageName,
|
|
},
|
|
).json));
|
|
final mainFile =
|
|
File.fromUri(dartAppUri.resolve('bin/$packageName.dart'));
|
|
await mainFile.create(recursive: true);
|
|
mainFile.writeAsString('''
|
|
void main(List<String> args) async {
|
|
await Future.delayed(Duration(days: 1000000));
|
|
}
|
|
''');
|
|
Future<RunProcessResult> doInstall(int expectedExitCode) async {
|
|
return await _runDartdev(fromDartdevSource, 'install',
|
|
[dartAppUri.toFilePath()], null, environment,
|
|
expectedExitCode: expectedExitCode);
|
|
}
|
|
|
|
await doInstall(0);
|
|
|
|
final runningProcess = await Process.start(
|
|
packageName,
|
|
[],
|
|
environment: environment,
|
|
runInShell: true,
|
|
);
|
|
|
|
final installWhileRunningResult = await doInstall(
|
|
Platform.isWindows ? errorExitCode : 0,
|
|
);
|
|
if (Platform.isWindows) {
|
|
expect(
|
|
installWhileRunningResult.stderr,
|
|
contains('The application might be in use.'),
|
|
);
|
|
} else {
|
|
expect(installWhileRunningResult.stderr, isEmpty);
|
|
}
|
|
|
|
final uninstallWhileRunningResult = await _runDartdev(
|
|
fromDartdevSource,
|
|
'uninstall',
|
|
[packageName],
|
|
null,
|
|
environment,
|
|
expectedExitCode: Platform.isWindows ? errorExitCode : 0,
|
|
);
|
|
if (Platform.isWindows) {
|
|
expect(
|
|
uninstallWhileRunningResult.stderr,
|
|
contains('The application might be in use.'),
|
|
);
|
|
} else {
|
|
expect(uninstallWhileRunningResult.stderr, isEmpty);
|
|
}
|
|
|
|
runningProcess.kill();
|
|
});
|
|
});
|
|
}
|
|
|
|
Future<RunProcessResult> _runDartdev(
|
|
bool fromDartdevSource,
|
|
String command,
|
|
List<String> arguments,
|
|
Uri? workingDirectory,
|
|
Map<String, String> environment, {
|
|
int expectedExitCode = 0,
|
|
}) async {
|
|
final installResult = await runDart(
|
|
arguments: [
|
|
if (fromDartdevSource) _dartDevEntryScriptUri.toFilePath(),
|
|
command,
|
|
...arguments,
|
|
],
|
|
workingDirectory: workingDirectory,
|
|
logger: logger,
|
|
environment: environment,
|
|
expectExitCodeZero: false,
|
|
);
|
|
expect(installResult.exitCode, equals(expectedExitCode));
|
|
return installResult;
|
|
}
|
|
|
|
/// Runs [_cliToolForTest] and expects the help message.
|
|
Future<RunProcessResult> _runToolForTest(
|
|
Map<String, String> environment,
|
|
) async {
|
|
final toolResult = await runProcess(
|
|
// Note this has `runInShell: true` under it to ensure PATHEXT is used on
|
|
// Windows so that invoking an executable without extension works.
|
|
executable: Uri.file(_cliToolForTest),
|
|
arguments: ['--help'],
|
|
// Run in some unrelated directory ensuring PATH is picked up.
|
|
workingDirectory: Directory.systemTemp.uri,
|
|
logger: logger,
|
|
environment: environment,
|
|
);
|
|
expect(
|
|
toolResult.stdout,
|
|
stringContainsInOrder([
|
|
'Tools for binary size analysis of Dart VM AOT snapshots.',
|
|
]),
|
|
);
|
|
expect(toolResult.exitCode, 0);
|
|
return toolResult;
|
|
}
|