| // 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. |
| |
| import 'dart:async'; |
| |
| import 'package:file/file.dart'; |
| import 'package:file/memory.dart'; |
| import 'package:file_testing/file_testing.dart'; |
| import 'package:flutter_tools/src/base/logger.dart'; |
| import 'package:flutter_tools/src/build_info.dart'; |
| import 'package:flutter_tools/src/build_system/build_system.dart'; |
| import 'package:flutter_tools/src/bundle.dart'; |
| import 'package:flutter_tools/src/bundle_builder.dart'; |
| import 'package:flutter_tools/src/convert.dart'; |
| import 'package:flutter_tools/src/custom_devices/custom_device.dart'; |
| import 'package:flutter_tools/src/custom_devices/custom_device_config.dart'; |
| import 'package:flutter_tools/src/custom_devices/custom_devices_config.dart'; |
| import 'package:flutter_tools/src/device.dart'; |
| import 'package:flutter_tools/src/globals.dart' as globals; |
| import 'package:flutter_tools/src/linux/application_package.dart'; |
| import 'package:flutter_tools/src/project.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:test/fake.dart'; |
| |
| import '../../src/common.dart'; |
| import '../../src/context.dart'; |
| import '../../src/fakes.dart'; |
| |
| |
| void _writeCustomDevicesConfigFile(Directory dir, List<CustomDeviceConfig> configs) { |
| dir.createSync(); |
| |
| final File file = dir.childFile('.flutter_custom_devices.json'); |
| file.writeAsStringSync(jsonEncode( |
| <String, dynamic>{ |
| 'custom-devices': configs.map<dynamic>((CustomDeviceConfig c) => c.toJson()).toList(), |
| } |
| )); |
| } |
| |
| FlutterProject _setUpFlutterProject(Directory directory) { |
| final FlutterProjectFactory flutterProjectFactory = FlutterProjectFactory( |
| fileSystem: directory.fileSystem, |
| logger: BufferLogger.test(), |
| ); |
| return flutterProjectFactory.fromDirectory(directory); |
| } |
| |
| void main() { |
| testWithoutContext('replacing string interpolation occurrences in custom device commands', () async { |
| expect( |
| interpolateCommand( |
| <String>['scp', r'${localPath}', r'/tmp/${appName}', 'pi@raspberrypi'], |
| <String, String>{ |
| 'localPath': 'build/flutter_assets', |
| 'appName': 'hello_world', |
| }, |
| ), |
| <String>[ |
| 'scp', 'build/flutter_assets', '/tmp/hello_world', 'pi@raspberrypi', |
| ] |
| ); |
| |
| expect( |
| interpolateCommand( |
| <String>[r'${test1}', r' ${test2}', r'${test3}'], |
| <String, String>{ |
| 'test1': '_test1', |
| 'test2': '_test2', |
| }, |
| ), |
| <String>[ |
| '_test1', ' _test2', r'', |
| ] |
| ); |
| |
| expect( |
| interpolateCommand( |
| <String>[r'${test1}', r' ${test2}', r'${test3}'], |
| <String, String>{ |
| 'test1': '_test1', |
| 'test2': '_test2', |
| }, |
| additionalReplacementValues: <String, String>{ |
| 'test2': '_nottest2', |
| 'test3': '_test3', |
| } |
| ), |
| <String>[ |
| '_test1', ' _test2', r'_test3', |
| ] |
| ); |
| }); |
| |
| final CustomDeviceConfig testConfig = CustomDeviceConfig( |
| id: 'testid', |
| label: 'testlabel', |
| sdkNameAndVersion: 'testsdknameandversion', |
| enabled: true, |
| pingCommand: const <String>['testping'], |
| pingSuccessRegex: RegExp('testpingsuccess'), |
| postBuildCommand: const <String>['testpostbuild'], |
| installCommand: const <String>['testinstall'], |
| uninstallCommand: const <String>['testuninstall'], |
| runDebugCommand: const <String>['testrundebug'], |
| forwardPortCommand: const <String>['testforwardport'], |
| forwardPortSuccessRegex: RegExp('testforwardportsuccess'), |
| screenshotCommand: const <String>['testscreenshot'], |
| ); |
| |
| const String testConfigPingSuccessOutput = 'testpingsuccess\n'; |
| const String testConfigForwardPortSuccessOutput = 'testforwardportsuccess\n'; |
| final CustomDeviceConfig disabledTestConfig = testConfig.copyWith(enabled: false); |
| final CustomDeviceConfig testConfigNonForwarding = testConfig.copyWith( |
| explicitForwardPortCommand: true, |
| explicitForwardPortSuccessRegex: true, |
| ); |
| |
| testUsingContext('CustomDevice defaults', |
| () async { |
| final CustomDevice device = CustomDevice( |
| config: testConfig, |
| processManager: FakeProcessManager.any(), |
| logger: BufferLogger.test() |
| ); |
| |
| final PrebuiltLinuxApp linuxApp = PrebuiltLinuxApp(executable: 'foo'); |
| |
| expect(device.id, 'testid'); |
| expect(device.name, 'testlabel'); |
| expect(device.platformType, PlatformType.custom); |
| expect(await device.sdkNameAndVersion, 'testsdknameandversion'); |
| expect(await device.targetPlatform, TargetPlatform.linux_arm64); |
| expect(await device.installApp(linuxApp), true); |
| expect(await device.uninstallApp(linuxApp), true); |
| expect(await device.isLatestBuildInstalled(linuxApp), false); |
| expect(await device.isAppInstalled(linuxApp), false); |
| expect(await device.stopApp(linuxApp), false); |
| expect(await device.stopApp(null), false); |
| expect(device.category, Category.mobile); |
| |
| expect(device.supportsRuntimeMode(BuildMode.debug), true); |
| expect(device.supportsRuntimeMode(BuildMode.profile), false); |
| expect(device.supportsRuntimeMode(BuildMode.release), false); |
| expect(device.supportsRuntimeMode(BuildMode.jitRelease), false); |
| }, |
| overrides: <Type, dynamic Function()>{ |
| FileSystem: () => MemoryFileSystem.test(), |
| ProcessManager: () => FakeProcessManager.any(), |
| } |
| ); |
| |
| testWithoutContext('CustomDevice: no devices listed if only disabled devices configured', () async { |
| final MemoryFileSystem fs = MemoryFileSystem.test(); |
| final Directory dir = fs.directory('custom_devices_config_dir'); |
| |
| _writeCustomDevicesConfigFile(dir, <CustomDeviceConfig>[disabledTestConfig]); |
| |
| expect(await CustomDevices( |
| featureFlags: TestFeatureFlags(areCustomDevicesEnabled: true), |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.any(), |
| config: CustomDevicesConfig.test( |
| fileSystem: fs, |
| directory: dir, |
| logger: BufferLogger.test() |
| ) |
| ).devices, <Device>[]); |
| }); |
| |
| testWithoutContext('CustomDevice: no devices listed if custom devices feature flag disabled', () async { |
| final MemoryFileSystem fs = MemoryFileSystem.test(); |
| final Directory dir = fs.directory('custom_devices_config_dir'); |
| |
| _writeCustomDevicesConfigFile(dir, <CustomDeviceConfig>[testConfig]); |
| |
| expect(await CustomDevices( |
| featureFlags: TestFeatureFlags(), |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.any(), |
| config: CustomDevicesConfig.test( |
| fileSystem: fs, |
| directory: dir, |
| logger: BufferLogger.test() |
| ) |
| ).devices, <Device>[]); |
| }); |
| |
| testWithoutContext('CustomDevices.devices', () async { |
| final MemoryFileSystem fs = MemoryFileSystem.test(); |
| final Directory dir = fs.directory('custom_devices_config_dir'); |
| |
| _writeCustomDevicesConfigFile(dir, <CustomDeviceConfig>[testConfig]); |
| |
| expect( |
| await CustomDevices( |
| featureFlags: TestFeatureFlags(areCustomDevicesEnabled: true), |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: testConfig.pingCommand, |
| stdout: testConfigPingSuccessOutput |
| ), |
| ]), |
| config: CustomDevicesConfig.test( |
| fileSystem: fs, |
| directory: dir, |
| logger: BufferLogger.test() |
| ) |
| ).devices, |
| hasLength(1) |
| ); |
| }); |
| |
| testWithoutContext('CustomDevices.discoverDevices successfully discovers devices and executes ping command', () async { |
| final MemoryFileSystem fs = MemoryFileSystem.test(); |
| final Directory dir = fs.directory('custom_devices_config_dir'); |
| |
| _writeCustomDevicesConfigFile(dir, <CustomDeviceConfig>[testConfig]); |
| |
| bool pingCommandWasExecuted = false; |
| |
| final CustomDevices discovery = CustomDevices( |
| featureFlags: TestFeatureFlags(areCustomDevicesEnabled: true), |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: testConfig.pingCommand, |
| onRun: () => pingCommandWasExecuted = true, |
| stdout: testConfigPingSuccessOutput |
| ), |
| ]), |
| config: CustomDevicesConfig.test( |
| fileSystem: fs, |
| directory: dir, |
| logger: BufferLogger.test(), |
| ), |
| ); |
| |
| final List<Device> discoveredDevices = await discovery.discoverDevices(); |
| |
| expect(discoveredDevices, hasLength(1)); |
| expect(pingCommandWasExecuted, true); |
| }); |
| |
| testWithoutContext("CustomDevices.discoverDevices doesn't report device when ping command fails", () async { |
| final MemoryFileSystem fs = MemoryFileSystem.test(); |
| final Directory dir = fs.directory('custom_devices_config_dir'); |
| |
| _writeCustomDevicesConfigFile(dir, <CustomDeviceConfig>[testConfig]); |
| |
| final CustomDevices discovery = CustomDevices( |
| featureFlags: TestFeatureFlags(areCustomDevicesEnabled: true), |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: testConfig.pingCommand, |
| stdout: testConfigPingSuccessOutput, |
| exitCode: 1 |
| ), |
| ]), |
| config: CustomDevicesConfig.test( |
| fileSystem: fs, |
| directory: dir, |
| logger: BufferLogger.test(), |
| ), |
| ); |
| |
| expect(await discovery.discoverDevices(), hasLength(0)); |
| }); |
| |
| testWithoutContext("CustomDevices.discoverDevices doesn't report device when ping command output doesn't match ping success regex", () async { |
| final MemoryFileSystem fs = MemoryFileSystem.test(); |
| final Directory dir = fs.directory('custom_devices_config_dir'); |
| |
| _writeCustomDevicesConfigFile(dir, <CustomDeviceConfig>[testConfig]); |
| |
| final CustomDevices discovery = CustomDevices( |
| featureFlags: TestFeatureFlags(areCustomDevicesEnabled: true), |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: testConfig.pingCommand, |
| ), |
| ]), |
| config: CustomDevicesConfig.test( |
| fileSystem: fs, |
| directory: dir, |
| logger: BufferLogger.test(), |
| ), |
| ); |
| |
| expect(await discovery.discoverDevices(), hasLength(0)); |
| }); |
| |
| testWithoutContext('CustomDevice.isSupportedForProject is true with editable host app', () async { |
| final MemoryFileSystem fileSystem = MemoryFileSystem.test(); |
| fileSystem.file('pubspec.yaml').createSync(); |
| fileSystem.file('.packages').createSync(); |
| |
| final FlutterProject flutterProject = _setUpFlutterProject(fileSystem.currentDirectory); |
| |
| expect(CustomDevice( |
| config: testConfig, |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.any(), |
| ).isSupportedForProject(flutterProject), true); |
| }); |
| |
| testUsingContext( |
| 'CustomDevice.install invokes uninstall and install command', |
| () async { |
| bool bothCommandsWereExecuted = false; |
| |
| final CustomDevice device = CustomDevice( |
| config: testConfig, |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand(command: testConfig.uninstallCommand), |
| FakeCommand(command: testConfig.installCommand, onRun: () => bothCommandsWereExecuted = true), |
| ]) |
| ); |
| |
| expect(await device.installApp(PrebuiltLinuxApp(executable: 'exe')), true); |
| expect(bothCommandsWereExecuted, true); |
| }, |
| overrides: <Type, dynamic Function()>{ |
| FileSystem: () => MemoryFileSystem.test(), |
| ProcessManager: () => FakeProcessManager.any(), |
| } |
| ); |
| |
| testWithoutContext('CustomDevicePortForwarder will run and terminate forwardPort command', () async { |
| final Completer<void> forwardPortCommandCompleter = Completer<void>(); |
| |
| final CustomDevicePortForwarder forwarder = CustomDevicePortForwarder( |
| deviceName: 'testdevicename', |
| forwardPortCommand: testConfig.forwardPortCommand!, |
| forwardPortSuccessRegex: testConfig.forwardPortSuccessRegex!, |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: testConfig.forwardPortCommand!, |
| stdout: testConfigForwardPortSuccessOutput, |
| completer: forwardPortCommandCompleter, |
| ), |
| ]) |
| ); |
| |
| // this should start the command |
| expect(await forwarder.forward(12345), 12345); |
| expect(forwardPortCommandCompleter.isCompleted, false); |
| |
| // this should terminate it |
| await forwarder.dispose(); |
| |
| // the termination should have completed our completer |
| expect(forwardPortCommandCompleter.isCompleted, true); |
| }); |
| |
| testWithoutContext('CustomDevice forwards observatory port correctly when port forwarding is configured', () async { |
| final Completer<void> runDebugCompleter = Completer<void>(); |
| final Completer<void> forwardPortCompleter = Completer<void>(); |
| |
| final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: testConfig.runDebugCommand, |
| completer: runDebugCompleter, |
| stdout: 'The Dart VM service is listening on http://127.0.0.1:12345/abcd/\n', |
| ), |
| FakeCommand( |
| command: testConfig.forwardPortCommand!, |
| completer: forwardPortCompleter, |
| stdout: testConfigForwardPortSuccessOutput, |
| ), |
| ]); |
| |
| final CustomDeviceAppSession appSession = CustomDeviceAppSession( |
| name: 'testname', |
| device: CustomDevice( |
| config: testConfig, |
| logger: BufferLogger.test(), |
| processManager: processManager |
| ), |
| appPackage: PrebuiltLinuxApp(executable: 'testexecutable'), |
| logger: BufferLogger.test(), |
| processManager: processManager, |
| ); |
| |
| final LaunchResult launchResult = await appSession.start(debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug)); |
| |
| expect(launchResult.started, true); |
| expect(launchResult.observatoryUri, Uri.parse('http://127.0.0.1:12345/abcd/')); |
| expect(runDebugCompleter.isCompleted, false); |
| expect(forwardPortCompleter.isCompleted, false); |
| |
| expect(await appSession.stop(), true); |
| expect(runDebugCompleter.isCompleted, true); |
| expect(forwardPortCompleter.isCompleted, true); |
| }); |
| |
| testWithoutContext('CustomDeviceAppSession forwards observatory port correctly when port forwarding is not configured', () async { |
| final Completer<void> runDebugCompleter = Completer<void>(); |
| |
| final FakeProcessManager processManager = FakeProcessManager.list( |
| <FakeCommand>[ |
| FakeCommand( |
| command: testConfigNonForwarding.runDebugCommand, |
| completer: runDebugCompleter, |
| stdout: 'The Dart VM service is listening on http://192.168.178.123:12345/abcd/\n' |
| ), |
| ] |
| ); |
| |
| final CustomDeviceAppSession appSession = CustomDeviceAppSession( |
| name: 'testname', |
| device: CustomDevice( |
| config: testConfigNonForwarding, |
| logger: BufferLogger.test(), |
| processManager: processManager |
| ), |
| appPackage: PrebuiltLinuxApp(executable: 'testexecutable'), |
| logger: BufferLogger.test(), |
| processManager: processManager |
| ); |
| |
| final LaunchResult launchResult = await appSession.start(debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug)); |
| |
| expect(launchResult.started, true); |
| expect(launchResult.observatoryUri, Uri.parse('http://192.168.178.123:12345/abcd/')); |
| expect(runDebugCompleter.isCompleted, false); |
| |
| expect(await appSession.stop(), true); |
| expect(runDebugCompleter.isCompleted, true); |
| }); |
| |
| testUsingContext( |
| 'custom device end-to-end test', |
| () async { |
| final Completer<void> runDebugCompleter = Completer<void>(); |
| final Completer<void> forwardPortCompleter = Completer<void>(); |
| |
| final FakeProcessManager processManager = FakeProcessManager.list( |
| <FakeCommand>[ |
| FakeCommand( |
| command: testConfig.pingCommand, |
| stdout: testConfigPingSuccessOutput |
| ), |
| FakeCommand(command: testConfig.postBuildCommand!), |
| FakeCommand(command: testConfig.uninstallCommand), |
| FakeCommand(command: testConfig.installCommand), |
| FakeCommand( |
| command: testConfig.runDebugCommand, |
| completer: runDebugCompleter, |
| stdout: 'The Dart VM service is listening on http://127.0.0.1:12345/abcd/\n', |
| ), |
| FakeCommand( |
| command: testConfig.forwardPortCommand!, |
| completer: forwardPortCompleter, |
| stdout: testConfigForwardPortSuccessOutput, |
| ), |
| ] |
| ); |
| |
| // Reuse our filesystem from context instead of mixing two filesystem instances |
| // together |
| final FileSystem fs = globals.fs; |
| |
| // CustomDevice.startApp doesn't care whether we pass a prebuilt app or |
| // buildable app as long as we pass prebuiltApplication as false |
| final PrebuiltLinuxApp app = PrebuiltLinuxApp(executable: 'testexecutable'); |
| |
| final Directory configFileDir = fs.directory('custom_devices_config_dir'); |
| _writeCustomDevicesConfigFile(configFileDir, <CustomDeviceConfig>[testConfig]); |
| |
| // finally start actually testing things |
| final CustomDevices customDevices = CustomDevices( |
| featureFlags: TestFeatureFlags(areCustomDevicesEnabled: true), |
| processManager: processManager, |
| logger: BufferLogger.test(), |
| config: CustomDevicesConfig.test( |
| fileSystem: fs, |
| directory: configFileDir, |
| logger: BufferLogger.test() |
| ) |
| ); |
| |
| final List<Device> devices = await customDevices.discoverDevices(); |
| expect(devices.length, 1); |
| expect(devices.single, isA<CustomDevice>()); |
| |
| final CustomDevice device = devices.single as CustomDevice; |
| expect(device.id, testConfig.id); |
| expect(device.name, testConfig.label); |
| expect(await device.sdkNameAndVersion, testConfig.sdkNameAndVersion); |
| |
| final LaunchResult result = await device.startApp( |
| app, |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| bundleBuilder: FakeBundleBuilder() |
| ); |
| expect(result.started, true); |
| expect(result.hasObservatory, true); |
| expect(result.observatoryUri, Uri.tryParse('http://127.0.0.1:12345/abcd/')); |
| expect(runDebugCompleter.isCompleted, false); |
| expect(forwardPortCompleter.isCompleted, false); |
| |
| expect(await device.stopApp(app), true); |
| expect(runDebugCompleter.isCompleted, true); |
| expect(forwardPortCompleter.isCompleted, true); |
| }, |
| overrides: <Type, Generator>{ |
| FileSystem: () => MemoryFileSystem.test(), |
| ProcessManager: () => FakeProcessManager.any(), |
| } |
| ); |
| |
| testWithoutContext('CustomDevice screenshotting', () async { |
| bool screenshotCommandWasExecuted = false; |
| |
| final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: testConfig.screenshotCommand!, |
| onRun: () => screenshotCommandWasExecuted = true, |
| ), |
| ]); |
| |
| final MemoryFileSystem fs = MemoryFileSystem.test(); |
| final File screenshotFile = fs.file('screenshot.png'); |
| |
| final CustomDevice device = CustomDevice( |
| config: testConfig, |
| logger: BufferLogger.test(), |
| processManager: processManager |
| ); |
| |
| expect(device.supportsScreenshot, true); |
| |
| await device.takeScreenshot(screenshotFile); |
| expect(screenshotCommandWasExecuted, true); |
| expect(screenshotFile, exists); |
| }); |
| |
| testWithoutContext('CustomDevice without screenshotting support', () async { |
| bool screenshotCommandWasExecuted = false; |
| |
| final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[ |
| FakeCommand( |
| command: testConfig.screenshotCommand!, |
| onRun: () => screenshotCommandWasExecuted = true, |
| ), |
| ]); |
| |
| final MemoryFileSystem fs = MemoryFileSystem.test(); |
| final File screenshotFile = fs.file('screenshot.png'); |
| |
| final CustomDevice device = CustomDevice( |
| config: testConfig.copyWith( |
| explicitScreenshotCommand: true |
| ), |
| logger: BufferLogger.test(), |
| processManager: processManager |
| ); |
| |
| expect(device.supportsScreenshot, false); |
| expect( |
| () => device.takeScreenshot(screenshotFile), |
| throwsA(const TypeMatcher<UnsupportedError>()), |
| ); |
| expect(screenshotCommandWasExecuted, false); |
| expect(screenshotFile.existsSync(), false); |
| }); |
| |
| testWithoutContext('CustomDevice returns correct target platform', () async { |
| final CustomDevice device = CustomDevice( |
| config: testConfig.copyWith( |
| platform: TargetPlatform.linux_x64 |
| ), |
| logger: BufferLogger.test(), |
| processManager: FakeProcessManager.empty() |
| ); |
| |
| expect(await device.targetPlatform, TargetPlatform.linux_x64); |
| }); |
| |
| testWithoutContext('CustomDeviceLogReader cancels subscriptions before closing logLines stream', () async { |
| final CustomDeviceLogReader logReader = CustomDeviceLogReader('testname'); |
| |
| final Iterable<List<int>> lines = Iterable<List<int>>.generate(5, (int _) => utf8.encode('test')); |
| |
| logReader.listenToProcessOutput( |
| FakeProcess( |
| exitCode: Future<int>.value(0), |
| stdout: Stream<List<int>>.fromIterable(lines), |
| stderr: Stream<List<int>>.fromIterable(lines), |
| ), |
| ); |
| |
| final List<MyFakeStreamSubscription<String>> subscriptions = <MyFakeStreamSubscription<String>>[]; |
| bool logLinesStreamDone = false; |
| logReader.logLines.listen((_) {}, onDone: () { |
| expect(subscriptions, everyElement((MyFakeStreamSubscription<String> s) => s.canceled)); |
| logLinesStreamDone = true; |
| }); |
| |
| logReader.subscriptions.replaceRange( |
| 0, |
| logReader.subscriptions.length, |
| logReader.subscriptions.map( |
| (StreamSubscription<String> e) => MyFakeStreamSubscription<String>(e) |
| ), |
| ); |
| |
| subscriptions.addAll(logReader.subscriptions.cast()); |
| |
| await logReader.dispose(); |
| |
| expect(logLinesStreamDone, true); |
| }); |
| } |
| |
| class MyFakeStreamSubscription<T> extends Fake implements StreamSubscription<T> { |
| MyFakeStreamSubscription(this.parent); |
| |
| StreamSubscription<T> parent; |
| bool canceled = false; |
| |
| @override |
| Future<void> cancel() { |
| canceled = true; |
| return parent.cancel(); |
| } |
| } |
| |
| class FakeBundleBuilder extends Fake implements BundleBuilder { |
| @override |
| Future<void> build({ |
| TargetPlatform? platform, |
| BuildInfo? buildInfo, |
| FlutterProject? project, |
| String? mainPath, |
| String manifestPath = defaultManifestPath, |
| String? applicationKernelFilePath, |
| String? depfilePath, |
| String? assetDirPath, |
| @visibleForTesting BuildSystem? buildSystem |
| }) async {} |
| } |