diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index f1f11d15ca..aaa7f7fb96 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -12,11 +12,21 @@ import 'package:pubspec_parse/pubspec_parse.dart'; import 'common.dart'; -// TODO(cyanglaz): Add tests for this command. -// https://github.com/flutter/flutter/issues/61049 class CreateAllPluginsAppCommand extends PluginCommand { - CreateAllPluginsAppCommand(Directory packagesDir, FileSystem fileSystem) - : super(packagesDir, fileSystem); + CreateAllPluginsAppCommand( + Directory packagesDir, + FileSystem fileSystem, { + this.pluginsRoot, + }) : super(packagesDir, fileSystem) { + pluginsRoot ??= fileSystem.currentDirectory; + appDirectory = pluginsRoot.childDirectory('all_plugins'); + } + + /// The root directory of the plugin repository. + Directory pluginsRoot; + + /// The location of the synthesized app project. + Directory appDirectory; @override String get description => @@ -27,7 +37,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { @override Future run() async { - final int exitCode = await _createPlugin(); + final int exitCode = await _createApp(); if (exitCode != 0) { throw ToolExit(exitCode); } @@ -39,7 +49,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { ]); } - Future _createPlugin() async { + Future _createApp() async { final io.ProcessResult result = io.Process.runSync( 'flutter', [ @@ -47,7 +57,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { '--template=app', '--project-name=all_plugins', '--android-language=java', - './all_plugins', + appDirectory.path, ], ); @@ -57,12 +67,10 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _updateAppGradle() async { - final File gradleFile = fileSystem.file(p.join( - 'all_plugins', - 'android', - 'app', - 'build.gradle', - )); + final File gradleFile = appDirectory + .childDirectory('android') + .childDirectory('app') + .childFile('build.gradle'); if (!gradleFile.existsSync()) { throw ToolExit(64); } @@ -86,14 +94,12 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _updateManifest() async { - final File manifestFile = fileSystem.file(p.join( - 'all_plugins', - 'android', - 'app', - 'src', - 'main', - 'AndroidManifest.xml', - )); + final File manifestFile = appDirectory + .childDirectory('android') + .childDirectory('app') + .childDirectory('src') + .childDirectory('main') + .childFile('AndroidManifest.xml'); if (!manifestFile.existsSync()) { throw ToolExit(64); } @@ -124,7 +130,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { version: Version.parse('1.0.0+1'), environment: { 'sdk': VersionConstraint.compatibleWith( - Version.parse('2.0.0'), + Version.parse('2.12.0'), ), }, dependencies: { @@ -135,8 +141,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { }, dependencyOverrides: pluginDeps, ); - final File pubspecFile = - fileSystem.file(p.join('all_plugins', 'pubspec.yaml')); + final File pubspecFile = appDirectory.childFile('pubspec.yaml'); pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); } diff --git a/script/tool/test/create_all_plugins_app_command_test.dart b/script/tool/test/create_all_plugins_app_command_test.dart new file mode 100644 index 0000000000..58f24c9a3d --- /dev/null +++ b/script/tool/test/create_all_plugins_app_command_test.dart @@ -0,0 +1,91 @@ +// Copyright 2013 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. + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/local.dart'; +import 'package:flutter_plugin_tools/src/create_all_plugins_app_command.dart'; +import 'package:test/test.dart'; + +import 'util.dart'; + +void main() { + group('$CreateAllPluginsAppCommand', () { + CommandRunner runner; + FileSystem fileSystem; + Directory testRoot; + Directory packagesDir; + Directory appDir; + + setUp(() { + // Since the core of this command is a call to 'flutter create', the test + // has to use the real filesystem. Put everything possible in a unique + // temporary to minimize affect on the host system. + fileSystem = LocalFileSystem(); + testRoot = fileSystem.systemTempDirectory.createTempSync(); + packagesDir = testRoot.childDirectory('packages'); + + final CreateAllPluginsAppCommand command = CreateAllPluginsAppCommand( + packagesDir, + fileSystem, + pluginsRoot: testRoot, + ); + appDir = command.appDirectory; + runner = CommandRunner( + 'create_all_test', 'Test for $CreateAllPluginsAppCommand'); + runner.addCommand(command); + }); + + tearDown(() { + testRoot.deleteSync(recursive: true); + }); + + test('pubspec includes all plugins', () async { + createFakePlugin('plugina', packagesDirectory: packagesDir); + createFakePlugin('pluginb', packagesDirectory: packagesDir); + createFakePlugin('pluginc', packagesDirectory: packagesDir); + + await runner.run(['all-plugins-app']); + final List pubspec = + appDir.childFile('pubspec.yaml').readAsLinesSync(); + + expect( + pubspec, + containsAll([ + contains(RegExp('path: .*/packages/plugina')), + contains(RegExp('path: .*/packages/pluginb')), + contains(RegExp('path: .*/packages/pluginc')), + ])); + }); + + test('pubspec has overrides for all plugins', () async { + createFakePlugin('plugina', packagesDirectory: packagesDir); + createFakePlugin('pluginb', packagesDirectory: packagesDir); + createFakePlugin('pluginc', packagesDirectory: packagesDir); + + await runner.run(['all-plugins-app']); + final List pubspec = + appDir.childFile('pubspec.yaml').readAsLinesSync(); + + expect( + pubspec, + containsAllInOrder([ + contains('dependency_overrides:'), + contains(RegExp('path: .*/packages/plugina')), + contains(RegExp('path: .*/packages/pluginb')), + contains(RegExp('path: .*/packages/pluginc')), + ])); + }); + + test('pubspec is compatible with null-safe app code', () async { + createFakePlugin('plugina', packagesDirectory: packagesDir); + + await runner.run(['all-plugins-app']); + final String pubspec = + appDir.childFile('pubspec.yaml').readAsStringSync(); + + expect(pubspec, contains(RegExp('sdk:\\s*(?:["\']>=|[^])2\\.12\\.'))); + }); + }); +} diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 908b67b3cf..d02b756ee9 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -12,6 +12,9 @@ import 'package:platform/platform.dart'; import 'package:flutter_plugin_tools/src/common.dart'; import 'package:quiver/collection.dart'; +// TODO(stuartmorgan): Eliminate this in favor of setting up a clean filesystem +// for each test, to eliminate the chance of files from one test interfering +// with another test. FileSystem mockFileSystem = MemoryFileSystem( style: LocalPlatform().isWindows ? FileSystemStyle.windows @@ -28,7 +31,8 @@ void initializeFakePackages({Directory parentDir}) { mockPackagesDir.createSync(); } -/// Creates a plugin package with the given [name] in [mockPackagesDir]. +/// Creates a plugin package with the given [name] in [packagesDirectory], +/// defaulting to [mockPackagesDir]. Directory createFakePlugin( String name, { bool withSingleExample = false, @@ -44,13 +48,16 @@ Directory createFakePlugin( bool includeChangeLog = false, bool includeVersion = false, String parentDirectoryName = '', + Directory packagesDirectory, }) { assert(!(withSingleExample && withExamples.isNotEmpty), 'cannot pass withSingleExample and withExamples simultaneously'); - final Directory pluginDirectory = (parentDirectoryName != '') - ? mockPackagesDir.childDirectory(parentDirectoryName).childDirectory(name) - : mockPackagesDir.childDirectory(name); + Directory parentDirectory = packagesDirectory ?? mockPackagesDir; + if (parentDirectoryName != '') { + parentDirectory = parentDirectory.childDirectory(parentDirectoryName); + } + final Directory pluginDirectory = parentDirectory.childDirectory(name); pluginDirectory.createSync(recursive: true); createFakePubspec(