| // 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 'dart:io'; |
| |
| import 'package:path/path.dart' as p; |
| |
| import 'io.dart'; |
| import 'log.dart'; |
| import 'test_directories.dart'; |
| import 'validate.dart'; |
| |
| int findFork(List<Fork> forks, String description) { |
| var matches = <int>[]; |
| |
| for (var i = 0; i < forks.length; i++) { |
| if (forks[i].twoPath.contains(description)) matches.add(i); |
| } |
| |
| if (matches.isEmpty) { |
| print('Could not find a test matching "${bold(description)}".'); |
| return null; |
| } else if (matches.length == 1) { |
| return matches.first; |
| } else { |
| print('Description "${bold(description)}" is ambiguous. Could be any of:'); |
| for (var i in matches) { |
| print("- ${forks[i].twoPath.replaceAll(description, bold(description))}"); |
| } |
| |
| print("Please use a more precise description."); |
| return null; |
| } |
| } |
| |
| /// 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>{}; |
| |
| for (var fromDir in fromRootDirs) { |
| var twoDir = toTwoDirectory(fromDir); |
| for (var path in listFiles(fromDir)) { |
| var fromPath = p.relative(path, from: testRoot); |
| var twoPath = p.join(twoDir, p.relative(fromPath, from: fromDir)); |
| |
| tests.putIfAbsent(twoPath, () => new Fork(twoPath)); |
| } |
| } |
| |
| // Include tests that have already been migrated too so we can show what |
| // works remains to be done in them. |
| for (var dir in twoRootDirs) { |
| for (var path in listFiles(dir)) { |
| var twoPath = p.relative(path, from: testRoot); |
| tests.putIfAbsent(twoPath, () => new Fork(twoPath)); |
| } |
| } |
| |
| var sorted = tests.values.toList(); |
| sorted.sort((a, b) => a.twoPath.compareTo(b.twoPath)); |
| return sorted; |
| } |
| |
| /// Tracks one test and the various forked locations where it may appear. |
| /// |
| /// * "One" refers to the original Dart 1.0 location of the test: language/, |
| /// corelib/, etc. |
| /// |
| /// * "Strong" is the DDC fork of the file: language_strong, etc. |
| /// |
| /// * "Two" is the migrated 2.0 destination location: language_2, etc. |
| class Fork { |
| final String twoPath; |
| String get onePath => toOnePath(twoPath); |
| String get strongPath => toStrongPath(twoPath); |
| |
| 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; |
| } |
| |
| bool get oneExists => new File(p.join(testRoot, onePath)).existsSync(); |
| bool get strongExists => new File(p.join(testRoot, strongPath)).existsSync(); |
| bool get twoExists => new File(p.join(testRoot, twoPath)).existsSync(); |
| |
| String _strongSource; |
| |
| Fork(this.twoPath); |
| |
| List<String> migrate() { |
| print("- ${bold(twoPath)}:"); |
| |
| var todos = <String>[]; |
| |
| if (!oneExists && !strongExists) { |
| // It's already been migrated, so there's nothing to move. |
| note("Is already migrated."); |
| } else if (twoExists) { |
| // If there is a migrated version and it's the same as an unmigrated one, |
| // note that. |
| if (oneExists) { |
| if (oneSource == twoSource) { |
| done("Is already migrated from $onePath."); |
| } else { |
| note("${bold(onePath)} does not match already-migrated file."); |
| todos.add("Merge from ${bold(onePath)} into this file."); |
| validateFile(onePath, oneSource); |
| } |
| } |
| |
| if (strongExists) { |
| if (strongSource == twoSource) { |
| deleteFile(strongPath); |
| done("Deleted already-migrated ${bold(strongPath)}."); |
| } else { |
| note("${bold(strongPath)} does not match already-migrated file."); |
| todos.add("Merge from ${bold(strongPath)} into this file."); |
| validateFile(strongPath, strongSource); |
| } |
| } |
| } else { |
| if (!strongExists) { |
| // If it only exists in one place, just move it. |
| copyFile(onePath, twoPath); |
| done("Copied from ${bold(onePath)} (no strong mode fork)."); |
| } else if (!oneExists) { |
| // If it only exists in one place, just move it. |
| moveFile(strongPath, twoPath); |
| done("Moved from ${bold(strongPath)} (no 1.0 mode fork)."); |
| } else if (oneSource == strongSource) { |
| // The forks are identical, move the strong one. |
| moveFile(strongPath, twoPath); |
| done("Moved from ${bold(strongPath)} (both forks identical)."); |
| validateFile(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 from ${bold(onePath)} into this file."); |
| validateFile(onePath, oneSource); |
| } |
| } |
| |
| // See what work is left to be done in the migrated file. |
| if (twoExists) { |
| validateFile(twoPath, twoSource, todos); |
| } |
| |
| return todos; |
| } |
| } |