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

Side by Side Diff: tools/migration/bin/migrate_batch.dart

Issue 2991803003: Report more heuristics for tests that require manual work. (Closed)
Patch Set: Tweaks. Created 3 years, 4 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 unified diff | Download patch
« no previous file with comments | « no previous file | tools/migration/lib/src/log.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 startIndex = i; 58 startIndex = i;
58 } 59 }
59 60
60 if (tests[i].twoPath.compareTo(last) > 0) { 61 if (tests[i].twoPath.compareTo(last) > 0) {
61 endIndex = i; 62 endIndex = i;
62 break; 63 break;
63 } 64 }
64 } 65 }
65 66
66 if ((endIndex - startIndex) == 0) { 67 if ((endIndex - startIndex) == 0) {
67 print(bold("No tests to migrate.")); 68 print(bold("No tests in range."));
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 16 matching lines...) Expand all
141 } 148 }
142 if (!filteredStatusFile.isEmpty) { 149 if (!filteredStatusFile.isEmpty) {
143 statusFileEntries.writeln("Entries for status file ${statusFile.path}:"); 150 statusFileEntries.writeln("Entries for status file ${statusFile.path}:");
144 statusFileEntries.writeln(filteredStatusFile); 151 statusFileEntries.writeln(filteredStatusFile);
145 } 152 }
146 } 153 }
147 154
148 String toTwoPath(String path) { 155 String toTwoPath(String path) {
149 // Allow eliding "_test" and/or ".dart" to make things more command-line 156 // Allow eliding "_test" and/or ".dart" to make things more command-line
150 // friendly. 157 // friendly.
151 if (!path.endsWith("_test.dart")) path += "_test.dart"; 158 if (!path.endsWith(".dart") && !path.endsWith("_test.dart")) {
159 path += "_test.dart";
160 }
152 if (!path.endsWith(".dart")) path += ".dart"; 161 if (!path.endsWith(".dart")) path += ".dart";
153 162
154 for (var dir in simpleDirs) { 163 for (var dir in simpleDirs) {
155 if (p.isWithin(dir, path)) { 164 if (p.isWithin(dir, path)) {
156 return p.join("${dir}_2", p.relative(path, from: dir)); 165 return p.join("${dir}_2", p.relative(path, from: dir));
157 } 166 }
158 167
159 if (p.isWithin("${dir}_strong", path)) { 168 if (p.isWithin("${dir}_strong", path)) {
160 return p.join("${dir}_2", p.relative(path, from: dir)); 169 return p.join("${dir}_2", p.relative(path, from: dir));
161 } 170 }
(...skipping 14 matching lines...) Expand all
176 /// Loads all of the unforked test files. 185 /// Loads all of the unforked test files.
177 /// 186 ///
178 /// Creates an list of [Fork]s, ordered by their destination paths. Handles 187 /// Creates an list of [Fork]s, ordered by their destination paths. Handles
179 /// tests that only appear in one fork or the other, or both. 188 /// tests that only appear in one fork or the other, or both.
180 List<Fork> scanTests() { 189 List<Fork> scanTests() {
181 var tests = <String, Fork>{}; 190 var tests = <String, Fork>{};
182 191
183 addTestDirectory(String fromDir, String twoDir) { 192 addTestDirectory(String fromDir, String twoDir) {
184 for (var entry 193 for (var entry
185 in new Directory(p.join(testRoot, fromDir)).listSync(recursive: true)) { 194 in new Directory(p.join(testRoot, fromDir)).listSync(recursive: true)) {
186 if (!entry.path.endsWith("_test.dart")) continue; 195 if (!entry.path.endsWith(".dart")) continue;
187 196
188 var fromPath = p.relative(entry.path, from: testRoot); 197 var fromPath = p.relative(entry.path, from: testRoot);
189 var twoPath = p.join(twoDir, p.relative(fromPath, from: fromDir)); 198 var twoPath = p.join(twoDir, p.relative(fromPath, from: fromDir));
199
190 var fork = tests.putIfAbsent(twoPath, () => new Fork(twoPath)); 200 var fork = tests.putIfAbsent(twoPath, () => new Fork(twoPath));
191 if (fromDir.contains("_strong")) { 201 if (fromDir.contains("_strong")) {
192 fork.strongPath = fromPath; 202 fork.strongPath = fromPath;
193 } else { 203 } else {
194 fork.onePath = fromPath; 204 fork.onePath = fromPath;
195 } 205 }
196 } 206 }
197 } 207 }
198 208
199 addTestDirectory("corelib", "corelib_2"); 209 addTestDirectory("corelib", "corelib_2");
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 } 244 }
235 245
236 /// Moves the file from [from] to [to], which are both assumed to be relative 246 /// Moves the file from [from] to [to], which are both assumed to be relative
237 /// paths inside "tests". 247 /// paths inside "tests".
238 void moveFile(String from, String to) { 248 void moveFile(String from, String to) {
239 if (dryRun) { 249 if (dryRun) {
240 print(" Dry run: move $from to $to"); 250 print(" Dry run: move $from to $to");
241 return; 251 return;
242 } 252 }
243 253
254 // Create the directory if needed.
255 new Directory(p.dirname(p.join(testRoot, to))).createSync(recursive: true);
256
244 new File(p.join(testRoot, from)).renameSync(p.join(testRoot, to)); 257 new File(p.join(testRoot, from)).renameSync(p.join(testRoot, to));
245 } 258 }
246 259
247 /// Reads the contents of the file at [path], which is assumed to be relative 260 /// Reads the contents of the file at [path], which is assumed to be relative
248 /// within "tests". 261 /// within "tests".
249 String readFile(String path) { 262 String readFile(String path) {
250 return new File(p.join(testRoot, path)).readAsStringSync(); 263 return new File(p.join(testRoot, path)).readAsStringSync();
251 } 264 }
252 265
253 /// Deletes the file at [path], which is assumed to be relative within "tests". 266 /// Deletes the file at [path], which is assumed to be relative within "tests".
254 void deleteFile(String path) { 267 void deleteFile(String path) {
255 if (dryRun) { 268 if (dryRun) {
256 print(" Dry run: delete $path"); 269 print(" Dry run: delete $path");
257 return; 270 return;
258 } 271 }
259 272
260 new File(p.join(testRoot, path)).deleteSync(); 273 new File(p.join(testRoot, path)).deleteSync();
261 } 274 }
262 275
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 { 276 class Fork {
271 final String twoPath; 277 final String twoPath;
272 String onePath; 278 String onePath;
273 String strongPath; 279 String strongPath;
274 280
275 String get twoSource { 281 String get twoSource {
276 if (twoPath == null) return null; 282 if (twoPath == null) return null;
277 if (_twoSource == null) _twoSource = readFile(twoPath); 283 if (_twoSource == null) _twoSource = readFile(twoPath);
278 return _twoSource; 284 return _twoSource;
279 } 285 }
(...skipping 11 matching lines...) Expand all
291 String get strongSource { 297 String get strongSource {
292 if (strongPath == null) return null; 298 if (strongPath == null) return null;
293 if (_strongSource == null) _strongSource = readFile(strongPath); 299 if (_strongSource == null) _strongSource = readFile(strongPath);
294 return _strongSource; 300 return _strongSource;
295 } 301 }
296 302
297 String _strongSource; 303 String _strongSource;
298 304
299 Fork(this.twoPath); 305 Fork(this.twoPath);
300 306
301 bool migrate(List<String> todos) { 307 List<String> migrate() {
302 print("- ${bold(twoPath)}:"); 308 print("- ${bold(twoPath)}:");
303 309
304 var todosBefore = todos.length; 310 var todos = <String>[];
305 var isMigrated = new File(p.join(testRoot, twoPath)).existsSync(); 311 var isMigrated = new File(p.join(testRoot, twoPath)).existsSync();
306 312
307 // If there is a migrated version and it's the same as an unmigrated one, 313 // If there is a migrated version and it's the same as an unmigrated one,
308 // delete the unmigrated one. 314 // delete the unmigrated one.
309 if (isMigrated) { 315 if (isMigrated) {
310 if (onePath != null) { 316 if (onePath != null) {
311 if (oneSource == twoSource) { 317 if (oneSource == twoSource) {
312 deleteFile(onePath); 318 deleteFile(onePath);
313 done("Deleted already-migrated $onePath."); 319 done("Deleted already-migrated $onePath.");
314 } else { 320 } else {
315 note("${bold(onePath)} does not match already-migrated " 321 note("${bold(onePath)} does not match already-migrated "
316 "${bold(twoPath)}."); 322 "${bold(twoPath)}.");
317 todos.add("Merge ${bold(onePath)} into ${bold(twoPath)}."); 323 todos.add("Merge from ${bold(onePath)} into this file.");
318 checkForUnitTest(onePath, oneSource); 324 validateFile(onePath, oneSource);
319 } 325 }
320 } 326 }
321 327
322 if (strongPath != null) { 328 if (strongPath != null) {
323 if (strongSource == twoSource) { 329 if (strongSource == twoSource) {
324 deleteFile(strongPath); 330 deleteFile(strongPath);
325 done("Deleted already-migrated ${bold(strongPath)}."); 331 done("Deleted already-migrated ${bold(strongPath)}.");
326 } else { 332 } else {
327 note("${bold(strongPath)} does not match already-migrated " 333 note("${bold(strongPath)} does not match already-migrated "
328 "${bold(twoPath)}."); 334 "${bold(twoPath)}.");
329 todos.add("Merge ${bold(strongPath)} into ${bold(twoPath)}."); 335 todos.add("Merge from ${bold(strongPath)} into this file.");
330 checkForUnitTest(strongPath, strongSource); 336 validateFile(strongPath, strongSource);
331 } 337 }
332 } 338 }
333 } else { 339 } else {
334 // If it only exists in one place, just move it. 340 // If it only exists in one place, just move it.
335 if (strongPath == null) { 341 if (strongPath == null) {
336 moveFile(onePath, twoPath); 342 moveFile(onePath, twoPath);
337 done("Moved from ${bold(onePath)} (no strong mode fork)."); 343 done("Moved from ${bold(onePath)} (no strong mode fork).");
338 } else if (onePath == null) { 344 } else if (onePath == null) {
339 moveFile(strongPath, twoPath); 345 moveFile(strongPath, twoPath);
340 done("Moved from ${bold(strongPath)} (no 1.0 mode fork)."); 346 done("Moved from ${bold(strongPath)} (no 1.0 mode fork).");
341 } else if (oneSource == strongSource) { 347 } else if (oneSource == strongSource) {
342 // The forks are identical, pick one. 348 // The forks are identical, pick one.
343 moveFile(onePath, twoPath); 349 moveFile(onePath, twoPath);
344 deleteFile(strongPath); 350 deleteFile(strongPath);
345 done("Merged identical forks."); 351 done("Merged identical forks.");
346 checkForUnitTest(twoPath, oneSource); 352 validateFile(twoPath, oneSource);
347 } else { 353 } else {
348 // Otherwise, a manual merge is required. Start with the strong one. 354 // Otherwise, a manual merge is required. Start with the strong one.
355 print(new File(strongPath).existsSync());
349 moveFile(strongPath, twoPath); 356 moveFile(strongPath, twoPath);
350 done("Moved strong fork, kept 1.0 fork, manual merge required."); 357 done("Moved strong fork, kept 1.0 fork, manual merge required.");
351 todos.add("Merge ${bold(onePath)} into ${bold(twoPath)}."); 358 todos.add("Merge from ${bold(onePath)} into this file.");
352 checkForUnitTest(onePath, oneSource); 359 validateFile(onePath, oneSource);
353 } 360 }
354 } 361 }
355 362
356 if (checkForUnitTest(twoPath, twoSource)) { 363 validateFile(twoPath, twoSource, todos);
357 todos.add("Migrate ${bold(twoPath)} off unittest.");
358 }
359 364
360 return todos.length == todosBefore; 365 return todos;
361 } 366 }
362 } 367 }
OLDNEW
« no previous file with comments | « no previous file | tools/migration/lib/src/log.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698