Shard gradle tests (#34857)

diff --git a/.cirrus.yml b/.cirrus.yml
index 405a09d..8f3e588 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -130,6 +130,48 @@
       container:
         cpu: 4
         memory: 12G
+    - name: integration_tests_gradle1-linux
+      env:
+        SHARD: integration_tests
+        SUBSHARD: gradle1
+      test_script:
+        # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they
+        # might include non-ASCII characters which makes Gradle crash.
+        # See: https://github.com/flutter/flutter/issues/24935
+        # This is a temporary workaround until we figure how to properly configure
+        # a UTF8 locale on Cirrus (or until the Gradle bug is fixed).
+        # TODO(amirh): Set the locale to UTF8.
+        - echo "$CIRRUS_CHANGE_MESSAGE" > /tmp/cirrus_change_message.txt
+        - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt
+        - export CIRRUS_CHANGE_MESSAGE=""
+        - export CIRRUS_COMMIT_MESSAGE=""
+        - dart --enable-asserts ./dev/bots/test.dart
+        - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt`
+        - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt`
+      container:
+        cpu: 4
+        memory: 12G
+    - name: integration_tests_gradle2-linux
+      env:
+        SHARD: integration_tests
+        SUBSHARD: gradle2
+      test_script:
+        # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they
+        # might include non-ASCII characters which makes Gradle crash.
+        # See: https://github.com/flutter/flutter/issues/24935
+        # This is a temporary workaround until we figure how to properly configure
+        # a UTF8 locale on Cirrus (or until the Gradle bug is fixed).
+        # TODO(amirh): Set the locale to UTF8.
+        - echo "$CIRRUS_CHANGE_MESSAGE" > /tmp/cirrus_change_message.txt
+        - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt
+        - export CIRRUS_CHANGE_MESSAGE=""
+        - export CIRRUS_COMMIT_MESSAGE=""
+        - dart --enable-asserts ./dev/bots/test.dart
+        - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt`
+        - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt`
+      container:
+        cpu: 4
+        memory: 12G
     - name: release_smoke_tests
       env:
         CLOUDSDK_CORE_DISABLE_PROMPTS: 1
@@ -328,6 +370,7 @@
     - tool_tests-linux
     - build_tests-linux
     - integration_tests-linux
+    - integration_tests_gradle-linux
   build_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_build.sh"
   login_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_login.sh"
   push_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_push.sh"
diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index 9980f43..4c8fde1 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -797,19 +797,28 @@
 }
 
 Future<void> _runIntegrationTests() async {
-  print('Platform env vars:');
+  final String subShard = Platform.environment['SUBSHARD'];
 
-  await _runDevicelabTest('dartdocs');
+  switch (subShard) {
+    case 'gradle1':
+    case 'gradle2':
+      // This runs some gradle integration tests if the subshard is Android.
+      await _androidGradleTests(subShard);
+      break;
+    default:
+      await _runDevicelabTest('dartdocs');
 
-  if (Platform.isLinux) {
-    await _runDevicelabTest('flutter_create_offline_test_linux');
-  } else if (Platform.isWindows) {
-    await _runDevicelabTest('flutter_create_offline_test_windows');
-  } else if (Platform.isMacOS) {
-    await _runDevicelabTest('flutter_create_offline_test_mac');
-    await _runDevicelabTest('module_test_ios');
+      if (Platform.isLinux) {
+        await _runDevicelabTest('flutter_create_offline_test_linux');
+      } else if (Platform.isWindows) {
+        await _runDevicelabTest('flutter_create_offline_test_windows');
+      } else if (Platform.isMacOS) {
+        await _runDevicelabTest('flutter_create_offline_test_mac');
+        await _runDevicelabTest('module_test_ios');
+      }
+      // This does less work if the subshard isn't Android.
+      await _androidPluginTest();
   }
-  await _integrationTestsAndroidSdk();
 }
 
 Future<void> _runDevicelabTest(String testName, {Map<String, String> env}) async {
@@ -821,12 +830,19 @@
   );
 }
 
-Future<void> _integrationTestsAndroidSdk() async {
+String get androidSdkRoot {
   final String androidSdkRoot = (Platform.environment['ANDROID_HOME']?.isEmpty ?? true)
       ? Platform.environment['ANDROID_SDK_ROOT']
       : Platform.environment['ANDROID_HOME'];
   if (androidSdkRoot == null || androidSdkRoot.isEmpty) {
-    print('No Android SDK detected, skipping Android Integration Tests');
+    return null;
+  }
+  return androidSdkRoot;
+}
+
+Future<void> _androidPluginTest() async {
+  if (androidSdkRoot == null) {
+    print('No Android SDK detected, skipping Android Plugin test.');
     return;
   }
 
@@ -835,13 +851,27 @@
     'ANDROID_SDK_ROOT': androidSdkRoot,
   };
 
+  await _runDevicelabTest('plugin_test', env: env);
+}
+
+Future<void> _androidGradleTests(String subShard) async {
   // TODO(dnfield): gradlew is crashing on the cirrus image and it's not clear why.
-  if (!Platform.isWindows) {
+  if (androidSdkRoot == null || Platform.isWindows) {
+    print('No Android SDK detected or on Windows, skipping Android gradle test.');
+    return;
+  }
+
+  final Map<String, String> env = <String, String> {
+    'ANDROID_HOME': androidSdkRoot,
+    'ANDROID_SDK_ROOT': androidSdkRoot,
+  };
+
+  if (subShard == 'gradle1') {
     await _runDevicelabTest('gradle_plugin_light_apk_test', env: env);
     await _runDevicelabTest('gradle_plugin_fat_apk_test', env: env);
+  }
+  if (subShard == 'gradle2') {
     await _runDevicelabTest('gradle_plugin_bundle_test', env: env);
     await _runDevicelabTest('module_test', env: env);
   }
-  // note: this also covers plugin_test_win as long as Windows has an Android SDK available.
-  await _runDevicelabTest('plugin_test', env: env);
 }