Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /// Given the beginning and ending file names in a batch, does as much automated | 5 /// Given the beginning and ending file names in a batch, does as much automated |
| 6 /// migration and possible and prints out the remaining manual steps required. | 6 /// migration and possible and prints out the remaining manual steps required. |
| 7 /// | 7 /// |
| 8 /// This should be safe to run, and safe to re-run on an in-progress chunk. | 8 /// This should be safe to run, and safe to re-run on an in-progress chunk. |
| 9 /// However, it has not been thoroughly tested, so run at your own risk. | 9 /// However, it has not been thoroughly tested, so run at your own risk. |
| 10 | 10 |
| 11 import 'dart:io'; | 11 import 'dart:io'; |
| 12 | 12 |
| 13 import 'package:path/path.dart' as p; | 13 import 'package:path/path.dart' as p; |
| 14 | 14 |
| 15 import 'package:migration/src/log.dart'; | 15 import 'package:migration/src/log.dart'; |
| 16 import 'package:migration/src/status_file.dart'; | |
| 16 | 17 |
| 17 const simpleDirs = const ["corelib", "language", "lib"]; | 18 const simpleDirs = const ["corelib", "language", "lib"]; |
| 18 | 19 |
| 19 final String sdkRoot = | 20 final String sdkRoot = |
| 20 p.normalize(p.join(p.dirname(p.fromUri(Platform.script)), '../../../')); | 21 p.normalize(p.join(p.dirname(p.fromUri(Platform.script)), '../../../')); |
| 21 | 22 |
| 22 final String testRoot = p.join(sdkRoot, "tests"); | 23 final String testRoot = p.join(sdkRoot, "tests"); |
| 23 | 24 |
| 24 bool dryRun = false; | 25 bool dryRun = false; |
| 25 | 26 |
| 26 void main(List<String> arguments) { | 27 void main(List<String> arguments) { |
| 27 if (arguments.contains("--dry-run")) { | 28 if (arguments.contains("--dry-run")) { |
| 28 dryRun = true; | 29 dryRun = true; |
| 29 arguments = arguments.where((argument) => argument != "--dry-run").toList(); | 30 arguments = arguments.where((argument) => argument != "--dry-run").toList(); |
| 30 } | 31 } |
| 31 | 32 |
| 32 if (arguments.length != 2) { | 33 if (arguments.length != 2) { |
| 33 stderr.writeln( | 34 stderr.writeln( |
| 34 "Usage: dart migrate_batch.dart [--dry-run] <first file> <last file>"); | 35 "Usage: dart migrate_batch.dart [--dry-run] <first file> <last file>"); |
| 35 stderr.writeln(); | 36 stderr.writeln(); |
| 36 stderr.writeln("Example:"); | 37 stderr.writeln("Example:"); |
| 37 stderr.writeln(); | 38 stderr.writeln(); |
| 38 stderr.writeln( | 39 stderr.writeln( |
| 39 " \$ dart migrate_batch.dart corelib/map_to_string corelib/queue"); | 40 " \$ dart migrate_batch.dart corelib/map_to_string corelib/queue"); |
| 40 exit(1); | 41 exit(1); |
| 41 } | 42 } |
| 42 | 43 |
| 43 var first = toTwoPath(arguments[0]); | 44 var first = arguments[0]; |
| 44 var last = toTwoPath(arguments[1]); | 45 var last = arguments[1]; |
| 46 | |
| 47 print("Preparing to migrate tests from ${bold(first)} to ${bold(last)}..."); | |
|
Bob Nystrom
2017/07/24 21:47:47
This is because the scan process is a lot longer w
bkonyi
2017/07/25 22:13:39
Oh yeah, this is here because I'm really not doing
| |
| 48 | |
| 49 first = toTwoPath(first); | |
| 50 last = toTwoPath(last); | |
| 45 | 51 |
| 46 var tests = scanTests(); | 52 var tests = scanTests(); |
| 47 | 53 |
| 48 // Find the range of files in the chunk. We use comparisons here instead of | 54 // Find the range of files in the chunk. We use comparisons here instead of |
| 49 // equality to try to compensate for files that may only appear in one fork | 55 // equality to try to compensate for files that may only appear in one fork |
| 50 // and should be part of the chunk but aren't officially listed as the begin | 56 // and should be part of the chunk but aren't officially listed as the begin |
| 51 // or end point. | 57 // or end point. |
| 52 var startIndex = -1; | 58 var startIndex = -1; |
| 53 var endIndex = 0; | 59 var endIndex = 0; |
| 54 for (var i = 0; i < tests.length; i++) { | 60 for (var i = 0; i < tests.length; i++) { |
| 55 if (startIndex == -1 && tests[i].twoPath.compareTo(first) >= 0) { | 61 if (startIndex == -1 && tests[i].twoPath.compareTo(first) >= 0) { |
| 56 startIndex = i; | 62 startIndex = i; |
| 57 } | 63 } |
| 58 | 64 |
| 59 if (tests[i].twoPath.compareTo(last) > 0) { | 65 if (tests[i].twoPath.compareTo(last) > 0) { |
| 60 endIndex = i; | 66 endIndex = i; |
| 61 break; | 67 break; |
| 62 } | 68 } |
| 63 } | 69 } |
| 64 | 70 |
| 71 if ((endIndex - startIndex) == 0) { | |
| 72 print(bold("No tests to migrate.")); | |
| 73 return; | |
| 74 } | |
| 75 | |
| 65 print("Migrating ${bold(endIndex - startIndex)} tests from ${bold(first)} " | 76 print("Migrating ${bold(endIndex - startIndex)} tests from ${bold(first)} " |
| 66 "to ${bold(last)}..."); | 77 "to ${bold(last)}..."); |
| 67 print(""); | 78 print(""); |
| 68 | 79 |
| 69 var todos = <String>[]; | 80 var todos = <String>[]; |
| 70 var migratedTests = 0; | 81 var migratedTests = 0; |
| 71 var unmigratedTests = 0; | 82 var unmigratedTests = 0; |
| 72 for (var i = startIndex; i < endIndex; i++) { | 83 for (var i = startIndex; i < endIndex; i++) { |
| 73 if (tests[i].migrate(todos)) { | 84 if (tests[i].migrate(todos)) { |
| 74 migratedTests++; | 85 migratedTests++; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 86 summary += "Successfully migrated ${green(migratedTests)} test$s. "; | 97 summary += "Successfully migrated ${green(migratedTests)} test$s. "; |
| 87 } | 98 } |
| 88 | 99 |
| 89 if (unmigratedTests > 0) { | 100 if (unmigratedTests > 0) { |
| 90 var s = migratedTests == 1 ? "" : "s"; | 101 var s = migratedTests == 1 ? "" : "s"; |
| 91 summary += "Need manual work on ${red(unmigratedTests)} test$s:"; | 102 summary += "Need manual work on ${red(unmigratedTests)} test$s:"; |
| 92 } | 103 } |
| 93 | 104 |
| 94 print(summary); | 105 print(summary); |
| 95 todos.forEach(todo); | 106 todos.forEach(todo); |
| 107 | |
| 108 // Print status file entries. | |
| 109 var statusFileEntries = ""; | |
| 110 for (var i = startIndex; i < endIndex; i++) { | |
| 111 statusFileEntries += printStatusFileEntries(tests[i]); | |
|
Bob Nystrom
2017/07/24 21:47:47
Instead of repeatedly concatenating strings, it's
bkonyi
2017/07/25 22:13:39
Right, that makes sense. Done.
| |
| 112 } | |
| 113 | |
| 114 new File("statuses.migration").writeAsStringSync(statusFileEntries); | |
| 115 print( | |
| 116 bold("Wrote relevant test status file entries to 'statuses.migration'")); | |
| 117 } | |
| 118 | |
| 119 String printStatusFileEntries(Fork test) { | |
| 120 var statusFileEntries = ""; | |
| 121 if (test.onePath != null && !test.oneStatusFile.isEmpty) { | |
| 122 statusFileEntries += | |
| 123 bold("Status file entries for: ${test.onePath}") + "\n"; | |
| 124 for (var section in test.oneStatusFile.sections) { | |
| 125 statusFileEntries += section.toString(); | |
| 126 } | |
| 127 } | |
| 128 if (test.strongPath != null && !test.strongStatusFile.isEmpty) { | |
| 129 if (statusFileEntries != "") { | |
| 130 statusFileEntries += "\n"; | |
| 131 } | |
| 132 statusFileEntries += | |
| 133 bold("Status file entries for: ${test.strongPath}") + "\n"; | |
| 134 for (var section in test.strongStatusFile.sections) { | |
| 135 statusFileEntries += section.toString(); | |
| 136 } | |
| 137 } | |
| 138 return statusFileEntries; | |
| 96 } | 139 } |
| 97 | 140 |
| 98 String toTwoPath(String path) { | 141 String toTwoPath(String path) { |
| 99 // Allow eliding "_test" and/or ".dart" to make things more command-line | 142 // Allow eliding "_test" and/or ".dart" to make things more command-line |
| 100 // friendly. | 143 // friendly. |
| 101 if (!path.endsWith("_test.dart")) path += "_test.dart"; | 144 if (!path.endsWith("_test.dart")) path += "_test.dart"; |
| 102 if (!path.endsWith(".dart")) path += ".dart"; | 145 if (!path.endsWith(".dart")) path += ".dart"; |
| 103 | 146 |
| 104 for (var dir in simpleDirs) { | 147 for (var dir in simpleDirs) { |
| 105 if (p.isWithin(dir, path)) { | 148 if (p.isWithin(dir, path)) { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 129 /// tests that only appear in one fork or the other, or both. | 172 /// tests that only appear in one fork or the other, or both. |
| 130 List<Fork> scanTests() { | 173 List<Fork> scanTests() { |
| 131 var tests = <String, Fork>{}; | 174 var tests = <String, Fork>{}; |
| 132 | 175 |
| 133 addTestDirectory(String fromDir, String twoDir) { | 176 addTestDirectory(String fromDir, String twoDir) { |
| 134 for (var entry | 177 for (var entry |
| 135 in new Directory(p.join(testRoot, fromDir)).listSync(recursive: true)) { | 178 in new Directory(p.join(testRoot, fromDir)).listSync(recursive: true)) { |
| 136 if (!entry.path.endsWith("_test.dart")) continue; | 179 if (!entry.path.endsWith("_test.dart")) continue; |
| 137 | 180 |
| 138 var fromPath = p.relative(entry.path, from: testRoot); | 181 var fromPath = p.relative(entry.path, from: testRoot); |
| 182 var fromStatus = testRoot + "/$fromDir/${fromDir}.status"; | |
| 183 var testName = entry.path.split("/").last.split(".")[0]; | |
| 184 var statusFile = getStatusFileEntries(fromStatus, testName); | |
|
Bob Nystrom
2017/07/24 21:47:47
This call does two things:
1. It reads the status
bkonyi
2017/07/25 22:13:39
Done.
| |
| 139 var twoPath = p.join(twoDir, p.relative(fromPath, from: fromDir)); | 185 var twoPath = p.join(twoDir, p.relative(fromPath, from: fromDir)); |
| 140 | |
| 141 var fork = tests.putIfAbsent(twoPath, () => new Fork(twoPath)); | 186 var fork = tests.putIfAbsent(twoPath, () => new Fork(twoPath)); |
| 142 if (fromDir.contains("_strong")) { | 187 if (fromDir.contains("_strong")) { |
| 143 fork.strongPath = fromPath; | 188 fork.strongPath = fromPath; |
| 189 fork.strongStatusFile = statusFile; | |
| 144 } else { | 190 } else { |
| 145 fork.onePath = fromPath; | 191 fork.onePath = fromPath; |
| 192 fork.oneStatusFile = statusFile; | |
| 146 } | 193 } |
| 147 } | 194 } |
| 148 } | 195 } |
| 149 | 196 |
| 150 addTestDirectory("corelib", "corelib_2"); | 197 addTestDirectory("corelib", "corelib_2"); |
| 151 addTestDirectory("corelib_strong", "corelib_2"); | 198 addTestDirectory("corelib_strong", "corelib_2"); |
| 152 addTestDirectory("html", "lib_2/html"); | 199 addTestDirectory("html", "lib_2/html"); |
| 153 addTestDirectory("isolate", "lib_2/isolate"); | 200 addTestDirectory("isolate", "lib_2/isolate"); |
| 154 addTestDirectory("language", "language_2"); | 201 addTestDirectory("language", "language_2"); |
| 155 addTestDirectory("language_strong", "language_2"); | 202 addTestDirectory("language_strong", "language_2"); |
| 156 addTestDirectory("lib", "lib_2"); | 203 addTestDirectory("lib", "lib_2"); |
| 157 addTestDirectory("lib_strong", "lib_2"); | 204 addTestDirectory("lib_strong", "lib_2"); |
| 158 | 205 |
| 159 var sorted = tests.values.toList(); | 206 var sorted = tests.values.toList(); |
| 160 sorted.sort((a, b) => a.twoPath.compareTo(b.twoPath)); | 207 sorted.sort((a, b) => a.twoPath.compareTo(b.twoPath)); |
| 161 return sorted; | 208 return sorted; |
| 162 } | 209 } |
| 163 | 210 |
| 211 StatusFile getStatusFileEntries(String path, String test) { | |
| 212 final statusFile = new StatusFile.read(path); | |
| 213 final filteredStatusFile = new StatusFile(path); | |
|
Bob Nystrom
2017/07/24 21:47:47
Style nit: Elsewhere, I tend to use "var" even for
bkonyi
2017/07/25 22:13:40
Hm, that's interesting. I've heard different opini
| |
| 214 for (var section in statusFile.sections) { | |
| 215 StatusSection currentSection = null; | |
| 216 for (var entry in section.entries) { | |
| 217 if (entry.contains(test)) { | |
| 218 if (currentSection == null) { | |
| 219 currentSection = new StatusSection(section.condition); | |
| 220 } | |
| 221 currentSection.entries.add(entry); | |
| 222 } | |
| 223 } | |
| 224 if (currentSection != null) { | |
| 225 filteredStatusFile.sections.add(currentSection); | |
| 226 } | |
| 227 } | |
| 228 return filteredStatusFile; | |
| 229 } | |
| 230 | |
| 164 /// Moves the file from [from] to [to], which are both assumed to be relative | 231 /// Moves the file from [from] to [to], which are both assumed to be relative |
| 165 /// paths inside "tests". | 232 /// paths inside "tests". |
| 166 void moveFile(String from, String to) { | 233 void moveFile(String from, String to) { |
| 167 if (dryRun) { | 234 if (dryRun) { |
| 168 print(" Dry run: move $from to $to"); | 235 print(" Dry run: move $from to $to"); |
| 169 return; | 236 return; |
| 170 } | 237 } |
| 171 | 238 |
| 172 new File(p.join(testRoot, from)).renameSync(p.join(testRoot, to)); | 239 new File(p.join(testRoot, from)).renameSync(p.join(testRoot, to)); |
| 173 } | 240 } |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 191 bool checkForUnitTest(String path, String source) { | 258 bool checkForUnitTest(String path, String source) { |
| 192 if (!source.contains("package:unittest")) return false; | 259 if (!source.contains("package:unittest")) return false; |
| 193 | 260 |
| 194 note("${bold(path)} uses unittest package."); | 261 note("${bold(path)} uses unittest package."); |
| 195 return true; | 262 return true; |
| 196 } | 263 } |
| 197 | 264 |
| 198 class Fork { | 265 class Fork { |
| 199 final String twoPath; | 266 final String twoPath; |
| 200 String onePath; | 267 String onePath; |
| 268 StatusFile oneStatusFile; | |
|
Bob Nystrom
2017/07/24 21:47:47
Unfortunately, to make things more annoying, there
bkonyi
2017/07/25 22:13:39
Ah, I didn't realize that. Fixed.
| |
| 201 String strongPath; | 269 String strongPath; |
| 270 StatusFile strongStatusFile; | |
| 202 | 271 |
| 203 String get twoSource { | 272 String get twoSource { |
| 204 if (twoPath == null) return null; | 273 if (twoPath == null) return null; |
| 205 if (_twoSource == null) _twoSource = readFile(twoPath); | 274 if (_twoSource == null) _twoSource = readFile(twoPath); |
| 206 return _twoSource; | 275 return _twoSource; |
| 207 } | 276 } |
| 208 | 277 |
| 209 String _twoSource; | 278 String _twoSource; |
| 210 | 279 |
| 211 String get oneSource { | 280 String get oneSource { |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 281 } | 350 } |
| 282 } | 351 } |
| 283 | 352 |
| 284 if (checkForUnitTest(twoPath, twoSource)) { | 353 if (checkForUnitTest(twoPath, twoSource)) { |
| 285 todos.add("Migrate ${bold(twoPath)} off unittest."); | 354 todos.add("Migrate ${bold(twoPath)} off unittest."); |
| 286 } | 355 } |
| 287 | 356 |
| 288 return todos.length == todosBefore; | 357 return todos.length == todosBefore; |
| 289 } | 358 } |
| 290 } | 359 } |
| OLD | NEW |