blob: 1edcefaa902c378e965d85233082273759fb4e92 [file] [log] [blame]
// Copyright (c) 2019, 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.
/// This executable provides the ability to run the migration tool in process
/// on a single package. It should be invoked with two command-line arguments:
/// a path to a configuration file and the name of a package to migrate.
///
/// The configuration file format is a JSON map, with the following keys:
/// - `sdk_root`: path to the SDK source code on the user's machine (this is the
/// directory that contains `pkg`, `third_party`, `tests`, etc.
/// - `output_root`: if present, path to the directory on the user's machine
/// where output HTML files should go. A subdirectory will be created for
/// each package that is migrated.
/// - `external_packages`: a map (name => path) of additional non-SDK packages
/// that may need to be migrated.
/// - `port`: if present, the port where a server should be spawned serving HTML
/// pages.
library migration_runner;
import 'dart:convert';
import 'dart:io' as io;
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
import 'package:analysis_server/src/utilities/mocks.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:cli_util/cli_util.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
Future<void> main(List<String> args) async {
if (args.length != 2) {
throw StateError(
'Exactly two arguments are required: the path to a JSON configuration '
'file, and the name of the package to migrate');
}
var testInfoJsonPath = args[0];
var testInfoJson = json.decode(io.File(testInfoJsonPath).readAsStringSync());
var packageName = args[1];
var testInfo = TestInfo(testInfoJson);
var packageRoot = testInfo.packageRoot(packageName);
var port = testInfo.port;
print('Preparing to migrate');
var migrationTest = MigrationTest();
migrationTest.setUp();
print('Migrating');
await migrationTest.run(packageRoot, port);
if (port == null) {
print('Done');
io.exit(0);
} else {
print('Done. Please point your browser to localhost:$port/\$filePath');
}
}
class MigrationBase {
ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE;
MockServerChannel serverChannel;
AnalysisServer server;
AnalysisServer createAnalysisServer() {
//
// Create server
//
var options = AnalysisServerOptions();
var sdkPath = getSdkPath();
return AnalysisServer(
serverChannel,
resourceProvider,
options,
DartSdkManager(sdkPath),
CrashReportingAttachmentsBuilder.empty,
InstrumentationService.NULL_SERVICE);
}
void processNotification(Notification notification) {
if (notification.event == SERVER_NOTIFICATION_ERROR) {
fail('${notification.toJson()}');
}
}
Future<Response> sendAnalysisSetAnalysisRoots(List<String> directories) {
var request =
AnalysisSetAnalysisRootsParams(directories, []).toRequest('0');
return waitResponse(request);
}
Future<Response> sendEditDartfix(List<String> directories, int port) {
var request = EditDartfixParams(directories,
includedFixes: ['non-nullable'], port: port)
.toRequest('1');
return waitResponse(request);
}
void setUp() {
serverChannel = MockServerChannel();
server = createAnalysisServer();
server.pluginManager = TestPluginManager();
// listen for notifications
var notificationStream = serverChannel.notificationController.stream;
notificationStream.listen((Notification notification) {
processNotification(notification);
});
}
void tearDown() {
server.done();
server = null;
serverChannel = null;
}
/// Returns a [Future] that completes when the server's analysis is complete.
Future waitForTasksFinished() {
return server.onAnalysisComplete;
}
/// Completes with a successful [Response] for the given [request].
Future<Response> waitResponse(Request request,
{bool throwOnError = true}) async {
return serverChannel.sendRequest(request, throwOnError: throwOnError);
}
}
class MigrationTest extends MigrationBase {
Future<void> run(String packageRoot, int port) async {
var packageRoots = <String>[packageRoot];
await sendAnalysisSetAnalysisRoots(packageRoots);
await sendEditDartfix(packageRoots, port);
}
}
class TestInfo {
static const Set<String> thirdPartyPackages = {
'charcode',
'collection',
'logging',
'meta',
'pedantic',
'typed_data'
};
static const Set<String> builtInPackages = {'meta', 'path'};
final Map<String, Object> testInfoJson;
TestInfo(this.testInfoJson);
Map<String, String> get externalPackages =>
((testInfoJson['external_packages'] ?? {}) as Map).cast<String, String>();
String get outputRoot => testInfoJson['output_root'];
int get port => testInfoJson['port'];
String get sdkRoot => testInfoJson['sdk_root'];
String packageRoot(String packageName) {
if (thirdPartyPackages.contains(packageName)) {
return path.join(sdkRoot, 'third_party', 'pkg', packageName);
} else if (builtInPackages.contains(packageName)) {
return path.join(sdkRoot, 'pkg', packageName);
} else if (externalPackages.containsKey(packageName)) {
return externalPackages[packageName];
} else {
throw StateError('Unrecognized package $packageName');
}
}
}