| // 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' show Directory, File, Process, ProcessSignal; | |
| import '../framework/devices.dart'; | |
| import '../framework/framework.dart'; | |
| import '../framework/task_result.dart'; | |
| import '../framework/utils.dart'; | |
| const String _messagePrefix = 'entrypoint:'; | |
| const String _entrypointName = 'entrypoint'; | |
| const String _dartCode = | |
| ''' | |
| import 'package:flutter/widgets.dart'; | |
| @pragma('vm:entry-point') | |
| void main() { | |
| print('$_messagePrefix main'); | |
| runApp(const ColoredBox(color: Color(0xffcc0000))); | |
| } | |
| @pragma('vm:entry-point') | |
| void $_entrypointName() { | |
| print('$_messagePrefix $_entrypointName'); | |
| runApp(const ColoredBox(color: Color(0xff00cc00))); | |
| } | |
| '''; | |
| const String _kotlinCode = | |
| ''' | |
| package com.example.entrypoint_dart_registrant | |
| import io.flutter.embedding.android.FlutterActivity | |
| class MainActivity: FlutterActivity() { | |
| override fun getDartEntrypointFunctionName(): String { | |
| return "$_entrypointName" | |
| } | |
| } | |
| '''; | |
| Future<TaskResult> _runWithTempDir(Directory tempDir) async { | |
| const testDirName = 'entrypoint_dart_registrant'; | |
| final testPath = '${tempDir.path}/$testDirName'; | |
| await inDirectory(tempDir, () async { | |
| await flutter('create', options: <String>['--platforms', 'android', testDirName]); | |
| }); | |
| final mainPath = '${tempDir.path}/$testDirName/lib/main.dart'; | |
| print(mainPath); | |
| File(mainPath).writeAsStringSync(_dartCode); | |
| final activityPath = | |
| '${tempDir.path}/$testDirName/android/app/src/main/kotlin/com/example/entrypoint_dart_registrant/MainActivity.kt'; | |
| File(activityPath).writeAsStringSync(_kotlinCode); | |
| final Device device = await devices.workingDevice; | |
| await device.unlock(); | |
| final String entrypoint = await inDirectory(testPath, () async { | |
| // The problem only manifested when the dart plugin registrant was used | |
| // (which path_provider has). | |
| await flutter('pub', options: <String>['add', 'path_provider:2.0.9']); | |
| // The problem only manifested on release builds, so we test release. | |
| final Process process = await startFlutter('run', options: <String>['--release']); | |
| final completer = Completer<String>(); | |
| final StreamSubscription<String> stdoutSub = process.stdout | |
| .transform<String>(const Utf8Decoder()) | |
| .transform<String>(const LineSplitter()) | |
| .listen((String line) async { | |
| print(line); | |
| if (line.contains(_messagePrefix)) { | |
| completer.complete(line); | |
| } | |
| }); | |
| final StreamSubscription<String> stderrSub = process.stderr | |
| .transform<String>(const Utf8Decoder()) | |
| .transform<String>(const LineSplitter()) | |
| .listen((String line) async { | |
| print(line); | |
| }); | |
| final Object result = await Future.any(<Future<Object>>[completer.future, process.exitCode]); | |
| if (result is int) { | |
| throw Exception('flutter run failed, exitCode=$result'); | |
| } | |
| final entrypoint = result as String; | |
| await stdoutSub.cancel(); | |
| await stderrSub.cancel(); | |
| process.stdin.write('q'); | |
| await process.stdin.flush(); | |
| process.kill(ProcessSignal.sigint); | |
| return entrypoint; | |
| }); | |
| if (entrypoint.contains('$_messagePrefix $_entrypointName')) { | |
| return TaskResult.success(null); | |
| } else { | |
| return TaskResult.failure('expected entrypoint:"$_entrypointName" but found:"$entrypoint"'); | |
| } | |
| } | |
| /// Asserts that the custom entrypoint works in the presence of the dart plugin | |
| /// registrant. | |
| TaskFunction entrypointDartRegistrant() { | |
| return () async { | |
| final Directory tempDir = Directory.systemTemp.createTempSync('entrypoint_dart_registrant.'); | |
| try { | |
| return await _runWithTempDir(tempDir); | |
| } finally { | |
| rmTree(tempDir); | |
| } | |
| }; | |
| } |