// 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; Future<void> main() async { await task(() async { try { await runPluginProjectTest((FlutterPluginProject pluginProject) async { section('APK content for task assembleDebug with target platform = android-arm'); await inDirectory(pluginProject.exampleAndroidPath, () { return flutter( 'build', options: <String>[ 'apk', '--debug', '--target-platform=android-arm', ], ); }); Iterable<String> apkFiles = await getFilesInApk(pluginProject.debugApkPath); checkCollectionContains<String>(<String>[ ...flutterAssets, ...debugAssets, ...baseApkFiles, 'lib/armeabi-v7a/libflutter.so', // Debug mode intentionally includes `x86` and `x86_64`. 'lib/x86/libflutter.so', 'lib/x86_64/libflutter.so', ], apkFiles); checkCollectionDoesNotContain<String>(<String>[ 'lib/arm64-v8a/libapp.so', 'lib/armeabi-v7a/libapp.so', 'lib/x86/libapp.so', 'lib/x86_64/libapp.so', ], apkFiles); section('APK content for task assembleDebug with target platform = android-x86'); // This is used by `flutter run` await inDirectory(pluginProject.exampleAndroidPath, () { return flutter( 'build', options: <String>[ 'apk', '--debug', '--target-platform=android-x86', ], ); }); apkFiles = await getFilesInApk(pluginProject.debugApkPath); checkCollectionContains<String>(<String>[ ...flutterAssets, ...debugAssets, ...baseApkFiles, // Debug mode intentionally includes `x86` and `x86_64`. 'lib/x86/libflutter.so', 'lib/x86_64/libflutter.so', ], apkFiles); checkCollectionDoesNotContain<String>(<String>[ 'lib/armeabi-v7a/libapp.so', 'lib/x86/libapp.so', 'lib/x86_64/libapp.so', ], apkFiles); section('APK content for task assembleDebug with target platform = android-x64'); // This is used by `flutter run` await inDirectory(pluginProject.exampleAndroidPath, () { return flutter( 'build', options: <String>[ 'apk', '--debug', '--target-platform=android-x64', ], ); }); apkFiles = await getFilesInApk(pluginProject.debugApkPath); checkCollectionContains<String>(<String>[ ...flutterAssets, ...debugAssets, ...baseApkFiles, // Debug mode intentionally includes `x86` and `x86_64`. 'lib/x86/libflutter.so', 'lib/x86_64/libflutter.so', ], apkFiles); checkCollectionDoesNotContain<String>(<String>[ 'lib/armeabi-v7a/libapp.so', 'lib/x86/libapp.so', 'lib/x86_64/libapp.so', ], apkFiles); section('APK content for task assembleRelease with target platform = android-arm'); await inDirectory(pluginProject.exampleAndroidPath, () { return flutter( 'build', options: <String>[ 'apk', '--release', '--target-platform=android-arm', ], ); }); apkFiles = await getFilesInApk(pluginProject.releaseApkPath); checkCollectionContains<String>(<String>[ ...flutterAssets, ...baseApkFiles, 'lib/armeabi-v7a/libflutter.so', 'lib/armeabi-v7a/libapp.so', ], apkFiles); checkCollectionDoesNotContain<String>(<String>[ ...debugAssets, 'lib/arm64-v8a/libflutter.so', 'lib/arm64-v8a/libapp.so', ], apkFiles); section('APK content for task assembleRelease with target platform = android-arm64'); await inDirectory(pluginProject.exampleAndroidPath, () { return flutter( 'build', options: <String>[ 'apk', '--release', '--target-platform=android-arm64', ], ); }); apkFiles = await getFilesInApk(pluginProject.releaseApkPath); checkCollectionContains<String>(<String>[ ...flutterAssets, ...baseApkFiles, 'lib/arm64-v8a/libflutter.so', 'lib/arm64-v8a/libapp.so', ], apkFiles); checkCollectionDoesNotContain<String>(<String>[ ...debugAssets, 'lib/armeabi-v7a/libflutter.so', 'lib/armeabi-v7a/libapp.so', ], apkFiles); }); await runProjectTest((FlutterProject project) async { section('gradlew assembleDebug'); await inDirectory(project.rootPath, () { return flutter( 'build', options: <String>[ 'apk', '--debug', ], ); }); final String? errorMessage = validateSnapshotDependency(project, 'kernel_blob.bin'); if (errorMessage != null) { throw TaskResult.failure(errorMessage); } section('gradlew assembleProfile'); await inDirectory(project.rootPath, () { return flutter( 'build', options: <String>[ 'apk', '--profile', ], ); }); section('gradlew assembleLocal (custom debug build)'); await project.addCustomBuildType('local', initWith: 'debug'); await project.runGradleTask('assembleLocal'); }); await runProjectTest((FlutterProject project) async { section('gradlew assembleLocal with plugin (custom debug build)'); final Directory tempDir = Directory.systemTemp.createTempSync('flutter_plugin.'); final Directory pluginDir = Directory(path.join(tempDir.path, 'plugin_under_test')); section('Create plugin'); await inDirectory(tempDir, () async { await flutter( 'create', options: <String>[ '--org', 'io.flutter.devicelab.plugin', '--template=plugin', '--platforms=android,ios', pluginDir.path, ], ); }); section('Configure'); project.addPlugin('plugin_under_test', value: '$platformLineSep path: ${pluginDir.path}'); await project.addCustomBuildType('local', initWith: 'debug'); await project.getPackages(); section('Build APK'); await project.runGradleTask('assembleLocal'); }); await runProjectTest((FlutterProject project) async { section('gradlew assembleBeta (custom release build)'); await project.addCustomBuildType('beta', initWith: 'release'); await project.runGradleTask('assembleBeta'); }); await runProjectTest((FlutterProject project) async { section('gradlew assembleLocal (plugin with custom build type)'); await project.addCustomBuildType('local', initWith: 'debug'); section('Add plugin'); project.addPlugin('path_provider'); await project.getPackages(); await project.runGradleTask('assembleLocal'); }); await runProjectTest((FlutterProject project) async { section('gradlew assembleFreeDebug (product flavor)'); await project.addProductFlavors(<String>['free']); await project.runGradleTask('assembleFreeDebug'); }); await runProjectTest((FlutterProject project) async { section('gradlew on build script with error'); await project.introduceError(); ProcessResult result = await inDirectory(project.rootPath, () { return executeFlutter('build', options: <String>[ 'apk', '--release', ]); }); if (result.exitCode == 0) { throw failure( 'Gradle did not exit with error as expected', result); } String output = '${result.stdout}\n${result.stderr}'; if (output.contains('GradleException') || output.contains('Failed to notify') || output.contains('at org.gradle')) { throw failure( 'Gradle output should not contain stacktrace', result); } if (!output.contains('Build failed')) { throw failure( 'Gradle output should contain a readable error message', result); } section('flutter build apk on build script with error'); await project.introduceError(); result = await inDirectory(project.rootPath, () { return executeFlutter('build', options: <String>[ 'apk', '--release', ]); }); if (result.exitCode == 0) { throw failure( 'flutter build apk should fail when Gradle does', result); } output = '${result.stdout}\n${result.stderr}'; if (!output.contains('Build failed')) { throw failure( 'flutter build apk output should contain a readable Gradle error message', result); } if (hasMultipleOccurrences(output, 'Build failed')) { throw failure( 'flutter build apk should not invoke Gradle repeatedly on error', result); } }); await runProjectTest((FlutterProject project) async { section('gradlew assembleDebug forwards stderr'); await project.introducePubspecError(); final ProcessResult result = await inDirectory(project.rootPath, () { return executeFlutter('build', options: <String>[ 'apk', '--release', ]); }); if (result.exitCode == 0) { throw failure( 'Gradle did not exit with error as expected', result); } final String output = '${result.stdout}\n${result.stderr}'; if (!output.contains('No file or variants found for asset: lib/gallery/example_code.dart.')) { throw failure(output, result); } }); return TaskResult.success(null); } on TaskResult catch (taskResult) { return taskResult; } catch (e) { return TaskResult.failure(e.toString()); } }); }