From 827b5dff026ed9db9dcc4c9d66fc1db8f86b31aa Mon Sep 17 00:00:00 2001 From: Gray Mackall <34871572+gmackall@users.noreply.github.com> Date: Thu, 7 Sep 2023 18:32:48 -0400 Subject: [PATCH] [integration_test] Allow capture of screenshots for `FlutterFragmentActivity`s (#132406) Fixes https://github.com/flutter/flutter/issues/89683. The changes to `getFlutterView` in `FlutterDeviceScreenshot` are the fix that was required, everything else was done to get tests running (such as re-generating some lockfiles and modifying the android manifest). The code was all currently not unit tested, and there were no other easy examples to base these java unit tests off in flutter/flutter, so let me know if this approach to testing is wrong. --- .ci.yaml | 6 +-- dev/bots/test.dart | 17 ++++++ .../integration_test/android/build.gradle | 5 +- .../android/src/main/AndroidManifest.xml | 2 +- .../FlutterDeviceScreenshot.java | 17 ++++-- .../FlutterDeviceScreenshotTest.java | 52 +++++++++++++++++++ .../android/project-integration_test.lockfile | 5 ++ 7 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 packages/integration_test/android/src/test/java/dev/flutter/plugins/integration_test/FlutterDeviceScreenshotTest.java diff --git a/.ci.yaml b/.ci.yaml index 4ce84b8cda..fa11c98b6c 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -598,7 +598,7 @@ targets: {"dependency": "clang", "version": "git_revision:5d5aba78dbbee75508f01bcaa69aedb2ab79065a"}, {"dependency": "cmake", "version": "build_id:8787856497187628321"}, {"dependency": "ninja", "version": "version:1.9.0"}, - {"dependency": "open_jdk", "version": "version:11"}, + {"dependency": "open_jdk", "version": "version:17"}, {"dependency": "android_sdk", "version": "version:33v6"} ] shard: framework_tests @@ -3092,7 +3092,7 @@ targets: [ {"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"}, {"dependency": "gems", "version": "v3.3.14"}, - {"dependency": "open_jdk", "version": "version:11"}, + {"dependency": "open_jdk", "version": "version:17"}, {"dependency": "android_sdk", "version": "version:33v6"} ] shard: framework_tests @@ -4502,7 +4502,7 @@ targets: [ {"dependency": "goldctl", "version": "git_revision:f808dcff91b221ae313e540c09d79696cd08b8de"}, {"dependency": "vs_build", "version": "version:vs2019"}, - {"dependency": "open_jdk", "version": "version:11"}, + {"dependency": "open_jdk", "version": "version:17"}, {"dependency": "android_sdk", "version": "version:33v6"} ] shard: framework_tests diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 8408e452d4..8d9dd00c6c 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -1004,6 +1004,23 @@ Future _runFrameworkTests() async { // Web-specific tests depend on Chromium, so they run as part of the web_long_running_tests shard. '--exclude-tags=web', ]); + // Run java unit tests for integration_test + // + // Generate Gradle wrapper if it doesn't exist. + Process.runSync( + flutter, + ['build', 'apk', '--config-only'], + workingDirectory: path.join(flutterRoot, 'packages', 'integration_test', 'example', 'android'), + ); + await runCommand( + path.join(flutterRoot, 'packages', 'integration_test', 'example', 'android', 'gradlew$bat'), + [ + ':integration_test:testDebugUnitTest', + '--tests', + 'dev.flutter.plugins.integration_test.FlutterDeviceScreenshotTest', + ], + workingDirectory: path.join(flutterRoot, 'packages', 'integration_test', 'example', 'android'), + ); await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_goldens')); await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations')); await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test')); diff --git a/packages/integration_test/android/build.gradle b/packages/integration_test/android/build.gradle index ba3e75cde3..2f8e2f988f 100644 --- a/packages/integration_test/android/build.gradle +++ b/packages/integration_test/android/build.gradle @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -group 'com.example.integration_test' +group 'dev.flutter.plugins.integration_test' version '1.0-SNAPSHOT' buildscript { @@ -47,7 +47,8 @@ android { dependencies { // TODO(egarciad): These dependencies should not be added to release builds. // https://github.com/flutter/flutter/issues/56591 - api 'junit:junit:4.12' + testImplementation 'junit:junit:4.12' + testImplementation 'org.mockito:mockito-inline:5.0.0' // https://developer.android.com/jetpack/androidx/releases/test/#1.2.0 api 'androidx.test:runner:1.2.0' diff --git a/packages/integration_test/android/src/main/AndroidManifest.xml b/packages/integration_test/android/src/main/AndroidManifest.xml index b362178b96..76ada4d34c 100644 --- a/packages/integration_test/android/src/main/AndroidManifest.xml +++ b/packages/integration_test/android/src/main/AndroidManifest.xml @@ -3,5 +3,5 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> + package="dev.flutter.integration_test"> diff --git a/packages/integration_test/android/src/main/java/dev/flutter/plugins/integration_test/FlutterDeviceScreenshot.java b/packages/integration_test/android/src/main/java/dev/flutter/plugins/integration_test/FlutterDeviceScreenshot.java index e9494439d7..5b376b0277 100644 --- a/packages/integration_test/android/src/main/java/dev/flutter/plugins/integration_test/FlutterDeviceScreenshot.java +++ b/packages/integration_test/android/src/main/java/dev/flutter/plugins/integration_test/FlutterDeviceScreenshot.java @@ -19,7 +19,11 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.android.FlutterFragment; +import io.flutter.embedding.android.FlutterFragmentActivity; import io.flutter.embedding.android.FlutterSurfaceView; import io.flutter.embedding.android.FlutterView; import io.flutter.plugin.common.MethodChannel; @@ -51,8 +55,15 @@ class FlutterDeviceScreenshot { * @return the Flutter view. */ @Nullable - private static FlutterView getFlutterView(@NonNull Activity activity) { - return (FlutterView)activity.findViewById(FlutterActivity.FLUTTER_VIEW_ID); + @VisibleForTesting + public static FlutterView getFlutterView(@NonNull Activity activity) { + if (activity instanceof FlutterActivity) { + return (FlutterView)activity.findViewById(FlutterActivity.FLUTTER_VIEW_ID); + } else if (activity instanceof FlutterFragmentActivity) { + return (FlutterView)activity.findViewById(FlutterFragment.FLUTTER_VIEW_ID); + } else { + return null; + } } /** @@ -110,7 +121,7 @@ class FlutterDeviceScreenshot { } } - // Handlers use to capture a view. + // Handlers used to capture a view. private static Handler backgroundHandler; private static Handler mainHandler; diff --git a/packages/integration_test/android/src/test/java/dev/flutter/plugins/integration_test/FlutterDeviceScreenshotTest.java b/packages/integration_test/android/src/test/java/dev/flutter/plugins/integration_test/FlutterDeviceScreenshotTest.java new file mode 100644 index 0000000000..15dfad615e --- /dev/null +++ b/packages/integration_test/android/src/test/java/dev/flutter/plugins/integration_test/FlutterDeviceScreenshotTest.java @@ -0,0 +1,52 @@ +// 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. + +package dev.flutter.plugins.integration_test; + +import androidx.test.runner.AndroidJUnitRunner; + +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.Activity; + +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.android.FlutterFragment; +import io.flutter.embedding.android.FlutterFragmentActivity; +import io.flutter.embedding.android.FlutterView; + +public class FlutterDeviceScreenshotTest extends AndroidJUnitRunner { + @Test + public void getFlutterView_returnsNullForNonFlutterActivity() { + Activity mockActivity = mock(Activity.class); + assertNull(FlutterDeviceScreenshot.getFlutterView(mockActivity)); + } + + @Test + public void getFlutterView_returnsFlutterViewForFlutterActivity() { + FlutterView mockFlutterView = mock(FlutterView.class); + FlutterActivity mockFlutterActivity = mock(FlutterActivity.class); + when(mockFlutterActivity.findViewById(FlutterActivity.FLUTTER_VIEW_ID)) + .thenReturn(mockFlutterView); + assertEquals( + FlutterDeviceScreenshot.getFlutterView(mockFlutterActivity), + mockFlutterView + ); + } + + @Test + public void getFlutterView_returnsFlutterViewForFlutterFragmentActivity() { + FlutterView mockFlutterView = mock(FlutterView.class); + FlutterFragmentActivity mockFlutterFragmentActivity = mock(FlutterFragmentActivity.class); + when(mockFlutterFragmentActivity.findViewById(FlutterFragment.FLUTTER_VIEW_ID)) + .thenReturn(mockFlutterView); + assertEquals( + FlutterDeviceScreenshot.getFlutterView(mockFlutterFragmentActivity), + mockFlutterView + ); + } +} diff --git a/packages/integration_test/example/android/project-integration_test.lockfile b/packages/integration_test/example/android/project-integration_test.lockfile index 83502ee6b2..0b94d008cc 100644 --- a/packages/integration_test/example/android/project-integration_test.lockfile +++ b/packages/integration_test/example/android/project-integration_test.lockfile @@ -82,6 +82,8 @@ javax.activation:javax.activation-api:1.2.0=lintClassPath javax.inject:javax.inject:1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath javax.xml.bind:jaxb-api:2.3.1=lintClassPath junit:junit:4.12=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +net.bytebuddy:byte-buddy-agent:1.12.22=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +net.bytebuddy:byte-buddy:1.12.22=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath net.sf.jopt-simple:jopt-simple:4.9=lintClassPath net.sf.kxml:kxml2:2.3.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath org.apache.commons:commons-compress:1.12=lintClassPath @@ -118,6 +120,9 @@ org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2=debugAndroidTestCompileClass org.jetbrains.trove4j:trove4j:20160824=lintClassPath org.jetbrains:annotations:13.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,lintClassPath,profileCompileClasspath,profileRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath org.jvnet.staxex:stax-ex:1.8=lintClassPath +org.mockito:mockito-core:5.0.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +org.mockito:mockito-inline:5.0.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,profileUnitTestCompileClasspath,profileUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +org.objenesis:objenesis:3.3=debugUnitTestRuntimeClasspath,profileUnitTestRuntimeClasspath,releaseUnitTestRuntimeClasspath org.ow2.asm:asm-analysis:7.0=lintClassPath org.ow2.asm:asm-analysis:9.2=androidJacocoAnt org.ow2.asm:asm-commons:7.0=lintClassPath