diff --git a/.ci.yaml b/.ci.yaml index da9363eecd..873c01617f 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -1330,6 +1330,29 @@ targets: - bin/** - .ci.yaml + - name: Linux flutter_build_apk_health_tests + recipe: flutter/flutter_drone + # WARNING: Do *NOT* enable. This intended for testing on CI only. + # Ask matanlurey@ or @gmackall if you have any questions. + bringup: true + timeout: 60 + properties: + add_recipes_cq: "true" + dependencies: >- + [ + {"dependency": "android_sdk", "version": "version:35v1"}, + {"dependency": "chrome_and_driver", "version": "version:125.0.6422.141"}, + {"dependency": "clang", "version": "git_revision:5d5aba78dbbee75508f01bcaa69aedb2ab79065a"}, + {"dependency": "cmake", "version": "build_id:8787856497187628321"}, + {"dependency": "goldctl", "version": "git_revision:2387d6fff449587eecbb7e45b2692ca0710b63b9"}, + {"dependency": "ninja", "version": "version:1.9.0"}, + {"dependency": "open_jdk", "version": "version:17"} + ] + shard: flutter_build_apk_health_tests + tags: > + ["framework", "hostonly", "shard", "linux"] + test_timeout_secs: "2700" + - name: Linux android_preview_tool_integration_tests recipe: flutter/flutter_drone timeout: 60 diff --git a/TESTOWNERS b/TESTOWNERS index 349b34c3b4..57a180a3e2 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -329,6 +329,7 @@ # coverage @goderbauer @flutter/infra # customer_testing @Piinks @flutter/framework # docs @Piinks @flutter/framework +# flutter_build_apk_health_tests @matanlurey @gmackall # flutter_driver_android_test @matanlurey @johnmccutchan # flutter_packaging @christopherfujino @flutter/infra # flutter_plugins @stuartmorgan @flutter/plugin diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 3b8a17c0a7..7073a9f3b0 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -132,6 +132,7 @@ Future main(List args) async { 'tool_tests': _runToolTests, 'web_tool_tests': _runWebToolTests, 'tool_integration_tests': _runIntegrationToolTests, + 'flutter_build_apk_health_tests': _runFlutterBuildApkHealthTests, 'android_preview_tool_integration_tests': androidPreviewIntegrationToolTestsRunner, 'android_java11_tool_integration_tests': androidJava11IntegrationToolTestsRunner, 'tool_host_cross_arch_tests': _runToolHostCrossArchTests, @@ -234,6 +235,20 @@ Future _runIntegrationToolTests() async { ); } +Future _runFlutterBuildApkHealthTests() async { + final List allTests = Directory(path.join(_toolsPath, 'test', 'flutter_build_apk.shard')) + .listSync(recursive: true).whereType() + .map((FileSystemEntity entry) => path.relative(entry.path, from: _toolsPath)) + .where((String testPath) => path.basename(testPath).endsWith('_test.dart')).toList(); + + await runDartTest( + _toolsPath, + forceSingleCore: true, + testPaths: selectIndexOfTotalSubshard(allTests), + collectMetrics: true, + ); +} + Future _runToolTests() async { await selectSubshard({ 'general': _runGeneralToolTests, diff --git a/packages/flutter_tools/test/flutter_build_apk.shard/README.md b/packages/flutter_tools/test/flutter_build_apk.shard/README.md new file mode 100644 index 0000000000..d5d85c11f4 --- /dev/null +++ b/packages/flutter_tools/test/flutter_build_apk.shard/README.md @@ -0,0 +1,3 @@ +# `flutter_build_apk.shard` + +Integration tests that debug why `flutter build apk` sometimes stalls on CI. diff --git a/packages/flutter_tools/test/flutter_build_apk.shard/flutter_build_apk_test.dart b/packages/flutter_tools/test/flutter_build_apk.shard/flutter_build_apk_test.dart new file mode 100644 index 0000000000..8a72d191c7 --- /dev/null +++ b/packages/flutter_tools/test/flutter_build_apk.shard/flutter_build_apk_test.dart @@ -0,0 +1,87 @@ +// 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' as io; + +import 'package:file/file.dart'; + +import '../integration.shard/test_utils.dart'; +import '../src/common.dart'; + +void main() { + final String flutterRoot = getFlutterRoot(); + final String flutterBin = fileSystem.path.join(flutterRoot, 'bin', 'flutter'); + + late Directory tmpDir; + + setUp(() { + tmpDir = fileSystem.systemTempDirectory.createTempSync(); + }); + + tearDown(() { + tryToDelete(tmpDir); + }); + + Future setGradleLoggingLevel( + String level, { + required Directory projectDir, + }) async { + // Open gradle.properties and append to it. + final Directory androidDir = projectDir.childDirectory('android'); + final File gradleDotProperties = androidDir.childFile('gradle.properties'); + final io.IOSink sink = gradleDotProperties.openWrite(mode: FileMode.append); + + sink.writeln('org.gradle.logging.level=$level'); + await sink.flush(); + await sink.close(); + + // For debugging, print the current output. + io.stderr.writeln('${gradleDotProperties.path}:'); + io.stderr.writeln(gradleDotProperties.readAsStringSync()); + } + + // Normally these tests should take about a minute, but sometimes for + // unknown reasons they can take 30m+ and timeout. The intent behind this loop + // is to get more information on what exactly is happening. + for (int i = 1; i <= 10; i++) { + test('flutter build apk | attempt $i of 10', () async { + final String package = 'flutter_build_apk_test_$i'; + + // Create a new Flutter app. + await expectLater( + processManager.run( + [ + flutterBin, + 'create', + package, + ], + workingDirectory: tmpDir.path, + ), + completion(const ProcessResultMatcher()), + reason: 'Should create a new blank Flutter project', + ); + + // Tweak verbosity of just gradle. + final Directory projectDir = tmpDir.childDirectory(package); + await setGradleLoggingLevel('debug', projectDir: projectDir); + + // Build the APK. + final List args = [ + flutterBin, + '--verbose', + 'build', + 'apk', + '--debug', + ]; + io.stderr.writeln('Running $args...'); + + final io.Process process = await processManager.start( + args, + workingDirectory: projectDir.path, + mode: io.ProcessStartMode.inheritStdio, + ); + await expectLater(process.exitCode, completion(0)); + }); + } +}