diff --git a/packages/flutter_tools/lib/src/commands/migrate.dart b/packages/flutter_tools/lib/src/commands/migrate.dart index abe7e78003b..7d592abec04 100644 --- a/packages/flutter_tools/lib/src/commands/migrate.dart +++ b/packages/flutter_tools/lib/src/commands/migrate.dart @@ -2,38 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// import 'package:process/process.dart'; -// import '../base/file_system.dart'; import '../base/logger.dart'; -// import '../base/platform.dart'; import '../base/terminal.dart'; import '../migrate/migrate_utils.dart'; import '../runner/flutter_command.dart'; -// TODO(garyq): Add each of these back in as they land. -// import 'migrate_abandon.dart'; -// import 'migrate_apply.dart'; -// import 'migrate_resolve_conflicts.dart'; -// import 'migrate_start.dart'; -// import 'migrate_status.dart'; + /// Base command for the migration tool. class MigrateCommand extends FlutterCommand { MigrateCommand({ - // bool verbose = false, required this.logger, - // TODO(garyq): Add each of these back in as they land. - // required FileSystem fileSystem, - // required Terminal terminal, - // required Platform platform, - // required ProcessManager processManager, + // TODO(garyq): Add each parameters in as subcommands land. }) { - // TODO(garyq): Add each of these back in as they land. - // addSubcommand(MigrateAbandonCommand(logger: logger, fileSystem: fileSystem, terminal: terminal, platform: platform, processManager: processManager)); - // addSubcommand(MigrateApplyCommand(verbose: verbose, logger: logger, fileSystem: fileSystem, terminal: terminal, platform: platform, processManager: processManager)); - // addSubcommand(MigrateResolveConflictsCommand(logger: logger, fileSystem: fileSystem, terminal: terminal)); - // addSubcommand(MigrateStartCommand(verbose: verbose, logger: logger, fileSystem: fileSystem, platform: platform, processManager: processManager)); - // addSubcommand(MigrateStatusCommand(verbose: verbose, logger: logger, fileSystem: fileSystem, platform: platform, processManager: processManager)); + // TODO(garyq): Add subcommands. } final Logger logger; diff --git a/packages/flutter_tools/lib/src/migrate/migrate_utils.dart b/packages/flutter_tools/lib/src/migrate/migrate_utils.dart index b6b57313cc5..16bc87e9be0 100644 --- a/packages/flutter_tools/lib/src/migrate/migrate_utils.dart +++ b/packages/flutter_tools/lib/src/migrate/migrate_utils.dart @@ -242,32 +242,32 @@ class MigrateUtils { bool exit = true, bool silent = false }) { - if (!allowedExitCodes.contains(result.exitCode) && !allowedExitCodes.contains(-1)) { - if (!silent) { - _logger.printError('Command encountered an error with exit code ${result.exitCode}.'); - if (commandDescription != null) { - _logger.printError('Command:'); - _logger.printError(commandDescription, indent: 2); - } - _logger.printError('Stdout:'); - _logger.printError(result.stdout, indent: 2); - _logger.printError('Stderr:'); - _logger.printError(result.stderr, indent: 2); - } - if (exit) { - throwToolExit('Command failed with exit code ${result.exitCode}', exitCode: result.exitCode); - } - return false; + if (allowedExitCodes.contains(result.exitCode) || allowedExitCodes.contains(-1)) { + return true; } - return true; + if (!silent) { + _logger.printError('Command encountered an error with exit code ${result.exitCode}.'); + if (commandDescription != null) { + _logger.printError('Command:'); + _logger.printError(commandDescription, indent: 2); + } + _logger.printError('Stdout:'); + _logger.printError(result.stdout, indent: 2); + _logger.printError('Stderr:'); + _logger.printError(result.stderr, indent: 2); + } + if (exit) { + throwToolExit('Command failed with exit code ${result.exitCode}', exitCode: result.exitCode); + } + return false; } /// Returns true if the file does not contain any git conflit markers. bool conflictsResolved(String contents) { - if (contents.contains('>>>>>>>') && contents.contains('=======') && contents.contains('<<<<<<<')) { - return false; - } - return true; + final bool hasMarker = contents.contains('>>>>>>>') || + contents.contains('=======') || + contents.contains('<<<<<<<'); + return !hasMarker; } } diff --git a/packages/flutter_tools/test/general.shard/migrate/migrate_manifest_test.dart b/packages/flutter_tools/test/general.shard/migrate/migrate_manifest_test.dart index ab166547a11..6f6f0badc56 100644 --- a/packages/flutter_tools/test/general.shard/migrate/migrate_manifest_test.dart +++ b/packages/flutter_tools/test/general.shard/migrate/migrate_manifest_test.dart @@ -4,6 +4,7 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/migrate/migrate_manifest.dart'; import 'package:flutter_tools/src/migrate/migrate_result.dart'; import 'package:flutter_tools/src/migrate/migrate_utils.dart'; @@ -13,12 +14,119 @@ import '../../src/common.dart'; void main() { late FileSystem fileSystem; late File manifestFile; + late BufferLogger logger; setUpAll(() { fileSystem = MemoryFileSystem.test(); + logger = BufferLogger.test(); manifestFile = fileSystem.file('.migrate_manifest'); }); + group('checkAndPrintMigrateStatus', () { + testWithoutContext('empty MigrateResult produces empty output', () async { + final Directory workingDir = fileSystem.directory('migrate_working_dir'); + workingDir.createSync(recursive: true); + final MigrateManifest manifest = MigrateManifest(migrateRootDir: workingDir, migrateResult: MigrateResult( + mergeResults: [], + addedFiles: [], + deletedFiles: [], + mergeTypeMap: {}, + diffMap: {}, + tempDirectories: [], + sdkDirs: {}, + )); + + checkAndPrintMigrateStatus(manifest, workingDir, warnConflict: true, logger: logger); + + expect(logger.statusText, contains('\n')); + }); + + testWithoutContext('populated MigrateResult produces correct output', () async { + final Directory workingDir = fileSystem.directory('migrate_working_dir'); + workingDir.createSync(recursive: true); + final MigrateManifest manifest = MigrateManifest(migrateRootDir: workingDir, migrateResult: MigrateResult( + mergeResults: [ + StringMergeResult.explicit( + localPath: 'merged_file', + mergedString: 'str', + hasConflict: false, + exitCode: 0, + ), + StringMergeResult.explicit( + localPath: 'conflict_file', + mergedString: 'hello\nwow a bunch of lines\n<<<<<<<\n=======\n<<<<<<<\nhi\n', + hasConflict: true, + exitCode: 1, + ), + ], + addedFiles: [FilePendingMigration('added_file', fileSystem.file('added_file'))], + deletedFiles: [FilePendingMigration('deleted_file', fileSystem.file('deleted_file'))], + // The following are ignored by the manifest. + mergeTypeMap: {'test': MergeType.threeWay}, + diffMap: {}, + tempDirectories: [], + sdkDirs: {}, + )); + + final File conflictFile = workingDir.childFile('conflict_file'); + conflictFile.writeAsStringSync('hello\nwow a bunch of lines\n<<<<<<<\n=======\n<<<<<<<\nhi\n', flush: true); + + checkAndPrintMigrateStatus(manifest, workingDir, warnConflict: true, logger: logger); + + expect(logger.statusText, contains(''' +Added files: + - added_file +Deleted files: + - deleted_file +Modified files: + - conflict_file + - merged_file +''')); + }); + + testWithoutContext('populated MigrateResult detects fixed conflict', () async { + final Directory workingDir = fileSystem.directory('migrate_working_dir'); + workingDir.createSync(recursive: true); + final MigrateManifest manifest = MigrateManifest(migrateRootDir: workingDir, migrateResult: MigrateResult( + mergeResults: [ + StringMergeResult.explicit( + localPath: 'merged_file', + mergedString: 'str', + hasConflict: false, + exitCode: 0, + ), + StringMergeResult.explicit( + localPath: 'conflict_file', + mergedString: 'hello\nwow a bunch of lines\n<<<<<<<\n=======\n<<<<<<<\nhi\n', + hasConflict: true, + exitCode: 1, + ), + ], + addedFiles: [FilePendingMigration('added_file', fileSystem.file('added_file'))], + deletedFiles: [FilePendingMigration('deleted_file', fileSystem.file('deleted_file'))], + // The following are ignored by the manifest. + mergeTypeMap: {'test': MergeType.threeWay}, + diffMap: {}, + tempDirectories: [], + sdkDirs: {}, + )); + + final File conflictFile = workingDir.childFile('conflict_file'); + conflictFile.writeAsStringSync('hello\nwow a bunch of lines\nhi\n', flush: true); + + checkAndPrintMigrateStatus(manifest, workingDir, warnConflict: true, logger: logger); + expect(logger.statusText, contains(''' +Added files: + - added_file +Deleted files: + - deleted_file +Modified files: + - conflict_file + - merged_file +''')); + }); + }); + group('manifest file parsing', () { testWithoutContext('empty fails', () async { manifestFile.writeAsStringSync(''); diff --git a/packages/flutter_tools/test/integration.shard/migrate_command_test.dart b/packages/flutter_tools/test/integration.shard/migrate_command_test.dart new file mode 100644 index 00000000000..db1afc879ba --- /dev/null +++ b/packages/flutter_tools/test/integration.shard/migrate_command_test.dart @@ -0,0 +1,96 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.8 + +import 'package:file/file.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/process.dart'; +import 'package:flutter_tools/src/commands/migrate.dart'; +import 'package:flutter_tools/src/globals.dart' as globals; +import 'package:flutter_tools/src/migrate/migrate_utils.dart'; + +import '../src/common.dart'; + +void main() { + BufferLogger logger; + FileSystem fileSystem; + Directory projectRoot; + String projectRootPath; + ProcessUtils processUtils; + MigrateUtils utils; + + setUpAll(() async { + fileSystem = globals.localFileSystem; + logger = BufferLogger.test(); + utils = MigrateUtils( + logger: logger, + fileSystem: fileSystem, + platform: globals.platform, + processManager: globals.processManager, + ); + processUtils = ProcessUtils(processManager: globals.processManager, logger: logger); + }); + + group('git', () { + setUp(() async { + projectRoot = fileSystem.systemTempDirectory.createTempSync('flutter_migrate_command_test'); + projectRoot.createSync(recursive: true); + projectRootPath = projectRoot.path; + }); + + tearDown(() async { + tryToDelete(projectRoot); + }); + + testWithoutContext('isGitRepo', () async { + expect(projectRoot.existsSync(), true); + expect(projectRoot.childDirectory('.git').existsSync(), false); + + expect(await gitRepoExists(projectRootPath, logger, utils), false); + expect(logger.statusText, contains('Project is not a git repo. Please initialize a git repo and try again.')); + + await utils.gitInit(projectRootPath); + expect(projectRoot.childDirectory('.git').existsSync(), true); + + expect(await gitRepoExists(projectRootPath, logger, utils), true); + }); + + testWithoutContext('printCommandText produces formatted output', () async { + printCommandText('some command --help', logger); + + expect(logger.statusText, contains(r' $ some command --help')); + }); + + testWithoutContext('hasUncommittedChanges false on clean repo', () async { + expect(projectRoot.existsSync(), true); + expect(projectRoot.childDirectory('.git').existsSync(), false); + await utils.gitInit(projectRootPath); + expect(projectRoot.childDirectory('.git').existsSync(), true); + + projectRoot.childFile('.gitignore') + ..createSync() + ..writeAsStringSync('ignored_file.dart', flush: true); + + await processUtils.run(['git', 'add', '.'], workingDirectory: projectRootPath); + await processUtils.run(['git', 'commit', '-m', 'Initial commit'], workingDirectory: projectRootPath); + + expect(await hasUncommittedChanges(projectRootPath, logger, utils), false); + }); + + testWithoutContext('hasUncommittedChanges true on dirty repo', () async { + expect(projectRoot.existsSync(), true); + expect(projectRoot.childDirectory('.git').existsSync(), false); + await utils.gitInit(projectRootPath); + expect(projectRoot.childDirectory('.git').existsSync(), true); + + projectRoot.childFile('some_file.dart') + ..createSync() + ..writeAsStringSync('void main() {}', flush: true); + + expect(await hasUncommittedChanges(projectRootPath, logger, utils), true); + }); + }); +} diff --git a/packages/flutter_tools/test/integration.shard/migrate_utils_test.dart b/packages/flutter_tools/test/integration.shard/migrate_utils_test.dart index 5c819e82c59..1e709419c6a 100644 --- a/packages/flutter_tools/test/integration.shard/migrate_utils_test.dart +++ b/packages/flutter_tools/test/integration.shard/migrate_utils_test.dart @@ -40,6 +40,10 @@ void main() { projectRootPath = projectRoot.path; }); + tearDown(() async { + tryToDelete(projectRoot); + }); + testWithoutContext('init', () async { expect(projectRoot.existsSync(), true); expect(projectRoot.childDirectory('.git').existsSync(), false); @@ -220,4 +224,15 @@ void main() { projectRoot.deleteSync(recursive: true); }); }); + + testWithoutContext('conflictsResolved', () async { + expect(utils.conflictsResolved(''), true); + expect(utils.conflictsResolved('hello'), true); + expect(utils.conflictsResolved('hello\n'), true); + expect(utils.conflictsResolved('hello\nwow a bunch of lines\n\nhi\n'), true); + expect(utils.conflictsResolved('hello\nwow a bunch of lines\n>>>>>>>\nhi\n'), false); + expect(utils.conflictsResolved('hello\nwow a bunch of lines\n=======\nhi\n'), false); + expect(utils.conflictsResolved('hello\nwow a bunch of lines\n<<<<<<<\nhi\n'), false); + expect(utils.conflictsResolved('hello\nwow a bunch of lines\n<<<<<<<\n=======\n<<<<<<<\nhi\n'), false); + }); }