// 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:io'; import 'package:flutter_devicelab/framework/apk_utils.dart'; import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/task_result.dart'; import 'package:flutter_devicelab/framework/utils.dart'; import 'package:path/path.dart' as path; final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew'; final String gradlewExecutable = Platform.isWindows ? '.\\$gradlew' : './$gradlew'; /// Tests that the Android app containing a Flutter module can be built when /// it has custom build types and flavors. 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'); section('Create Flutter module project'); await flutter( 'precache', options: <String>['--android', '--no-ios'], ); final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.'); final Directory projectDir = Directory(path.join(tempDir.path, 'hello')); try { await inDirectory(tempDir, () async { await flutter( 'create', options: <String>['--org', 'io.flutter.devicelab', '--template=module', 'hello'], ); }); section('Run flutter pub get'); await inDirectory(projectDir, () async { await flutter( 'pub', options: <String>['get'], ); }); section('Add to existing Android app'); final Directory hostAppDir = Directory(path.join(tempDir.path, 'hello_host_app_with_custom_build')); mkdir(hostAppDir); recursiveCopy( Directory( path.join( flutterDirectory.path, 'dev', 'integration_tests', 'module_host_with_custom_build_v2_embedding', ), ), hostAppDir, ); copy( File(path.join(projectDir.path, '.android', gradlew)), hostAppDir, ); copy( File(path.join(projectDir.path, '.android', 'gradle', 'wrapper', 'gradle-wrapper.jar')), Directory(path.join(hostAppDir.path, 'gradle', 'wrapper')), ); Future<void> clean() async { section('Clean'); await inDirectory(hostAppDir, () async { await exec(gradlewExecutable, <String>['clean'], environment: <String, String>{ 'JAVA_HOME': javaHome, }, ); }); } if (!Platform.isWindows) { section('Make $gradlewExecutable executable'); await inDirectory(hostAppDir, () async { await exec('chmod', <String>['+x', gradlewExecutable]); }); } section('Build debug APKs'); section('Run app:assembleDemoDebug'); await inDirectory(hostAppDir, () async { await exec(gradlewExecutable, <String>['app:assembleDemoDebug'], environment: <String, String>{ 'JAVA_HOME': javaHome, }, ); }); final String demoDebugApk = path.join( hostAppDir.path, 'app', 'build', 'outputs', 'apk', 'demo', 'debug', 'app-demo-debug.apk', ); if (!exists(File(demoDebugApk))) { return TaskResult.failure('Failed to build app-demo-debug.apk'); } section('Verify snapshots in app-demo-debug.apk'); checkCollectionContains<String>(<String>[ ...flutterAssets, ...debugAssets, ], await getFilesInApk(demoDebugApk)); await clean(); // Change the order of the task and ensure that flutter_assets are in the APK. // https://github.com/flutter/flutter/pull/41333 section('Run app:assembleDemoDebug - Merge assets before processing manifest'); await inDirectory(hostAppDir, () async { await exec(gradlewExecutable, <String>[ // Normally, `app:processDemoDebugManifest` runs before `app:mergeDemoDebugAssets`. // In this case, we run `app:mergeDemoDebugAssets` first. 'app:mergeDemoDebugAssets', 'app:processDemoDebugManifest', 'app:assembleDemoDebug', ], environment: <String, String>{ 'JAVA_HOME': javaHome, }, ); }); final String demoDebugApk2 = path.join( hostAppDir.path, 'app', 'build', 'outputs', 'apk', 'demo', 'debug', 'app-demo-debug.apk', ); if (!exists(File(demoDebugApk2))) { return TaskResult.failure('Failed to build app-demo-debug.apk'); } section('Verify snapshots in app-demo-debug.apk'); checkCollectionContains<String>(<String>[ ...flutterAssets, ...debugAssets, ], await getFilesInApk(demoDebugApk2)); await clean(); section('Run app:assembleDemoStaging'); await inDirectory(hostAppDir, () async { await exec(gradlewExecutable, <String>['app:assembleDemoStaging'], environment: <String, String>{ 'JAVA_HOME': javaHome, }, ); }); final String demoStagingApk = path.join( hostAppDir.path, 'app', 'build', 'outputs', 'apk', 'demo', 'staging', 'app-demo-staging.apk', ); if (!exists(File(demoStagingApk))) { return TaskResult.failure('Failed to build app-demo-staging.apk'); } section('Verify snapshots in app-demo-staging.apk'); checkCollectionContains<String>(<String>[ ...flutterAssets, ...debugAssets, ], await getFilesInApk(demoStagingApk)); await clean(); section('Build release APKs'); section('Run app:assembleDemoRelease'); await inDirectory(hostAppDir, () async { await exec(gradlewExecutable, <String>['app:assembleDemoRelease'], environment: <String, String>{ 'JAVA_HOME': javaHome, }, ); }); final String demoReleaseApk = path.join( hostAppDir.path, 'app', 'build', 'outputs', 'apk', 'demo', 'release', 'app-demo-release-unsigned.apk', ); if (!exists(File(demoReleaseApk))) { return TaskResult.failure('Failed to build app-demo-release-unsigned.apk'); } section('Verify AOT ELF in app-demo-release-unsigned.apk'); checkCollectionContains<String>(<String>[ ...flutterAssets, 'lib/arm64-v8a/libflutter.so', 'lib/arm64-v8a/libapp.so', 'lib/armeabi-v7a/libflutter.so', 'lib/armeabi-v7a/libapp.so', ], await getFilesInApk(demoReleaseApk)); await clean(); section('Run app:assembleDemoProd'); await inDirectory(hostAppDir, () async { await exec(gradlewExecutable, <String>['app:assembleDemoProd'], environment: <String, String>{ 'JAVA_HOME': javaHome, }, ); }); final String demoProdApk = path.join( hostAppDir.path, 'app', 'build', 'outputs', 'apk', 'demo', 'prod', 'app-demo-prod-unsigned.apk', ); if (!exists(File(demoProdApk))) { return TaskResult.failure('Failed to build app-demo-prod-unsigned.apk'); } section('Verify AOT ELF in app-demo-prod-unsigned.apk'); checkCollectionContains<String>(<String>[ ...flutterAssets, 'lib/arm64-v8a/libapp.so', 'lib/arm64-v8a/libflutter.so', 'lib/armeabi-v7a/libapp.so', 'lib/armeabi-v7a/libflutter.so', ], await getFilesInApk(demoProdApk)); return TaskResult.success(null); } on TaskResult catch (taskResult) { return taskResult; } catch (e) { return TaskResult.failure(e.toString()); } finally { rmTree(tempDir); } }); }