| // 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 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:path/path.dart' as path; |
| |
| import '../framework/framework.dart'; |
| import '../framework/task_result.dart'; |
| import '../framework/utils.dart'; |
| |
| TaskFunction dartPluginRegistryTest({ |
| String? deviceIdOverride, |
| Map<String, String>? environment, |
| }) { |
| final Directory tempDir = Directory.systemTemp |
| .createTempSync('flutter_devicelab_dart_plugin_test.'); |
| return () async { |
| try { |
| section('Create implementation plugin'); |
| await inDirectory(tempDir, () async { |
| await flutter( |
| 'create', |
| options: <String>[ |
| '--template=plugin', |
| '--org', |
| 'io.flutter.devicelab', |
| '--platforms', |
| 'macos', |
| 'aplugin_platform_implementation', |
| ], |
| environment: environment, |
| ); |
| }); |
| |
| final File pluginMain = File(path.join( |
| tempDir.absolute.path, |
| 'aplugin_platform_implementation', |
| 'lib', |
| 'aplugin_platform_implementation.dart', |
| )); |
| if (!pluginMain.existsSync()) { |
| return TaskResult.failure('${pluginMain.path} does not exist'); |
| } |
| |
| // Patch plugin main dart file. |
| await pluginMain.writeAsString(''' |
| class ApluginPlatformInterfaceMacOS { |
| static void registerWith() { |
| print('ApluginPlatformInterfaceMacOS.registerWith() was called'); |
| } |
| } |
| ''', flush: true); |
| |
| // Patch plugin main pubspec file. |
| final File pluginImplPubspec = File(path.join( |
| tempDir.absolute.path, |
| 'aplugin_platform_implementation', |
| 'pubspec.yaml', |
| )); |
| String pluginImplPubspecContent = await pluginImplPubspec.readAsString(); |
| pluginImplPubspecContent = pluginImplPubspecContent.replaceFirst( |
| ' pluginClass: ApluginPlatformImplementationPlugin', |
| ' pluginClass: ApluginPlatformImplementationPlugin\n' |
| ' dartPluginClass: ApluginPlatformInterfaceMacOS\n', |
| ); |
| pluginImplPubspecContent = pluginImplPubspecContent.replaceFirst( |
| ' platforms:\n', |
| ' implements: aplugin_platform_interface\n' |
| ' platforms:\n'); |
| await pluginImplPubspec.writeAsString(pluginImplPubspecContent, |
| flush: true); |
| |
| section('Create interface plugin'); |
| await inDirectory(tempDir, () async { |
| await flutter( |
| 'create', |
| options: <String>[ |
| '--template=plugin', |
| '--org', |
| 'io.flutter.devicelab', |
| '--platforms', |
| 'macos', |
| 'aplugin_platform_interface', |
| ], |
| environment: environment, |
| ); |
| }); |
| final File pluginInterfacePubspec = File(path.join( |
| tempDir.absolute.path, |
| 'aplugin_platform_interface', |
| 'pubspec.yaml', |
| )); |
| String pluginInterfacePubspecContent = |
| await pluginInterfacePubspec.readAsString(); |
| pluginInterfacePubspecContent = |
| pluginInterfacePubspecContent.replaceFirst( |
| ' pluginClass: ApluginPlatformInterfacePlugin', |
| ' default_package: aplugin_platform_implementation\n'); |
| pluginInterfacePubspecContent = |
| pluginInterfacePubspecContent.replaceFirst( |
| 'dependencies:', |
| 'dependencies:\n' |
| ' aplugin_platform_implementation:\n' |
| ' path: ../aplugin_platform_implementation\n'); |
| await pluginInterfacePubspec.writeAsString(pluginInterfacePubspecContent, |
| flush: true); |
| |
| section('Create app'); |
| |
| await inDirectory(tempDir, () async { |
| await flutter( |
| 'create', |
| options: <String>[ |
| '--template=app', |
| '--org', |
| 'io.flutter.devicelab', |
| '--platforms', |
| 'macos', |
| 'app', |
| ], |
| environment: environment, |
| ); |
| }); |
| |
| final File appPubspec = File(path.join( |
| tempDir.absolute.path, |
| 'app', |
| 'pubspec.yaml', |
| )); |
| String appPubspecContent = await appPubspec.readAsString(); |
| appPubspecContent = appPubspecContent.replaceFirst( |
| 'dependencies:', |
| 'dependencies:\n' |
| ' aplugin_platform_interface:\n' |
| ' path: ../aplugin_platform_interface\n'); |
| await appPubspec.writeAsString(appPubspecContent, flush: true); |
| |
| section('Flutter run for macos'); |
| |
| late Process run; |
| await inDirectory(path.join(tempDir.path, 'app'), () async { |
| run = await startProcess( |
| path.join(flutterDirectory.path, 'bin', 'flutter'), |
| flutterCommandArgs('run', <String>['-d', 'macos', '-v']), |
| ); |
| }); |
| |
| Completer<void> registryExecutedCompleter = Completer<void>(); |
| final StreamSubscription<void> stdoutSub = run.stdout |
| .transform<String>(utf8.decoder) |
| .transform<String>(const LineSplitter()) |
| .listen((String line) { |
| if (line.contains('ApluginPlatformInterfaceMacOS.registerWith() was called')) { |
| registryExecutedCompleter.complete(); |
| } |
| print('stdout: $line'); |
| }); |
| |
| final StreamSubscription<void> stderrSub = run.stderr |
| .transform<String>(utf8.decoder) |
| .transform<String>(const LineSplitter()) |
| .listen((String line) { |
| print('stderr: $line'); |
| }); |
| |
| final Future<void> stdoutDone = stdoutSub.asFuture<void>(); |
| final Future<void> stderrDone = stderrSub.asFuture<void>(); |
| |
| Future<void> waitForStreams() { |
| return Future.wait<void>(<Future<void>>[stdoutDone, stderrDone]); |
| } |
| |
| Future<void> waitOrExit(Future<void> future) async { |
| final dynamic result = await Future.any<dynamic>( |
| <Future<dynamic>>[ |
| future, |
| run.exitCode, |
| ], |
| ); |
| if (result is int) { |
| await waitForStreams(); |
| throw 'process exited with code $result'; |
| } |
| } |
| |
| section('Wait for registry execution'); |
| await waitOrExit(registryExecutedCompleter.future); |
| |
| // Hot restart. |
| run.stdin.write('R'); |
| await run.stdin.flush(); |
| await run.stdin.close(); |
| |
| registryExecutedCompleter = Completer<void>(); |
| section('Wait for registry execution after hot restart'); |
| await waitOrExit(registryExecutedCompleter.future); |
| |
| run.kill(); |
| |
| section('Wait for stdout/stderr streams'); |
| await waitForStreams(); |
| |
| unawaited(stdoutSub.cancel()); |
| unawaited(stderrSub.cancel()); |
| |
| return TaskResult.success(null); |
| } finally { |
| rmTree(tempDir); |
| } |
| }; |
| } |