| // 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; | |
| } | |
| } |