Files
sdk/pkg/dartdev/test/utils.dart
T
Michael Richards 8d262e2944 [dartdev] This adds the ability for the Dart CLI to use the Resident Frontend Compiler for faster start times.
Dartdev will use the user's home directory to store the server information and will keep a directory for cached kernel files in each dart package's .dart_tool directory and in the .dart directory for stand alone dart programs.

This functionality is accessed by providing the --resident flag to the Dart CLI, and the server can be manually shutdown with the new shutdown command.

Change-Id: I5231a00b7535266ab0704ca3ae35c039738bd38b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/254341
Reviewed-by: Jake Macdonald <jakemac@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Michael Richards <msrichards@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
2022-08-18 17:01:34 +00:00

212 lines
5.6 KiB
Dart

// Copyright (c) 2020, 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:cli_util/cli_logging.dart';
import 'package:dartdev/src/core.dart';
import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart';
import 'package:test/test.dart';
/// A long [Timeout] is provided for tests that start a process on
/// `bin/dartdev.dart` as the command is not compiled ahead of time, and each
/// invocation requires the VM to compile the entire dependency graph.
const Timeout longTimeout = Timeout(Duration(minutes: 5));
/// This version of dart is the last guaranteed pre-null safety language
/// version:
const String dartVersionFilePrefix2_9 = '// @dart = 2.9\n';
void initGlobalState() {
log = Logger.standard();
}
TestProject project(
{String? mainSrc,
String? analysisOptions,
bool logAnalytics = false,
String name = TestProject._defaultProjectName,
VersionConstraint? sdkConstraint,
Map<String, dynamic>? pubspec}) =>
TestProject(
mainSrc: mainSrc,
analysisOptions: analysisOptions,
logAnalytics: logAnalytics,
sdkConstraint: sdkConstraint,
pubspec: pubspec);
class TestProject {
static const String _defaultProjectName = 'dartdev_temp';
late Directory dir;
String get dirPath => dir.path;
String get pubCachePath => path.join(dirPath, 'pub_cache');
String get pubCacheBinPath => path.join(pubCachePath, 'bin');
String get mainPath => path.join(dirPath, relativeFilePath);
final String name;
String get relativeFilePath => 'lib/main.dart';
final bool logAnalytics;
final VersionConstraint? sdkConstraint;
final Map<String, dynamic>? pubspec;
Process? _process;
TestProject(
{String? mainSrc,
String? analysisOptions,
this.name = _defaultProjectName,
this.logAnalytics = false,
this.sdkConstraint,
this.pubspec}) {
initGlobalState();
dir = Directory.systemTemp.createTempSync('a');
file(
'pubspec.yaml',
pubspec == null
? '''
name: $name
environment:
sdk: '${sdkConstraint ?? '>=2.10.0 <3.0.0'}'
dev_dependencies:
test: any
'''
: json.encode(pubspec));
if (analysisOptions != null) {
file('analysis_options.yaml', analysisOptions);
}
if (mainSrc != null) {
file(relativeFilePath, mainSrc);
}
}
void file(String name, String contents) {
var file = File(path.join(dir.path, name));
file.parent.createSync();
file.writeAsStringSync(contents);
}
void deleteFile(String name) {
var file = File(path.join(dir.path, name));
assert(file.existsSync());
file.deleteSync();
}
Future<void> dispose() async {
_process?.kill();
await _process?.exitCode;
_process = null;
await deleteDirectory(dir);
}
Future<ProcessResult> run(
List<String> arguments, {
String? workingDir,
}) async {
final process = await Process.start(
Platform.resolvedExecutable,
[
'--no-analytics',
...arguments,
],
workingDirectory: workingDir ?? dir.path,
environment: {
if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true',
'PUB_CACHE': pubCachePath,
});
_process = process;
final stdoutContents = process.stdout.transform(utf8.decoder).join();
final stderrContents = process.stderr.transform(utf8.decoder).join();
final code = await process.exitCode;
return ProcessResult(
process.pid,
code,
await stdoutContents,
await stderrContents,
);
}
Future<Process> start(
List<String> arguments, {
String? workingDir,
}) {
return Process.start(
Platform.resolvedExecutable,
[
'--no-analytics',
...arguments,
],
workingDirectory: workingDir ?? dir.path,
environment: {
if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true',
'PUB_CACHE': pubCachePath,
})
..then((p) => _process = p);
}
String? _sdkRootPath;
/// Return the root of the SDK.
String get sdkRootPath {
if (_sdkRootPath == null) {
// Assumes the script importing this one is somewhere under the SDK.
String current = path.canonicalize(Platform.script.toFilePath());
do {
String tryDir = path.dirname(current);
if (File(path.join(tryDir, 'pkg', 'dartdev', 'bin', 'dartdev.dart'))
.existsSync()) {
_sdkRootPath = tryDir;
return _sdkRootPath!;
}
current = tryDir;
} while (path.dirname(current) != current);
throw StateError('can not find SDK repository root');
}
return _sdkRootPath!;
}
String get absolutePathToDartdevFile =>
path.join(sdkRootPath, 'pkg', 'dartdev', 'bin', 'dartdev.dart');
Directory? findDirectory(String name) {
var directory = Directory(path.join(dir.path, name));
return directory.existsSync() ? directory : null;
}
File? findFile(String name) {
var file = File(path.join(dir.path, name));
return file.existsSync() ? file : null;
}
}
Future<void> deleteDirectory(Directory dir) async {
int deleteAttempts = 5;
while (deleteAttempts >= 0) {
deleteAttempts--;
try {
if (!dir.existsSync()) {
return;
}
dir.deleteSync(recursive: true);
} catch (e) {
if (deleteAttempts <= 0) {
rethrow;
}
await Future.delayed(Duration(milliseconds: 500));
log.stdout('Got $e while deleting $dir. Trying again...');
}
}
}