Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(84)

Unified Diff: tools/migration/bin/migrate_batch.dart

Issue 2987533002: Add simple test migration helper. (Closed)
Patch Set: Merge branch 'master' into migration-tool Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/migration/analysis_options.yaml ('k') | tools/migration/lib/src/log.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/migration/bin/migrate_batch.dart
diff --git a/tools/migration/bin/migrate_batch.dart b/tools/migration/bin/migrate_batch.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b0ae2b1f2722a5cf9940da0314a11dc39df77ed4
--- /dev/null
+++ b/tools/migration/bin/migrate_batch.dart
@@ -0,0 +1,290 @@
+// Copyright (c) 2017, 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.
+
+/// Given the beginning and ending file names in a batch, does as much automated
+/// migration and possible and prints out the remaining manual steps required.
+///
+/// This should be safe to run, and safe to re-run on an in-progress chunk.
+/// However, it has not been thoroughly tested, so run at your own risk.
+
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+
+import 'package:migration/src/log.dart';
+
+const simpleDirs = const ["corelib", "language", "lib"];
+
+final String sdkRoot =
+ p.normalize(p.join(p.dirname(p.fromUri(Platform.script)), '../../../'));
+
+final String testRoot = p.join(sdkRoot, "tests");
+
+bool dryRun = false;
+
+void main(List<String> arguments) {
+ if (arguments.contains("--dry-run")) {
+ dryRun = true;
+ arguments = arguments.where((argument) => argument != "--dry-run").toList();
+ }
+
+ if (arguments.length != 2) {
+ stderr.writeln(
+ "Usage: dart migrate_batch.dart [--dry-run] <first file> <last file>");
+ stderr.writeln();
+ stderr.writeln("Example:");
+ stderr.writeln();
+ stderr.writeln(
+ " \$ dart migrate_batch.dart corelib/map_to_string corelib/queue");
+ exit(1);
+ }
+
+ var first = toTwoPath(arguments[0]);
+ var last = toTwoPath(arguments[1]);
+
+ var tests = scanTests();
+
+ // Find the range of files in the chunk. We use comparisons here instead of
+ // equality to try to compensate for files that may only appear in one fork
+ // and should be part of the chunk but aren't officially listed as the begin
+ // or end point.
+ var startIndex = -1;
+ var endIndex = 0;
+ for (var i = 0; i < tests.length; i++) {
+ if (startIndex == -1 && tests[i].twoPath.compareTo(first) >= 0) {
+ startIndex = i;
+ }
+
+ if (tests[i].twoPath.compareTo(last) > 0) {
+ endIndex = i;
+ break;
+ }
+ }
+
+ print("Migrating ${bold(endIndex - startIndex)} tests from ${bold(first)} "
+ "to ${bold(last)}...");
+ print("");
+
+ var todos = <String>[];
+ var migratedTests = 0;
+ var unmigratedTests = 0;
+ for (var i = startIndex; i < endIndex; i++) {
+ if (tests[i].migrate(todos)) {
+ migratedTests++;
+ } else {
+ unmigratedTests++;
+ }
+ }
+
+ print("");
+
+ var summary = "";
+
+ if (migratedTests > 0) {
+ var s = migratedTests == 1 ? "" : "s";
+ summary += "Successfully migrated ${green(migratedTests)} test$s. ";
+ }
+
+ if (unmigratedTests > 0) {
+ var s = migratedTests == 1 ? "" : "s";
+ summary += "Need manual work on ${red(unmigratedTests)} test$s:";
+ }
+
+ print(summary);
+ todos.forEach(todo);
+}
+
+String toTwoPath(String path) {
+ // Allow eliding "_test" and/or ".dart" to make things more command-line
+ // friendly.
+ if (!path.endsWith("_test.dart")) path += "_test.dart";
+ if (!path.endsWith(".dart")) path += ".dart";
+
+ for (var dir in simpleDirs) {
+ if (p.isWithin(dir, path)) {
+ return p.join("${dir}_2", p.relative(path, from: dir));
+ }
+
+ if (p.isWithin("${dir}_strong", path)) {
+ return p.join("${dir}_2", p.relative(path, from: dir));
+ }
+ }
+
+ if (p.isWithin("html", path)) {
+ return p.join("lib_2/html", p.relative(path, from: "html"));
+ }
+
+ if (p.isWithin("isolate", path)) {
+ return p.join("lib_2/isolate", p.relative(path, from: "isolate"));
+ }
+
+ // Guess it's already a two path.
+ return path;
+}
+
+/// Loads all of the unforked test files.
+///
+/// Creates an list of [Fork]s, ordered by their destination paths. Handles
+/// tests that only appear in one fork or the other, or both.
+List<Fork> scanTests() {
+ var tests = <String, Fork>{};
+
+ addTestDirectory(String fromDir, String twoDir) {
+ for (var entry
+ in new Directory(p.join(testRoot, fromDir)).listSync(recursive: true)) {
+ if (!entry.path.endsWith("_test.dart")) continue;
+
+ var fromPath = p.relative(entry.path, from: testRoot);
+ var twoPath = p.join(twoDir, p.relative(fromPath, from: fromDir));
+
+ var fork = tests.putIfAbsent(twoPath, () => new Fork(twoPath));
+ if (fromDir.contains("_strong")) {
+ fork.strongPath = fromPath;
+ } else {
+ fork.onePath = fromPath;
+ }
+ }
+ }
+
+ addTestDirectory("corelib", "corelib_2");
+ addTestDirectory("corelib_strong", "corelib_2");
+ addTestDirectory("html", "lib_2/html");
+ addTestDirectory("isolate", "lib_2/isolate");
+ addTestDirectory("language", "language_2");
+ addTestDirectory("language_strong", "language_2");
+ addTestDirectory("lib", "lib_2");
+ addTestDirectory("lib_strong", "lib_2");
+
+ var sorted = tests.values.toList();
+ sorted.sort((a, b) => a.twoPath.compareTo(b.twoPath));
+ return sorted;
+}
+
+/// Moves the file from [from] to [to], which are both assumed to be relative
+/// paths inside "tests".
+void moveFile(String from, String to) {
+ if (dryRun) {
+ print(" Dry run: move $from to $to");
+ return;
+ }
+
+ new File(p.join(testRoot, from)).renameSync(p.join(testRoot, to));
+}
+
+/// Reads the contents of the file at [path], which is assumed to be relative
+/// within "tests".
+String readFile(String path) {
+ return new File(p.join(testRoot, path)).readAsStringSync();
+}
+
+/// Deletes the file at [path], which is assumed to be relative within "tests".
+void deleteFile(String path) {
+ if (dryRun) {
+ print(" Dry run: delete $path");
+ return;
+ }
+
+ new File(p.join(testRoot, path)).deleteSync();
+}
+
+bool checkForUnitTest(String path, String source) {
+ if (!source.contains("package:unittest")) return false;
+
+ note("${bold(path)} uses unittest package.");
+ return true;
+}
+
+class Fork {
+ final String twoPath;
+ String onePath;
+ String strongPath;
+
+ String get twoSource {
+ if (twoPath == null) return null;
+ if (_twoSource == null) _twoSource = readFile(twoPath);
+ return _twoSource;
+ }
+
+ String _twoSource;
+
+ String get oneSource {
+ if (onePath == null) return null;
+ if (_oneSource == null) _oneSource = readFile(onePath);
+ return _oneSource;
+ }
+
+ String _oneSource;
+
+ String get strongSource {
+ if (strongPath == null) return null;
+ if (_strongSource == null) _strongSource = readFile(strongPath);
+ return _strongSource;
+ }
+
+ String _strongSource;
+
+ Fork(this.twoPath);
+
+ bool migrate(List<String> todos) {
+ print("- ${bold(twoPath)}:");
+
+ var todosBefore = todos.length;
+ var isMigrated = new File(p.join(testRoot, twoPath)).existsSync();
+
+ // If there is a migrated version and it's the same as an unmigrated one,
+ // delete the unmigrated one.
+ if (isMigrated) {
+ if (onePath != null) {
+ if (oneSource == twoSource) {
+ deleteFile(onePath);
+ done("Deleted already-migrated $onePath.");
+ } else {
+ note("${bold(onePath)} does not match already-migrated "
+ "${bold(twoPath)}.");
+ todos.add("Merge ${bold(onePath)} into ${bold(twoPath)}.");
+ checkForUnitTest(onePath, oneSource);
+ }
+ }
+
+ if (strongPath != null) {
+ if (strongSource == twoSource) {
+ deleteFile(strongPath);
+ done("Deleted already-migrated ${bold(strongPath)}.");
+ } else {
+ note("${bold(strongPath)} does not match already-migrated "
+ "${bold(twoPath)}.");
+ todos.add("Merge ${bold(strongPath)} into ${bold(twoPath)}.");
+ checkForUnitTest(strongPath, strongSource);
+ }
+ }
+ } else {
+ // If it only exists in one place, just move it.
+ if (strongPath == null) {
+ moveFile(onePath, twoPath);
+ done("Moved from ${bold(onePath)} (no strong mode fork).");
+ } else if (onePath == null) {
+ moveFile(strongPath, twoPath);
+ done("Moved from ${bold(strongPath)} (no 1.0 mode fork).");
+ } else if (oneSource == strongSource) {
+ // The forks are identical, pick one.
+ moveFile(onePath, twoPath);
+ deleteFile(strongPath);
+ done("Merged identical forks.");
+ checkForUnitTest(twoPath, oneSource);
+ } else {
+ // Otherwise, a manual merge is required. Start with the strong one.
+ moveFile(strongPath, twoPath);
+ done("Moved strong fork, kept 1.0 fork, manual merge required.");
+ todos.add("Merge ${bold(onePath)} into ${bold(twoPath)}.");
+ checkForUnitTest(onePath, oneSource);
+ }
+ }
+
+ if (checkForUnitTest(twoPath, twoSource)) {
+ todos.add("Migrate ${bold(twoPath)} off unittest.");
+ }
+
+ return todos.length == todosBefore;
+ }
+}
« no previous file with comments | « tools/migration/analysis_options.yaml ('k') | tools/migration/lib/src/log.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698