Index: tools/migration/lib/src/migrate_statuses.dart |
diff --git a/tools/migration/lib/src/migrate_statuses.dart b/tools/migration/lib/src/migrate_statuses.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..dead6ef88e88527308d65c1b5e747fadb6b81e6c |
--- /dev/null |
+++ b/tools/migration/lib/src/migrate_statuses.dart |
@@ -0,0 +1,128 @@ |
+// 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. |
+ |
+import 'package:path/path.dart' as p; |
+ |
+import 'editable_status_file.dart'; |
+import 'fork.dart'; |
+import 'io.dart'; |
+import 'test_directories.dart'; |
+ |
+/// Migrates the status file entries that match [files]. |
+void migrateStatusEntries(List<Fork> files) { |
+ var entriesToMove = new EntrySet(); |
+ |
+ _collectEntries(files, entriesToMove, isOne: true); |
+ _collectEntries(files, entriesToMove, isOne: false); |
+ |
+ for (var statusFile in entriesToMove.statusFiles) { |
+ var sections = entriesToMove.sections(statusFile); |
+ _addEntries(statusFile, sections); |
+ } |
+ |
+ // TODO(rnystrom): Should this log any output? |
+} |
+ |
+/// Tracks a set of entries to add to a set of Dart 2.0 status files. |
+class EntrySet { |
+ /// Keys are the names of the Dart 2.0 status file that will receive the |
+ /// entries. The value for each key is a map of section headers to the list |
+ /// of entries to add under that section. |
+ final Map<String, Map<String, List<String>>> _files = {}; |
+ |
+ Iterable<String> get statusFiles => _files.keys; |
+ |
+ void add(String fromDir, String header, String entry) { |
+ var toDir = toTwoDirectory(fromDir); |
+ var sections = _files.putIfAbsent(p.join(toDir, "$toDir.status"), () => {}); |
+ var entries = sections.putIfAbsent(header, () => []); |
+ entries.add(entry); |
+ } |
+ |
+ Map<String, List<String>> sections(String file) => _files[file]; |
+} |
+ |
+/// Removes entries from the 1.0 and strong status files that correspond to |
+/// the list of [files] being migrated. |
+/// |
+/// Adds moved entries to [entriesToMove]. |
+void _collectEntries(List<Fork> files, EntrySet entriesToMove, {bool isOne}) { |
+ // Map the files to the way they will appear in the status file. |
+ var filePaths = files |
+ .map((fork) => p.withoutExtension(isOne ? fork.onePath : fork.strongPath)) |
+ .toList(); |
+ |
+ for (var fromDir in isOne ? oneRootDirs : strongRootDirs) { |
+ for (var path in listFiles(fromDir, extension: ".status")) { |
+ var editable = new EditableStatusFile(path); |
+ |
+ var deleteLines = <int>[]; |
+ for (var section in editable.statusFile.sections) { |
+ // TODO(rnystrom): For now, we don't support entries in the initial |
+ // implicit section at the top of the file. Do we need to? |
+ if (section.condition == null) continue; |
+ |
+ for (var entry in section.entries) { |
+ var entryPath = p.join(fromDir, entry.path); |
+ |
+ for (var filePath in filePaths) { |
+ // We only support entries that precisely match the file being |
+ // migrated, or a multitest within that. In both cases, the entry |
+ // path will begin with the full path of the file. We don't migrate |
+ // directory or glob patterns because those may also match other |
+ // files that have not been migrated yet. |
+ // TODO(rnystrom): It would be good to detect when a glob matches |
+ // a migrated file and let the user know that they may need to |
+ // manually handle it. |
+ if (!entryPath.startsWith(filePath)) continue; |
+ |
+ // Remove it from this status file. |
+ deleteLines.add(entry.lineNumber - 1); |
+ |
+ // And add it to the 2.0 one. |
+ entriesToMove.add(fromDir, editable.lineAt(section.lineNumber), |
+ editable.lineAt(entry.lineNumber)); |
+ } |
+ } |
+ } |
+ |
+ // TODO(rnystrom): If all of the entries are deleted from a section, it |
+ // would be nice to delete the section header too. |
+ editable.delete(deleteLines); |
+ } |
+ } |
+} |
+ |
+/// Adds all of [entries] to the status file at [path]. |
+/// |
+/// If the status file already has a section that matches a header in [entries], |
+/// then adds those entries to the end of that section. Otherwise, appends a |
+/// new section to the end of the file. |
+void _addEntries(String path, Map<String, List<String>> entries) { |
+ var editable = new EditableStatusFile(p.join(testRoot, path)); |
+ |
+ for (var header in entries.keys) { |
+ var found = false; |
+ |
+ // Look for an existing section with the same header to add it to. |
+ for (var section in editable.statusFile.sections) { |
+ if (header == editable.lineAt(section.lineNumber)) { |
+ var line = section.lineNumber; |
+ // Add after existing entries, if there are any. |
+ if (section.entries.isNotEmpty) { |
+ line = section.entries.last.lineNumber; |
+ } |
+ |
+ editable.insert(line, entries[header]); |
+ found = true; |
+ break; |
+ } |
+ } |
+ |
+ if (!found) { |
+ // This section doesn't exist in the status file, so add it. |
+ editable.append(header, entries[header]); |
+ } |
+ } |
+} |