| // 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:flutter_devicelab/framework/framework.dart'; |
| import 'package:flutter_devicelab/framework/utils.dart'; |
| import 'package:path/path.dart' as path; |
| |
| /// Tests that a plugin A can depend on platform code from a plugin B |
| /// as long as plugin B is defined as a pub dependency of plugin A. |
| /// |
| /// This test fails when `flutter build apk` fails and the stderr from this command |
| /// contains "Unresolved reference: plugin_b". |
| Future<void> main() async { |
| await task(() async { |
| |
| section('Find Java'); |
| |
| final String javaHome = await findJavaHome(); |
| if (javaHome == null) { |
| return TaskResult.failure('Could not find Java'); |
| } |
| |
| print('\nUsing JAVA_HOME=$javaHome'); |
| |
| final Directory tempDir = Directory.systemTemp.createTempSync('flutter_plugin_dependencies.'); |
| try { |
| |
| section('Create plugin A'); |
| |
| final Directory pluginADirectory = Directory(path.join(tempDir.path, 'plugin_a')); |
| await inDirectory(tempDir, () async { |
| await flutter( |
| 'create', |
| options: <String>[ |
| '--org', |
| 'io.flutter.devicelab.plugin_a', |
| '--template=plugin', |
| pluginADirectory.path, |
| ], |
| ); |
| }); |
| |
| section('Create plugin B'); |
| |
| final Directory pluginBDirectory = Directory(path.join(tempDir.path, 'plugin_b')); |
| await inDirectory(tempDir, () async { |
| await flutter( |
| 'create', |
| options: <String>[ |
| '--org', |
| 'io.flutter.devicelab.plugin_b', |
| '--template=plugin', |
| pluginBDirectory.path, |
| ], |
| ); |
| }); |
| |
| section('Create plugin C without android/ directory'); |
| |
| final Directory pluginCDirectory = Directory(path.join(tempDir.path, 'plugin_c')); |
| await inDirectory(tempDir, () async { |
| await flutter( |
| 'create', |
| options: <String>[ |
| '--org', |
| 'io.flutter.devicelab.plugin_c', |
| '--template=plugin', |
| pluginCDirectory.path, |
| ], |
| ); |
| }); |
| |
| final File pluginCpubspec = File(path.join(pluginCDirectory.path, 'pubspec.yaml')); |
| await pluginCpubspec.writeAsString(''' |
| name: plugin_c |
| version: 0.0.1 |
| |
| flutter: |
| plugin: |
| platforms: |
| ios: |
| pluginClass: Plugin_cPlugin |
| |
| dependencies: |
| flutter: |
| sdk: flutter |
| |
| environment: |
| sdk: ">=2.0.0-dev.28.0 <3.0.0" |
| flutter: ">=1.5.0 <2.0.0" |
| ''', flush: true); |
| |
| section('Write dummy Kotlin code in plugin B'); |
| |
| final File pluginBKotlinClass = File(path.join( |
| pluginBDirectory.path, |
| 'android', |
| 'src', |
| 'main', |
| 'kotlin', |
| 'DummyPluginBClass.kt', |
| )); |
| |
| await pluginBKotlinClass.writeAsString(''' |
| package io.flutter.devicelab.plugin_b |
| |
| public class DummyPluginBClass { |
| companion object { |
| fun dummyStaticMethod() { |
| } |
| } |
| } |
| ''', flush: true); |
| |
| section('Make plugin A depend on plugin B and plugin C'); |
| |
| final File pluginApubspec = File(path.join(pluginADirectory.path, 'pubspec.yaml')); |
| String pluginApubspecContent = await pluginApubspec.readAsString(); |
| pluginApubspecContent = pluginApubspecContent.replaceFirst( |
| '\ndependencies:\n', |
| '\ndependencies:\n' |
| ' plugin_b:\n' |
| ' path: ${pluginBDirectory.path}\n' |
| ' plugin_c:\n' |
| ' path: ${pluginCDirectory.path}\n', |
| ); |
| await pluginApubspec.writeAsString(pluginApubspecContent, flush: true); |
| |
| section('Write Kotlin code in plugin A that references Kotlin code from plugin B'); |
| |
| final File pluginAKotlinClass = File(path.join( |
| pluginADirectory.path, |
| 'android', |
| 'src', |
| 'main', |
| 'kotlin', |
| 'DummyPluginAClass.kt', |
| )); |
| |
| await pluginAKotlinClass.writeAsString(''' |
| package io.flutter.devicelab.plugin_a |
| |
| import io.flutter.devicelab.plugin_b.DummyPluginBClass |
| |
| public class DummyPluginAClass { |
| constructor() { |
| // Call a method from plugin b. |
| DummyPluginBClass.dummyStaticMethod(); |
| } |
| } |
| ''', flush: true); |
| |
| section('Verify .flutter-plugins-dependencies'); |
| |
| final Directory exampleApp = Directory(path.join(pluginADirectory.path, 'example')); |
| |
| await inDirectory(exampleApp, () async { |
| await flutter( |
| 'packages', |
| options: <String>['get'], |
| ); |
| }); |
| |
| final File flutterPluginsDependenciesFile = |
| File(path.join(exampleApp.path, '.flutter-plugins-dependencies')); |
| |
| if (!flutterPluginsDependenciesFile.existsSync()) { |
| return TaskResult.failure('${flutterPluginsDependenciesFile.path} doesn\'t exist'); |
| } |
| |
| final String flutterPluginsDependenciesFileContent = flutterPluginsDependenciesFile.readAsStringSync(); |
| |
| final Map<String, dynamic> jsonContent = json.decode(flutterPluginsDependenciesFileContent) as Map<String, dynamic>; |
| |
| // Verify the dependencyGraph object is valid. The rest of the contents of this file are not relevant to the |
| // dependency graph and are tested by unit tests. |
| final List<dynamic> dependencyGraph = jsonContent['dependencyGraph'] as List<dynamic>; |
| const String kExpectedPluginsDependenciesContent = |
| '[' |
| '{' |
| '\"name\":\"plugin_a\",' |
| '\"dependencies\":[\"plugin_b\",\"plugin_c\"]' |
| '},' |
| '{' |
| '\"name\":\"plugin_b\",' |
| '\"dependencies\":[]' |
| '},' |
| '{' |
| '\"name\":\"plugin_c\",' |
| '\"dependencies\":[]' |
| '}' |
| ']'; |
| final String graphString = json.encode(dependencyGraph); |
| if (graphString != kExpectedPluginsDependenciesContent) { |
| return TaskResult.failure( |
| 'Unexpected file content in ${flutterPluginsDependenciesFile.path}: ' |
| 'Found "$graphString" instead of "$kExpectedPluginsDependenciesContent"' |
| ); |
| } |
| |
| section('Build plugin A example app'); |
| |
| final StringBuffer stderr = StringBuffer(); |
| await inDirectory(exampleApp, () async { |
| await evalFlutter( |
| 'build', |
| options: <String>['apk', '--target-platform', 'android-arm'], |
| canFail: true, |
| stderr: stderr, |
| ); |
| }); |
| |
| if (stderr.toString().contains('Unresolved reference: plugin_b')) { |
| return TaskResult.failure('plugin_a cannot reference plugin_b'); |
| } |
| |
| final bool pluginAExampleApk = exists(File(path.join( |
| pluginADirectory.path, |
| 'example', |
| 'build', |
| 'app', |
| 'outputs', |
| 'apk', |
| 'release', |
| 'app-release.apk', |
| ))); |
| |
| if (!pluginAExampleApk) { |
| return TaskResult.failure('Failed to build plugin A example APK'); |
| } |
| |
| return TaskResult.success(null); |
| } on TaskResult catch (taskResult) { |
| return taskResult; |
| } catch (e) { |
| return TaskResult.failure(e.toString()); |
| } finally { |
| rmTree(tempDir); |
| } |
| }); |
| } |