| // 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 'dart:io' as io; |
| |
| import 'package:args/command_runner.dart'; |
| import 'package:file/file.dart'; |
| import 'package:file/memory.dart'; |
| import 'package:flutter_plugin_tools/src/build_examples_command.dart'; |
| import 'package:flutter_plugin_tools/src/common/core.dart'; |
| import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'mocks.dart'; |
| import 'util.dart'; |
| |
| void main() { |
| group('build-example', () { |
| late FileSystem fileSystem; |
| late MockPlatform mockPlatform; |
| late Directory packagesDir; |
| late CommandRunner<void> runner; |
| late RecordingProcessRunner processRunner; |
| |
| setUp(() { |
| fileSystem = MemoryFileSystem(); |
| mockPlatform = MockPlatform(); |
| packagesDir = createPackagesDirectory(fileSystem: fileSystem); |
| processRunner = RecordingProcessRunner(); |
| final BuildExamplesCommand command = BuildExamplesCommand( |
| packagesDir, |
| processRunner: processRunner, |
| platform: mockPlatform, |
| ); |
| |
| runner = CommandRunner<void>( |
| 'build_examples_command', 'Test for build_example_command'); |
| runner.addCommand(command); |
| }); |
| |
| test('fails if no plaform flags are passed', () async { |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples'], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('At least one platform must be provided'), |
| ])); |
| }); |
| |
| test('fails if building fails', () async { |
| createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| processRunner |
| .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = |
| <io.Process>[ |
| MockProcess(exitCode: 1) // flutter packages get |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--ios'], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('The following packages had errors:'), |
| contains(' plugin:\n' |
| ' plugin/example (iOS)'), |
| ])); |
| }); |
| |
| test('fails if a plugin has no examples', () async { |
| createFakePlugin('plugin', packagesDir, |
| examples: <String>[], |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline) |
| }); |
| |
| processRunner |
| .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = |
| <io.Process>[ |
| MockProcess(exitCode: 1) // flutter packages get |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--ios'], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('The following packages had errors:'), |
| contains(' plugin:\n' |
| ' No examples found'), |
| ])); |
| }); |
| |
| test('building for iOS when plugin is not set up for iOS results in no-op', |
| () async { |
| mockPlatform.isMacOS = true; |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = |
| await runCapturingPrint(runner, <String>['build-examples', '--ios']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('iOS is not supported by this plugin'), |
| ]), |
| ); |
| |
| // Output should be empty since running build-examples --macos with no macos |
| // implementation is a no-op. |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('building for iOS', () async { |
| mockPlatform.isMacOS = true; |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final List<String> output = await runCapturingPrint(runner, |
| <String>['build-examples', '--ios', '--enable-experiment=exp1']); |
| |
| expect( |
| output, |
| containsAllInOrder(<String>[ |
| '\nBUILDING plugin/example for iOS', |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>[ |
| 'build', |
| 'ios', |
| '--no-codesign', |
| '--enable-experiment=exp1' |
| ], |
| pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test( |
| 'building for Linux when plugin is not set up for Linux results in no-op', |
| () async { |
| mockPlatform.isLinux = true; |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--linux']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('Linux is not supported by this plugin'), |
| ]), |
| ); |
| |
| // Output should be empty since running build-examples --linux with no |
| // Linux implementation is a no-op. |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('building for Linux', () async { |
| mockPlatform.isLinux = true; |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformLinux: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--linux']); |
| |
| expect( |
| output, |
| containsAllInOrder(<String>[ |
| '\nBUILDING plugin/example for Linux', |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall(getFlutterCommand(mockPlatform), |
| const <String>['build', 'linux'], pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test('building for macOS with no implementation results in no-op', |
| () async { |
| mockPlatform.isMacOS = true; |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--macos']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('macOS is not supported by this plugin'), |
| ]), |
| ); |
| |
| // Output should be empty since running build-examples --macos with no macos |
| // implementation is a no-op. |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('building for macOS', () async { |
| mockPlatform.isMacOS = true; |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--macos']); |
| |
| expect( |
| output, |
| containsAllInOrder(<String>[ |
| '\nBUILDING plugin/example for macOS', |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall(getFlutterCommand(mockPlatform), |
| const <String>['build', 'macos'], pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test('building for web with no implementation results in no-op', () async { |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = |
| await runCapturingPrint(runner, <String>['build-examples', '--web']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('web is not supported by this plugin'), |
| ]), |
| ); |
| |
| // Output should be empty since running build-examples --macos with no macos |
| // implementation is a no-op. |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('building for web', () async { |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformWeb: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final List<String> output = |
| await runCapturingPrint(runner, <String>['build-examples', '--web']); |
| |
| expect( |
| output, |
| containsAllInOrder(<String>[ |
| '\nBUILDING plugin/example for web', |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall(getFlutterCommand(mockPlatform), |
| const <String>['build', 'web'], pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test( |
| 'building for win32 when plugin is not set up for Windows results in no-op', |
| () async { |
| mockPlatform.isWindows = true; |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--windows']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('Win32 is not supported by this plugin'), |
| ]), |
| ); |
| |
| // Output should be empty since running build-examples --windows with no |
| // Windows implementation is a no-op. |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('building for win32', () async { |
| mockPlatform.isWindows = true; |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformWindows: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--windows']); |
| |
| expect( |
| output, |
| containsAllInOrder(<String>[ |
| '\nBUILDING plugin/example for Win32 (windows)', |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>['build', 'windows'], |
| pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test('building for UWP when plugin does not support UWP is a no-op', |
| () async { |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--winuwp']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('UWP is not supported by this plugin'), |
| ]), |
| ); |
| |
| // Output should be empty since running build-examples --macos with no macos |
| // implementation is a no-op. |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('building for UWP', () async { |
| final Directory pluginDirectory = |
| createFakePlugin('plugin', packagesDir, extraFiles: <String>[ |
| 'example/test', |
| ], platformSupport: <String, PlatformDetails>{ |
| platformWindows: const PlatformDetails(PlatformSupport.federated, |
| variants: <String>[platformVariantWinUwp]), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--winuwp']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('BUILDING plugin/example for UWP (winuwp)'), |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| containsAll(<ProcessCall>[ |
| ProcessCall(getFlutterCommand(mockPlatform), |
| const <String>['build', 'winuwp'], pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test('building for UWP creates a folder if necessary', () async { |
| final Directory pluginDirectory = |
| createFakePlugin('plugin', packagesDir, extraFiles: <String>[ |
| 'example/test', |
| ], platformSupport: <String, PlatformDetails>{ |
| platformWindows: const PlatformDetails(PlatformSupport.federated, |
| variants: <String>[platformVariantWinUwp]), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--winuwp']); |
| |
| expect( |
| output, |
| contains('Creating temporary winuwp folder'), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>['create', '--platforms=winuwp', '.'], |
| pluginExampleDirectory.path), |
| ProcessCall(getFlutterCommand(mockPlatform), |
| const <String>['build', 'winuwp'], pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test( |
| 'building for Android when plugin is not set up for Android results in no-op', |
| () async { |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = |
| await runCapturingPrint(runner, <String>['build-examples', '--apk']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('Android is not supported by this plugin'), |
| ]), |
| ); |
| |
| // Output should be empty since running build-examples --macos with no macos |
| // implementation is a no-op. |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('building for Android', () async { |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'build-examples', |
| '--apk', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<String>[ |
| '\nBUILDING plugin/example for Android (apk)', |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall(getFlutterCommand(mockPlatform), |
| const <String>['build', 'apk'], pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test('enable-experiment flag for Android', () async { |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| await runCapturingPrint(runner, |
| <String>['build-examples', '--apk', '--enable-experiment=exp1']); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>['build', 'apk', '--enable-experiment=exp1'], |
| pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test('enable-experiment flag for ios', () async { |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| await runCapturingPrint(runner, |
| <String>['build-examples', '--ios', '--enable-experiment=exp1']); |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>[ |
| 'build', |
| 'ios', |
| '--no-codesign', |
| '--enable-experiment=exp1' |
| ], |
| pluginExampleDirectory.path), |
| ])); |
| }); |
| |
| test('logs skipped platforms', () async { |
| createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--apk', '--ios', '--macos']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Skipping unsupported platform(s): iOS, macOS'), |
| ]), |
| ); |
| }); |
| |
| group('packages', () { |
| test('builds when requested platform is supported by example', () async { |
| final Directory packageDirectory = createFakePackage( |
| 'package', packagesDir, isFlutter: true, extraFiles: <String>[ |
| 'example/ios/Runner.xcodeproj/project.pbxproj' |
| ]); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--ios']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for package'), |
| contains('BUILDING package/example for iOS'), |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>[ |
| 'build', |
| 'ios', |
| '--no-codesign', |
| ], |
| packageDirectory.childDirectory('example').path), |
| ])); |
| }); |
| |
| test('skips non-Flutter examples', () async { |
| createFakePackage('package', packagesDir, isFlutter: false); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--ios']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for package'), |
| contains('No examples found supporting requested platform(s).'), |
| ]), |
| ); |
| |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('skips when there is no example', () async { |
| createFakePackage('package', packagesDir, |
| isFlutter: true, examples: <String>[]); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--ios']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for package'), |
| contains('No examples found supporting requested platform(s).'), |
| ]), |
| ); |
| |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('skip when example does not support requested platform', () async { |
| createFakePackage('package', packagesDir, |
| isFlutter: true, |
| extraFiles: <String>['example/linux/CMakeLists.txt']); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--ios']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for package'), |
| contains('Skipping iOS for package/example; not supported.'), |
| contains('No examples found supporting requested platform(s).'), |
| ]), |
| ); |
| |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('logs skipped platforms when only some are supported', () async { |
| final Directory packageDirectory = createFakePackage( |
| 'package', packagesDir, |
| isFlutter: true, |
| extraFiles: <String>['example/linux/CMakeLists.txt']); |
| |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['build-examples', '--apk', '--linux']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for package'), |
| contains('Building for: Android, Linux'), |
| contains('Skipping Android for package/example; not supported.'), |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>['build', 'linux'], |
| packageDirectory.childDirectory('example').path), |
| ])); |
| }); |
| }); |
| |
| test('The .pluginToolsConfig.yaml file', () async { |
| mockPlatform.isLinux = true; |
| final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformLinux: const PlatformDetails(PlatformSupport.inline), |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }); |
| |
| final Directory pluginExampleDirectory = |
| pluginDirectory.childDirectory('example'); |
| |
| final File pluginExampleConfigFile = |
| pluginExampleDirectory.childFile('.pluginToolsConfig.yaml'); |
| pluginExampleConfigFile |
| .writeAsStringSync('buildFlags:\n global:\n - "test argument"'); |
| |
| final List<String> output = <String>[ |
| ...await runCapturingPrint( |
| runner, <String>['build-examples', '--linux']), |
| ...await runCapturingPrint( |
| runner, <String>['build-examples', '--macos']), |
| ]; |
| |
| expect( |
| output, |
| containsAllInOrder(<String>[ |
| '\nBUILDING plugin/example for Linux', |
| '\nBUILDING plugin/example for macOS', |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>['build', 'linux', 'test argument'], |
| pluginExampleDirectory.path), |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>['build', 'macos', 'test argument'], |
| pluginExampleDirectory.path), |
| ])); |
| }); |
| }); |
| } |