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/validate.dart'; | |
16 import 'package:status_file/status_file.dart'; | 17 import 'package:status_file/status_file.dart'; |
17 | 18 |
18 const simpleDirs = const ["corelib", "language", "lib"]; | 19 const simpleDirs = const ["corelib", "language", "lib"]; |
19 | 20 |
20 final String sdkRoot = | 21 final String sdkRoot = |
21 p.normalize(p.join(p.dirname(p.fromUri(Platform.script)), '../../../')); | 22 p.normalize(p.join(p.dirname(p.fromUri(Platform.script)), '../../../')); |
22 | 23 |
23 final String testRoot = p.join(sdkRoot, "tests"); | 24 final String testRoot = p.join(sdkRoot, "tests"); |
24 | 25 |
25 bool dryRun = false; | 26 bool dryRun = false; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
65 | 66 |
66 if ((endIndex - startIndex) == 0) { | 67 if ((endIndex - startIndex) == 0) { |
67 print(bold("No tests to migrate.")); | 68 print(bold("No tests to migrate.")); |
68 return; | 69 return; |
69 } | 70 } |
70 | 71 |
71 print("Migrating ${bold(endIndex - startIndex)} tests from ${bold(first)} " | 72 print("Migrating ${bold(endIndex - startIndex)} tests from ${bold(first)} " |
72 "to ${bold(last)}..."); | 73 "to ${bold(last)}..."); |
73 print(""); | 74 print(""); |
74 | 75 |
75 tests = tests.getRange(startIndex, endIndex); | 76 var allTodos = <String, List<String>>{}; |
76 var todos = <String>[]; | 77 tests = tests.sublist(startIndex, endIndex); |
77 var migratedTests = 0; | 78 var migratedTests = 0; |
78 var unmigratedTests = 0; | 79 var unmigratedTests = 0; |
79 for (var test in tests) { | 80 for (var test in tests) { |
80 if (test.migrate(todos)) { | 81 var todos = test.migrate(); |
82 if (todos.isEmpty) { | |
81 migratedTests++; | 83 migratedTests++; |
82 } else { | 84 } else { |
83 unmigratedTests++; | 85 unmigratedTests++; |
86 allTodos[test.twoPath] = todos; | |
84 } | 87 } |
85 } | 88 } |
86 | 89 |
90 // Print status file entries. | |
91 var statusFileEntries = new StringBuffer(); | |
92 var statusFiles = loadStatusFiles(); | |
93 for (var statusFile in statusFiles) { | |
94 printStatusFileEntries(statusFileEntries, tests, statusFile); | |
95 } | |
96 | |
97 new File("statuses.migration") | |
98 .writeAsStringSync(statusFileEntries.toString()); | |
99 print("Wrote relevant test status file entries to 'statuses.migration'."); | |
100 | |
101 // Tell the user what's left TODO. | |
87 print(""); | 102 print(""); |
88 | |
89 var summary = ""; | 103 var summary = ""; |
90 | 104 |
91 if (migratedTests > 0) { | 105 if (migratedTests > 0) { |
92 var s = migratedTests == 1 ? "" : "s"; | 106 var s = migratedTests == 1 ? "" : "s"; |
93 summary += "Successfully migrated ${green(migratedTests)} test$s. "; | 107 summary += "Successfully migrated ${green(migratedTests)} test$s. "; |
94 } | 108 } |
95 | 109 |
96 if (unmigratedTests > 0) { | 110 if (unmigratedTests > 0) { |
97 var s = migratedTests == 1 ? "" : "s"; | 111 var s = unmigratedTests == 1 ? "" : "s"; |
98 summary += "Need manual work on ${red(unmigratedTests)} test$s:"; | 112 summary += "Need manual work on ${red(unmigratedTests)} test$s:"; |
99 } | 113 } |
100 | 114 |
101 print(summary); | 115 print(summary); |
102 todos.forEach(todo); | 116 var todoTests = allTodos.keys.toList(); |
103 | 117 todoTests.sort(); |
104 // Print status file entries. | 118 for (var todoTest in todoTests) { |
105 var statusFileEntries = new StringBuffer(); | 119 print("- ${bold(todoTest)}:"); |
106 var statusFiles = loadStatusFiles(); | 120 allTodos[todoTest].forEach(todo); |
107 for (var statusFile in statusFiles) { | |
108 printStatusFileEntries(statusFileEntries, tests, statusFile); | |
109 } | 121 } |
110 | |
111 new File("statuses.migration") | |
112 .writeAsStringSync(statusFileEntries.toString()); | |
113 print( | |
114 bold("Wrote relevant test status file entries to 'statuses.migration'")); | |
115 } | 122 } |
116 | 123 |
117 /// Returns a [String] of the relevant status file entries associated with the | 124 /// Returns a [String] of the relevant status file entries associated with the |
118 /// tests in [tests] found in [statusFile]. | 125 /// tests in [tests] found in [statusFile]. |
119 void printStatusFileEntries( | 126 void printStatusFileEntries( |
120 StringBuffer statusFileEntries, List<Fork> tests, StatusFile statusFile) { | 127 StringBuffer statusFileEntries, List<Fork> tests, StatusFile statusFile) { |
121 var filteredStatusFile = new StatusFile(statusFile.path); | 128 var filteredStatusFile = new StatusFile(statusFile.path); |
122 var testNames = <String>[]; | 129 var testNames = <String>[]; |
123 for (var test in tests) { | 130 for (var test in tests) { |
124 testNames.add(test.twoPath.split("/").last.split(".")[0]); | 131 testNames.add(test.twoPath.split("/").last.split(".")[0]); |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
234 } | 241 } |
235 | 242 |
236 /// Moves the file from [from] to [to], which are both assumed to be relative | 243 /// Moves the file from [from] to [to], which are both assumed to be relative |
237 /// paths inside "tests". | 244 /// paths inside "tests". |
238 void moveFile(String from, String to) { | 245 void moveFile(String from, String to) { |
239 if (dryRun) { | 246 if (dryRun) { |
240 print(" Dry run: move $from to $to"); | 247 print(" Dry run: move $from to $to"); |
241 return; | 248 return; |
242 } | 249 } |
243 | 250 |
251 // Create the directory if needed. | |
252 new Directory(p.dirname(p.join(testRoot, to))).createSync(recursive: true); | |
bkonyi
2017/07/26 17:59:26
Nice! I just ran into this case.
| |
253 | |
244 new File(p.join(testRoot, from)).renameSync(p.join(testRoot, to)); | 254 new File(p.join(testRoot, from)).renameSync(p.join(testRoot, to)); |
245 } | 255 } |
246 | 256 |
247 /// Reads the contents of the file at [path], which is assumed to be relative | 257 /// Reads the contents of the file at [path], which is assumed to be relative |
248 /// within "tests". | 258 /// within "tests". |
249 String readFile(String path) { | 259 String readFile(String path) { |
250 return new File(p.join(testRoot, path)).readAsStringSync(); | 260 return new File(p.join(testRoot, path)).readAsStringSync(); |
251 } | 261 } |
252 | 262 |
253 /// Deletes the file at [path], which is assumed to be relative within "tests". | 263 /// Deletes the file at [path], which is assumed to be relative within "tests". |
254 void deleteFile(String path) { | 264 void deleteFile(String path) { |
255 if (dryRun) { | 265 if (dryRun) { |
256 print(" Dry run: delete $path"); | 266 print(" Dry run: delete $path"); |
257 return; | 267 return; |
258 } | 268 } |
259 | 269 |
260 new File(p.join(testRoot, path)).deleteSync(); | 270 new File(p.join(testRoot, path)).deleteSync(); |
261 } | 271 } |
262 | 272 |
263 bool checkForUnitTest(String path, String source) { | |
264 if (!source.contains("package:unittest")) return false; | |
265 | |
266 note("${bold(path)} uses unittest package."); | |
267 return true; | |
268 } | |
269 | |
270 class Fork { | 273 class Fork { |
271 final String twoPath; | 274 final String twoPath; |
272 String onePath; | 275 String onePath; |
273 String strongPath; | 276 String strongPath; |
274 | 277 |
275 String get twoSource { | 278 String get twoSource { |
276 if (twoPath == null) return null; | 279 if (twoPath == null) return null; |
277 if (_twoSource == null) _twoSource = readFile(twoPath); | 280 if (_twoSource == null) _twoSource = readFile(twoPath); |
278 return _twoSource; | 281 return _twoSource; |
279 } | 282 } |
(...skipping 11 matching lines...) Expand all Loading... | |
291 String get strongSource { | 294 String get strongSource { |
292 if (strongPath == null) return null; | 295 if (strongPath == null) return null; |
293 if (_strongSource == null) _strongSource = readFile(strongPath); | 296 if (_strongSource == null) _strongSource = readFile(strongPath); |
294 return _strongSource; | 297 return _strongSource; |
295 } | 298 } |
296 | 299 |
297 String _strongSource; | 300 String _strongSource; |
298 | 301 |
299 Fork(this.twoPath); | 302 Fork(this.twoPath); |
300 | 303 |
301 bool migrate(List<String> todos) { | 304 List<String> migrate() { |
302 print("- ${bold(twoPath)}:"); | 305 print("- ${bold(twoPath)}:"); |
303 | 306 |
304 var todosBefore = todos.length; | 307 var todos = <String>[]; |
305 var isMigrated = new File(p.join(testRoot, twoPath)).existsSync(); | 308 var isMigrated = new File(p.join(testRoot, twoPath)).existsSync(); |
306 | 309 |
307 // If there is a migrated version and it's the same as an unmigrated one, | 310 // If there is a migrated version and it's the same as an unmigrated one, |
308 // delete the unmigrated one. | 311 // delete the unmigrated one. |
309 if (isMigrated) { | 312 if (isMigrated) { |
310 if (onePath != null) { | 313 if (onePath != null) { |
311 if (oneSource == twoSource) { | 314 if (oneSource == twoSource) { |
312 deleteFile(onePath); | 315 deleteFile(onePath); |
313 done("Deleted already-migrated $onePath."); | 316 done("Deleted already-migrated $onePath."); |
314 } else { | 317 } else { |
315 note("${bold(onePath)} does not match already-migrated " | 318 note("${bold(onePath)} does not match already-migrated " |
316 "${bold(twoPath)}."); | 319 "${bold(twoPath)}."); |
317 todos.add("Merge ${bold(onePath)} into ${bold(twoPath)}."); | 320 todos.add("Merge from ${bold(onePath)} into this file."); |
318 checkForUnitTest(onePath, oneSource); | 321 validateFile(onePath, oneSource); |
319 } | 322 } |
320 } | 323 } |
321 | 324 |
322 if (strongPath != null) { | 325 if (strongPath != null) { |
323 if (strongSource == twoSource) { | 326 if (strongSource == twoSource) { |
324 deleteFile(strongPath); | 327 deleteFile(strongPath); |
325 done("Deleted already-migrated ${bold(strongPath)}."); | 328 done("Deleted already-migrated ${bold(strongPath)}."); |
326 } else { | 329 } else { |
327 note("${bold(strongPath)} does not match already-migrated " | 330 note("${bold(strongPath)} does not match already-migrated " |
328 "${bold(twoPath)}."); | 331 "${bold(twoPath)}."); |
329 todos.add("Merge ${bold(strongPath)} into ${bold(twoPath)}."); | 332 todos.add("Merge from ${bold(strongPath)} into this file."); |
330 checkForUnitTest(strongPath, strongSource); | 333 validateFile(strongPath, strongSource); |
331 } | 334 } |
332 } | 335 } |
333 } else { | 336 } else { |
334 // If it only exists in one place, just move it. | 337 // If it only exists in one place, just move it. |
335 if (strongPath == null) { | 338 if (strongPath == null) { |
336 moveFile(onePath, twoPath); | 339 moveFile(onePath, twoPath); |
337 done("Moved from ${bold(onePath)} (no strong mode fork)."); | 340 done("Moved from ${bold(onePath)} (no strong mode fork)."); |
338 } else if (onePath == null) { | 341 } else if (onePath == null) { |
339 moveFile(strongPath, twoPath); | 342 moveFile(strongPath, twoPath); |
340 done("Moved from ${bold(strongPath)} (no 1.0 mode fork)."); | 343 done("Moved from ${bold(strongPath)} (no 1.0 mode fork)."); |
341 } else if (oneSource == strongSource) { | 344 } else if (oneSource == strongSource) { |
342 // The forks are identical, pick one. | 345 // The forks are identical, pick one. |
343 moveFile(onePath, twoPath); | 346 moveFile(onePath, twoPath); |
344 deleteFile(strongPath); | 347 deleteFile(strongPath); |
345 done("Merged identical forks."); | 348 done("Merged identical forks."); |
346 checkForUnitTest(twoPath, oneSource); | 349 validateFile(twoPath, oneSource); |
347 } else { | 350 } else { |
348 // Otherwise, a manual merge is required. Start with the strong one. | 351 // Otherwise, a manual merge is required. Start with the strong one. |
352 print(new File(strongPath).existsSync()); | |
349 moveFile(strongPath, twoPath); | 353 moveFile(strongPath, twoPath); |
350 done("Moved strong fork, kept 1.0 fork, manual merge required."); | 354 done("Moved strong fork, kept 1.0 fork, manual merge required."); |
351 todos.add("Merge ${bold(onePath)} into ${bold(twoPath)}."); | 355 todos.add("Merge from ${bold(onePath)} into this file."); |
352 checkForUnitTest(onePath, oneSource); | 356 validateFile(onePath, oneSource); |
353 } | 357 } |
354 } | 358 } |
355 | 359 |
356 if (checkForUnitTest(twoPath, twoSource)) { | 360 validateFile(twoPath, twoSource, todos); |
357 todos.add("Migrate ${bold(twoPath)} off unittest."); | |
358 } | |
359 | 361 |
360 return todos.length == todosBefore; | 362 return todos; |
361 } | 363 } |
362 } | 364 } |
OLD | NEW |