blob: f159c9bf25acf7b8d72b62ee2620519f51b68a6f [file] [log] [blame]
// 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;
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));
print('Got $e while deleting $dir. Trying again...');
}
}
}
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;
}
}