diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a62148fd49ce0..15e97c6b8f784 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -936,7 +936,6 @@ FILE: ../../../flutter/shell/platform/android/platform_view_android_delegate/pla FILE: ../../../flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate_unittests.cc FILE: ../../../flutter/shell/platform/android/platform_view_android_jni_impl.cc FILE: ../../../flutter/shell/platform/android/platform_view_android_jni_impl.h -FILE: ../../../flutter/shell/platform/android/robolectric.properties FILE: ../../../flutter/shell/platform/android/surface/android_native_window.cc FILE: ../../../flutter/shell/platform/android/surface/android_native_window.h FILE: ../../../flutter/shell/platform/android/surface/android_surface.cc diff --git a/ci/licenses_golden/tool_signature b/ci/licenses_golden/tool_signature index 330cd676f83ab..cb34937aafd1e 100644 --- a/ci/licenses_golden/tool_signature +++ b/ci/licenses_golden/tool_signature @@ -1,2 +1,2 @@ -Signature: 42213051c23cf7aacd7319a315599219 +Signature: 556bb3a9c92c7ac1fb27943070183aae diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index e4d5f496ddc58..6d3ffa76c953c 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -470,120 +470,9 @@ action("pom_embedding") { ] } -# To build and run: -# testing/run_tests.py [--type=java] [--java-filter=io.flutter.TestClassName] -action("robolectric_tests") { - script = "//build/android/gyp/javac.py" - depfile = "$target_gen_dir/$target_name.d" - - jar_path = "$root_out_dir/robolectric_tests.jar" - - sources = [ - "test/io/flutter/FlutterInjectorTest.java", - "test/io/flutter/FlutterTestSuite.java", - "test/io/flutter/SmokeTest.java", - "test/io/flutter/TestUtils.java", - "test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java", - "test/io/flutter/embedding/android/FlutterActivityTest.java", - "test/io/flutter/embedding/android/FlutterAndroidComponentTest.java", - "test/io/flutter/embedding/android/FlutterFragmentActivityTest.java", - "test/io/flutter/embedding/android/FlutterFragmentTest.java", - "test/io/flutter/embedding/android/FlutterTextureViewTest.java", - "test/io/flutter/embedding/android/FlutterViewTest.java", - "test/io/flutter/embedding/android/KeyChannelResponderTest.java", - "test/io/flutter/embedding/android/KeyboardManagerTest.java", - "test/io/flutter/embedding/android/RobolectricFlutterActivity.java", - "test/io/flutter/embedding/android/SplashShadowResources.java", - "test/io/flutter/embedding/engine/FlutterEngineCacheTest.java", - "test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java", - "test/io/flutter/embedding/engine/FlutterEngineGroupComponentTest.java", - "test/io/flutter/embedding/engine/FlutterEngineTest.java", - "test/io/flutter/embedding/engine/FlutterJNITest.java", - "test/io/flutter/embedding/engine/FlutterShellArgsTest.java", - "test/io/flutter/embedding/engine/PluginComponentTest.java", - "test/io/flutter/embedding/engine/RenderingComponentTest.java", - "test/io/flutter/embedding/engine/dart/DartExecutorTest.java", - "test/io/flutter/embedding/engine/dart/DartMessengerTest.java", - "test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java", - "test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java", - "test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java", - "test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java", - "test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java", - "test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java", - "test/io/flutter/embedding/engine/systemchannels/DeferredComponentChannelTest.java", - "test/io/flutter/embedding/engine/systemchannels/KeyEventChannelTest.java", - "test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java", - "test/io/flutter/embedding/engine/systemchannels/RestorationChannelTest.java", - "test/io/flutter/external/FlutterLaunchTests.java", - "test/io/flutter/plugin/common/BinaryCodecTest.java", - "test/io/flutter/plugin/common/StandardMessageCodecTest.java", - "test/io/flutter/plugin/common/StandardMethodCodecTest.java", - "test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java", - "test/io/flutter/plugin/editing/ListenableEditingStateTest.java", - "test/io/flutter/plugin/editing/TextInputPluginTest.java", - "test/io/flutter/plugin/localization/LocalizationPluginTest.java", - "test/io/flutter/plugin/mouse/MouseCursorPluginTest.java", - "test/io/flutter/plugin/platform/PlatformPluginTest.java", - "test/io/flutter/plugin/platform/PlatformViewsControllerTest.java", - "test/io/flutter/plugin/platform/SingleViewPresentationTest.java", - "test/io/flutter/plugins/GeneratedPluginRegistrant.java", - "test/io/flutter/util/FakeKeyEvent.java", - "test/io/flutter/util/PreconditionsTest.java", - "test/io/flutter/util/ViewUtilsTest.java", - "test/io/flutter/view/AccessibilityBridgeTest.java", - ] - - outputs = [ - depfile, - jar_path, - jar_path + ".md5.stamp", - ] - - additional_jar_files = [ "robolectric.properties" ] - - _jar_dependencies = [ - android_sdk_jar, - embedding_jar_path, - ] + embedding_dependencies_jars - - inputs = _jar_dependencies + additional_jar_files - - _rebased_current_path = rebase_path(".") - _rebased_jar_path = rebase_path(jar_path, root_build_dir) - _rebased_depfile = rebase_path(depfile, root_build_dir) - _rebased_classpath = rebase_path(_jar_dependencies, root_build_dir) - _rebased_srcjars = rebase_path(_jar_dependencies, root_build_dir) - _rebased_additional_files = rebase_path(additional_jar_files, root_build_dir) - - if (host_os == "mac") { - _javacbin = - rebase_path("//third_party/java/openjdk/Contents/Home/bin/javac") - _jarbin = rebase_path("//third_party/java/openjdk/Contents/Home/bin/jar") - } else if (host_os == "win") { - _javacbin = rebase_path("//third_party/java/openjdk/bin/javac.exe") - _jarbin = rebase_path("//third_party/java/openjdk/bin/jar.exe") - } else { - _javacbin = rebase_path("//third_party/java/openjdk/bin/javac") - _jarbin = rebase_path("//third_party/java/openjdk/bin/jar") - } - - args = [ - "--depfile=$_rebased_depfile", - "--jar-path=$_rebased_jar_path", - "--classpath=$_rebased_classpath", - "--jar-source-base-dir=$_rebased_current_path", - "--java-srcjars=$_rebased_srcjars", - "--additional-jar-files=$_rebased_additional_files", - "--jar-bin=$_jarbin", - "--javac-bin=$_javacbin", - ] - - args += rebase_path(sources, root_build_dir) - - deps = [ - ":android_jar", - ":flutter_shell_java", - ] +# TODO(jsimmons): remove this placeholder when it is no longer used by the LUCI recipes +group("robolectric_tests") { + deps = [ ":android_jar" ] } zip_bundle("android_symbols") { @@ -688,10 +577,4 @@ group("android") { if (target_cpu != "x86") { deps += [ ":gen_snapshot" ] } - - # We only run the robolectric tests for android_debug_unopt (arm CPU), and - # the target is relatively expensive to build. - if (flutter_runtime_mode == "debug" && current_cpu == "arm") { - deps += [ ":robolectric_tests" ] - } } diff --git a/shell/platform/android/robolectric.properties b/shell/platform/android/robolectric.properties deleted file mode 100644 index 4098ca3a5a4d4..0000000000000 --- a/shell/platform/android/robolectric.properties +++ /dev/null @@ -1,4 +0,0 @@ -# Match the value at tools/cipd/android_embedding_bundle/build.gradle. -# TODO(https://github.com/flutter/flutter/issues/57655): figure out -# what's wrong with sdk=29. -sdk=28 diff --git a/shell/platform/android/test/io/flutter/FlutterInjectorTest.java b/shell/platform/android/test/io/flutter/FlutterInjectorTest.java index 65c958b1010a5..41d62ea533f01 100644 --- a/shell/platform/android/test/io/flutter/FlutterInjectorTest.java +++ b/shell/platform/android/test/io/flutter/FlutterInjectorTest.java @@ -11,6 +11,7 @@ import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager; import io.flutter.embedding.engine.loader.FlutterLoader; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -32,6 +33,11 @@ public void setUp() { MockitoAnnotations.initMocks(this); } + @After + public void tearDown() { + FlutterInjector.reset(); + } + @Test public void itHasSomeReasonableDefaults() { // Implicitly builds when first accessed. diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java index 29eb3ea93d15e..6abee827641ba 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java @@ -54,6 +54,7 @@ public class FlutterActivityTest { @Before public void setUp() { + FlutterInjector.reset(); GeneratedPluginRegistrant.clearRegisteredEngines(); FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); when(mockFlutterJNI.isAttached()).thenReturn(true); diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java index 32255da988ac6..d47992afa6106 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java @@ -1,6 +1,5 @@ package io.flutter.embedding.android; -import static android.os.Looper.getMainLooper; import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.HANDLE_DEEPLINKING_META_DATA_KEY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -22,6 +21,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.test.core.app.ActivityScenario; import io.flutter.FlutterInjector; import io.flutter.TestUtils; import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode; @@ -93,29 +93,30 @@ protected RenderMode getRenderMode() { @Test public void itRegistersPluginsAtConfigurationTime() { - FlutterFragmentActivity activity = - Robolectric.buildActivity(FlutterFragmentActivity.class).get(); - assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty()); - - // Calling onCreate on the FlutterFragmentActivity will create a FlutterFragment and - // commit it to the fragment manager. This attaches the fragment to the FlutterFragmentActivity - // creating and configuring the engine. - activity.onCreate(null); - shadowOf(getMainLooper()).idle(); - - List registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines(); - assertEquals(1, registeredEngines.size()); - assertEquals(activity.getFlutterEngine(), registeredEngines.get(0)); + try (ActivityScenario scenario = + ActivityScenario.launch(FlutterFragmentActivity.class)) { + scenario.onActivity( + activity -> { + List registeredEngines = + GeneratedPluginRegistrant.getRegisteredEngines(); + assertEquals(1, registeredEngines.size()); + assertEquals(activity.getFlutterEngine(), registeredEngines.get(0)); + }); + } } @Test public void itDoesNotRegisterPluginsTwiceWhenUsingACachedEngine() { - FlutterFragmentActivity activity = - Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get(); - activity.onCreate(null); - shadowOf(getMainLooper()).idle(); - - activity.configureFlutterEngine(activity.getFlutterEngine()); + try (ActivityScenario scenario = + ActivityScenario.launch(FlutterFragmentActivity.class)) { + scenario.onActivity( + activity -> { + List registeredEngines = + GeneratedPluginRegistrant.getRegisteredEngines(); + assertEquals(1, registeredEngines.size()); + assertEquals(activity.getFlutterEngine(), registeredEngines.get(0)); + }); + } List registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines(); // This might cause the plugins to be registered twice, once by the FlutterEngine constructor, @@ -184,16 +185,14 @@ public void itAllowsRootLayoutOverride() { @Test public void itCreatesAValidFlutterFragment() { - FlutterFragmentActivityWithProvidedEngine activity = - Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get(); - - // Creating the FlutterFragmentActivity will create and attach the FlutterFragment, causing - // a FlutterEngine to be created. - activity.onCreate(null); - shadowOf(getMainLooper()).idle(); - - assertNotNull(activity.getFlutterEngine()); - assertEquals(1, activity.numberOfEnginesCreated); + try (ActivityScenario scenario = + ActivityScenario.launch(FlutterFragmentActivityWithProvidedEngine.class)) { + scenario.onActivity( + activity -> { + assertNotNull(activity.getFlutterEngine()); + assertEquals(1, activity.numberOfEnginesCreated); + }); + } } @Test diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java index d0fbf671f95de..a9ba39e0e5bf6 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java @@ -18,6 +18,7 @@ import io.flutter.embedding.engine.FlutterEngineCache; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.embedding.engine.loader.FlutterLoader; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; @@ -197,8 +198,14 @@ public void itHandlesPopSystemNavigationAutomaticallyWhenEnabled() { .beginTransaction() .add(android.R.id.content, fragment) .commitNow(); - OnBackPressedCallback callback = mock(OnBackPressedCallback.class); - when(callback.isEnabled()).thenReturn(true); + final AtomicBoolean onBackPressedCalled = new AtomicBoolean(false); + OnBackPressedCallback callback = + new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + onBackPressedCalled.set(true); + } + }; activity.getOnBackPressedDispatcher().addCallback(callback); FlutterActivityAndFragmentDelegate mockDelegate = @@ -208,6 +215,6 @@ public void itHandlesPopSystemNavigationAutomaticallyWhenEnabled() { assertTrue(fragment.popSystemNavigator()); verify(mockDelegate, never()).onBackPressed(); - verify(callback, times(1)).handleOnBackPressed(); + assertTrue(onBackPressedCalled.get()); } } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java index a8b17284a367f..9aa0ec9c0156b 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java @@ -241,27 +241,28 @@ public void setPaddingTopToZeroForFullscreenMode() { assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); // Then we simulate the system applying a window inset. - WindowInsets windowInsets = mock(WindowInsets.class); - when(windowInsets.getSystemWindowInsetTop()).thenReturn(100); - when(windowInsets.getSystemWindowInsetBottom()).thenReturn(100); - when(windowInsets.getSystemWindowInsetLeft()).thenReturn(100); - when(windowInsets.getSystemWindowInsetRight()).thenReturn(100); + WindowInsets windowInsets = + new WindowInsets.Builder() + .setInsets( + android.view.WindowInsets.Type.navigationBars() + | android.view.WindowInsets.Type.statusBars(), + Insets.of(100, 100, 100, 100)) + .build(); flutterView.onApplyWindowInsets(windowInsets); // Verify. - verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); - assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); - // Padding bottom is always 0. - assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); + verify(flutterRenderer, times(3)).setViewportMetrics(viewportMetricsCaptor.capture()); + assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingTop); + assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingBottom); assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingLeft); assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingRight); } // This test uses the pre-API 30 Algorithm for window insets. @Test - @TargetApi(29) + @TargetApi(28) @Config( - sdk = 29, + sdk = 28, shadows = { FlutterViewTest.ShadowFullscreenView.class, FlutterViewTest.ShadowFullscreenViewGroup.class @@ -293,7 +294,7 @@ public void setPaddingTopToZeroForFullscreenModeLegacy() { // Verify. verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); - assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingBottom); + assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingLeft); assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingRight); } @@ -323,27 +324,28 @@ public void reportSystemInsetWhenNotFullscreen() { assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); // Then we simulate the system applying a window inset. - WindowInsets windowInsets = mock(WindowInsets.class); - when(windowInsets.getSystemWindowInsetTop()).thenReturn(100); - when(windowInsets.getSystemWindowInsetBottom()).thenReturn(100); - when(windowInsets.getSystemWindowInsetLeft()).thenReturn(100); - when(windowInsets.getSystemWindowInsetRight()).thenReturn(100); + WindowInsets windowInsets = + new WindowInsets.Builder() + .setInsets( + android.view.WindowInsets.Type.navigationBars() + | android.view.WindowInsets.Type.statusBars(), + Insets.of(100, 100, 100, 100)) + .build(); flutterView.onApplyWindowInsets(windowInsets); // Verify. - verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); + verify(flutterRenderer, times(3)).setViewportMetrics(viewportMetricsCaptor.capture()); // Top padding is reported as-is. assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingTop); - // Padding bottom is always 0. - assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); + assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingBottom); assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingLeft); assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingRight); } // This test uses the pre-API 30 Algorithm for window insets. @Test - @TargetApi(29) - @Config(sdk = 29) + @TargetApi(28) + @Config(sdk = 28) public void reportSystemInsetWhenNotFullscreenLegacy() { // Without custom shadows, the default system ui visibility flags is 0. FlutterView flutterView = new FlutterView(RuntimeEnvironment.application); @@ -506,7 +508,7 @@ public void systemInsetGetInsetsFullscreen() { verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); - Insets insets = Insets.of(100, 100, 100, 100); + Insets insets = Insets.of(10, 20, 30, 40); // Then we simulate the system applying a window inset. WindowInsets windowInsets = mock(WindowInsets.class); when(windowInsets.getSystemWindowInsetTop()).thenReturn(-1); @@ -518,19 +520,16 @@ public void systemInsetGetInsetsFullscreen() { flutterView.onApplyWindowInsets(windowInsets); verify(flutterRenderer, times(2)).setViewportMetrics(viewportMetricsCaptor.capture()); - // Top padding is removed due to full screen. - assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); - // Padding bottom is always 0. - assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); - // Left padding is zero because the rotation is 270deg - assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingLeft); - assertEquals(100, viewportMetricsCaptor.getValue().viewPaddingRight); + assertEquals(10, viewportMetricsCaptor.getValue().viewPaddingLeft); + assertEquals(20, viewportMetricsCaptor.getValue().viewPaddingTop); + assertEquals(30, viewportMetricsCaptor.getValue().viewPaddingRight); + assertEquals(40, viewportMetricsCaptor.getValue().viewPaddingBottom); } // This test uses the pre-API 30 Algorithm for window insets. @Test - @TargetApi(29) - @Config(sdk = 29) + @TargetApi(28) + @Config(sdk = 28) public void systemInsetGetInsetsFullscreenLegacy() { RuntimeEnvironment.setQualifiers("+land"); FlutterView flutterView = spy(new FlutterView(RuntimeEnvironment.systemContext)); diff --git a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java index 969a965ead9bc..37a73cbf65978 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java @@ -21,7 +21,6 @@ import android.content.res.AssetManager; import android.os.Build; import android.os.Bundle; -import android.text.Emoji; import android.text.InputType; import android.text.Selection; import android.text.SpannableStringBuilder; @@ -33,6 +32,8 @@ import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import com.ibm.icu.lang.UCharacter; +import com.ibm.icu.lang.UProperty; import io.flutter.embedding.android.KeyboardManager; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.embedding.engine.dart.DartExecutor; @@ -1198,11 +1199,7 @@ private static InputConnectionAdaptor sampleInputConnectionAdaptor( when(mockFlutterJNI.nativeFlutterTextUtilsIsEmojiModifierBase(anyInt())) .thenAnswer((invocation) -> Emoji.isEmojiModifierBase((int) invocation.getArguments()[0])); when(mockFlutterJNI.nativeFlutterTextUtilsIsVariationSelector(anyInt())) - .thenAnswer( - (invocation) -> { - int codePoint = (int) invocation.getArguments()[0]; - return 0xFE0E <= codePoint && codePoint <= 0xFE0F; - }); + .thenAnswer((invocation) -> Emoji.isVariationSelector((int) invocation.getArguments()[0])); when(mockFlutterJNI.nativeFlutterTextUtilsIsRegionalIndicator(anyInt())) .thenAnswer( (invocation) -> Emoji.isRegionalIndicatorSymbol((int) invocation.getArguments()[0])); @@ -1210,6 +1207,28 @@ private static InputConnectionAdaptor sampleInputConnectionAdaptor( testView, client, textInputChannel, mockKeyboardManager, editable, null, mockFlutterJNI); } + private static class Emoji { + public static boolean isEmoji(int codePoint) { + return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI); + } + + public static boolean isEmojiModifier(int codePoint) { + return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER); + } + + public static boolean isEmojiModifierBase(int codePoint) { + return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER_BASE); + } + + public static boolean isRegionalIndicatorSymbol(int codePoint) { + return UCharacter.hasBinaryProperty(codePoint, UProperty.REGIONAL_INDICATOR); + } + + public static boolean isVariationSelector(int codePoint) { + return UCharacter.hasBinaryProperty(codePoint, UProperty.VARIATION_SELECTOR); + } + } + private class TestTextInputChannel extends TextInputChannel { public TestTextInputChannel(DartExecutor dartExecutor) { super(dartExecutor); diff --git a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java index bb1f39841934d..eab8ce9d545cd 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -7,6 +7,7 @@ import static org.mockito.AdditionalMatchers.gt; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; @@ -60,10 +61,12 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -82,6 +85,12 @@ public class TextInputPluginTest { @Mock FlutterJNI mockFlutterJni; @Mock FlutterLoader mockFlutterLoader; + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mockFlutterJni.isAttached()).thenReturn(true); + } + // Verifies the method and arguments for a captured method call. private void verifyMethodCall(ByteBuffer buffer, String methodName, String[] expectedArgs) throws JSONException { @@ -1201,7 +1210,7 @@ public void ime_windowInsetsSync() { imeSyncCallback.getInsetsListener().onApplyWindowInsets(testView, deferredInsets); imeSyncCallback.getInsetsListener().onApplyWindowInsets(testView, noneInsets); - verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); + verify(flutterRenderer, atLeast(1)).setViewportMetrics(viewportMetricsCaptor.capture()); assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); assertEquals(0, viewportMetricsCaptor.getValue().viewInsetBottom); @@ -1213,7 +1222,7 @@ public void ime_windowInsetsSync() { // Only the final state call is saved, extra calls are passed on. imeSyncCallback.getInsetsListener().onApplyWindowInsets(testView, imeInsets2); - verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); + verify(flutterRenderer, atLeast(1)).setViewportMetrics(viewportMetricsCaptor.capture()); // No change, as deferredInset is stored to be passed in onEnd() assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingTop); @@ -1222,23 +1231,23 @@ public void ime_windowInsetsSync() { imeSyncCallback.getAnimationCallback().onProgress(imeInsets0, animationList); - verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); - assertEquals(40, viewportMetricsCaptor.getValue().viewPaddingBottom); + verify(flutterRenderer, atLeast(1)).setViewportMetrics(viewportMetricsCaptor.capture()); + assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); assertEquals(10, viewportMetricsCaptor.getValue().viewPaddingTop); assertEquals(60, viewportMetricsCaptor.getValue().viewInsetBottom); assertEquals(0, viewportMetricsCaptor.getValue().viewInsetTop); imeSyncCallback.getAnimationCallback().onProgress(imeInsets1, animationList); - verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); - assertEquals(40, viewportMetricsCaptor.getValue().viewPaddingBottom); + verify(flutterRenderer, atLeast(1)).setViewportMetrics(viewportMetricsCaptor.capture()); + assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); assertEquals(10, viewportMetricsCaptor.getValue().viewPaddingTop); assertEquals(0, viewportMetricsCaptor.getValue().viewInsetBottom); // Cannot be negative assertEquals(0, viewportMetricsCaptor.getValue().viewInsetTop); imeSyncCallback.getAnimationCallback().onEnd(animation); - verify(flutterRenderer).setViewportMetrics(viewportMetricsCaptor.capture()); + verify(flutterRenderer, atLeast(1)).setViewportMetrics(viewportMetricsCaptor.capture()); // Values should be of deferredInsets, not imeInsets2 assertEquals(0, viewportMetricsCaptor.getValue().viewPaddingBottom); assertEquals(10, viewportMetricsCaptor.getValue().viewPaddingTop); diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java index fea6db961e248..f20cf4dcb1790 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -1,6 +1,5 @@ package io.flutter.plugin.platform; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -17,22 +16,22 @@ import android.content.ClipboardManager; import android.content.ContentResolver; import android.content.Context; -import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; import android.view.View; import android.view.Window; -import androidx.activity.OnBackPressedDispatcher; +import androidx.activity.OnBackPressedCallback; import androidx.fragment.app.FragmentActivity; import io.flutter.embedding.engine.systemchannels.PlatformChannel; +import io.flutter.embedding.engine.systemchannels.PlatformChannel.Brightness; import io.flutter.embedding.engine.systemchannels.PlatformChannel.ClipboardContentFormat; import io.flutter.embedding.engine.systemchannels.PlatformChannel.SystemChromeStyle; import io.flutter.plugin.platform.PlatformPlugin.PlatformPluginDelegate; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -80,21 +79,11 @@ public void platformPlugin_getClipboardData() throws IOException { assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); ContentResolver contentResolver = RuntimeEnvironment.application.getContentResolver(); + when(fakeActivity.getContentResolver()).thenReturn(contentResolver); Uri uri = Uri.parse("content://media/external_primary/images/media/"); clip = ClipData.newUri(contentResolver, "URI", uri); clipboardManager.setPrimaryClip(clip); assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); - - uri = - RingtoneManager.getActualDefaultRingtoneUri( - RuntimeEnvironment.application.getApplicationContext(), RingtoneManager.TYPE_RINGTONE); - clip = ClipData.newUri(contentResolver, "URI", uri); - clipboardManager.setPrimaryClip(clip); - String uriData = - platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat).toString(); - InputStream uriInputStream = contentResolver.openInputStream(uri); - InputStream dataInputStream = new ByteArrayInputStream(uriData.getBytes()); - assertEquals(dataInputStream.read(), uriInputStream.read()); } @Config(sdk = 28) @@ -162,23 +151,24 @@ public void setNavigationBarDividerColor() { when(fakeActivity.getWindow()).thenReturn(fakeWindow); PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); - // Default style test - SystemChromeStyle style = - new SystemChromeStyle( - 0XFF000000, // statusBarColor - null, // statusBarIconBrightness - true, // systemStatusBarContrastEnforced - 0XFFC70039, // systemNavigationBarColor - null, // systemNavigationBarIconBrightness - 0XFF006DB3, // systemNavigationBarDividerColor - true); // systemNavigationBarContrastEnforced if (Build.VERSION.SDK_INT >= 28) { + // Default style test + SystemChromeStyle style = + new SystemChromeStyle( + 0XFF000000, // statusBarColor + Brightness.LIGHT, // statusBarIconBrightness + true, // systemStatusBarContrastEnforced + 0XFFC70039, // systemNavigationBarColor + Brightness.LIGHT, // systemNavigationBarIconBrightness + 0XFF006DB3, // systemNavigationBarDividerColor + true); // systemNavigationBarContrastEnforced + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); - assertEquals(0XFF000000, fakeActivity.getWindow().getStatusBarColor()); - assertEquals(0XFFC70039, fakeActivity.getWindow().getNavigationBarColor()); - assertEquals(0XFF006DB3, fakeActivity.getWindow().getNavigationBarDividerColor()); + verify(fakeWindow).setStatusBarColor(0xFF000000); + verify(fakeWindow).setNavigationBarColor(0XFFC70039); + verify(fakeWindow).setNavigationBarDividerColor(0XFF006DB3); // Regression test for https://github.com/flutter/flutter/issues/88431 // A null brightness should not affect changing color settings. @@ -194,9 +184,9 @@ public void setNavigationBarDividerColor() { platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); - assertEquals(0XFFC70039, fakeActivity.getWindow().getStatusBarColor()); - assertEquals(0XFF000000, fakeActivity.getWindow().getNavigationBarColor()); - assertEquals(0XFF006DB3, fakeActivity.getWindow().getNavigationBarDividerColor()); + verify(fakeWindow).setStatusBarColor(0XFF006DB3); + verify(fakeWindow).setNavigationBarColor(0XFF000000); + verify(fakeWindow, times(2)).setNavigationBarDividerColor(0XFF006DB3); } } @@ -214,43 +204,43 @@ public void setSystemUiMode() { if (Build.VERSION.SDK_INT >= 28) { platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.LEAN_BACK); - assertEquals( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN, - fakeActivity.getWindow().getDecorView().getSystemUiVisibility()); + verify(fakeDecorView) + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN); platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.IMMERSIVE); - assertEquals( - View.SYSTEM_UI_FLAG_IMMERSIVE - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN, - fakeActivity.getWindow().getDecorView().getSystemUiVisibility()); + verify(fakeDecorView) + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_IMMERSIVE + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN); platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.IMMERSIVE_STICKY); - assertEquals( - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN, - fakeActivity.getWindow().getDecorView().getSystemUiVisibility()); + verify(fakeDecorView) + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN); platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.EDGE_TO_EDGE); - assertEquals( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, - fakeActivity.getWindow().getDecorView().getSystemUiVisibility()); + verify(fakeDecorView) + .setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } } @@ -287,40 +277,44 @@ public void doesNotDoAnythingByDefaultIfPopSystemNavigatorOverridden() { @Test public void popSystemNavigatorFlutterFragment() { - FragmentActivity mockFragmentActivity = mock(FragmentActivity.class); - OnBackPressedDispatcher onBackPressedDispatcher = mock(OnBackPressedDispatcher.class); - when(mockFragmentActivity.getOnBackPressedDispatcher()).thenReturn(onBackPressedDispatcher); + FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class)); + final AtomicBoolean onBackPressedCalled = new AtomicBoolean(false); + OnBackPressedCallback backCallback = + new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + onBackPressedCalled.set(true); + } + }; + activity.getOnBackPressedDispatcher().addCallback(backCallback); + PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false); PlatformPlugin platformPlugin = - new PlatformPlugin(mockFragmentActivity, mockPlatformChannel, mockPlatformPluginDelegate); + new PlatformPlugin(activity, mockPlatformChannel, mockPlatformPluginDelegate); platformPlugin.mPlatformMessageHandler.popSystemNavigator(); - verify(mockFragmentActivity, never()).finish(); + verify(activity, never()).finish(); verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator(); - verify(mockFragmentActivity, times(1)).getOnBackPressedDispatcher(); - verify(onBackPressedDispatcher, times(1)).onBackPressed(); + assertTrue(onBackPressedCalled.get()); } @Test public void doesNotDoAnythingByDefaultIfFragmentPopSystemNavigatorOverridden() { - FragmentActivity mockFragmentActivity = mock(FragmentActivity.class); - OnBackPressedDispatcher onBackPressedDispatcher = mock(OnBackPressedDispatcher.class); - when(mockFragmentActivity.getOnBackPressedDispatcher()).thenReturn(onBackPressedDispatcher); + FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class)); PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(true); PlatformPlugin platformPlugin = - new PlatformPlugin(mockFragmentActivity, mockPlatformChannel, mockPlatformPluginDelegate); + new PlatformPlugin(activity, mockPlatformChannel, mockPlatformPluginDelegate); platformPlugin.mPlatformMessageHandler.popSystemNavigator(); verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator(); // No longer perform the default action when overridden. - verify(mockFragmentActivity, never()).finish(); - verify(mockFragmentActivity, never()).getOnBackPressedDispatcher(); + verify(activity, never()).finish(); } @Test diff --git a/shell/platform/android/test_runner/.gitignore b/shell/platform/android/test_runner/.gitignore new file mode 100644 index 0000000000000..08a55c09bdf37 --- /dev/null +++ b/shell/platform/android/test_runner/.gitignore @@ -0,0 +1 @@ +.gradle diff --git a/shell/platform/android/test_runner/build.gradle b/shell/platform/android/test_runner/build.gradle new file mode 100644 index 0000000000000..704dc7c5b10c9 --- /dev/null +++ b/shell/platform/android/test_runner/build.gradle @@ -0,0 +1,70 @@ +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.0" + } +} + +repositories { + google() + jcenter() +} + +apply plugin: "com.android.library" + +rootProject.buildDir = project.property("build_dir") + +android { + compileSdkVersion 31 + defaultConfig { + minSdkVersion 16 + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + dependencies { + testImplementation files(project.property("flutter_jar")) + testImplementation "androidx.annotation:annotation:1.1.0" + testImplementation "androidx.fragment:fragment:1.1.0" + testImplementation "androidx.lifecycle:lifecycle-runtime:2.2.0" + testImplementation "androidx.lifecycle:lifecycle-common-java8:2.2.0" + testImplementation "androidx.test:core:1.4.0" + testImplementation "com.google.android.play:core:1.8.0" + testImplementation "com.ibm.icu:icu4j:69.1" + testImplementation "org.mockito:mockito-core:3.11.2" + testImplementation "org.robolectric:robolectric:4.6.1" + testImplementation "junit:junit:4.13" + } + + sourceSets { + main { + test { + java { + srcDirs = ["../test"] + } + } + } + } + + testOptions.unitTests.all { + jvmArgs "-Xmx1g" + + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + exceptionFormat "full" + showStackTraces true + } + } +} diff --git a/shell/platform/android/test_runner/gradle.properties b/shell/platform/android/test_runner/gradle.properties new file mode 100644 index 0000000000000..5bac8ac504623 --- /dev/null +++ b/shell/platform/android/test_runner/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX=true diff --git a/shell/platform/android/test_runner/src/main/AndroidManifest.xml b/shell/platform/android/test_runner/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000..d67f4e5760241 --- /dev/null +++ b/shell/platform/android/test_runner/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/shell/platform/android/test_runner/src/main/resources/robolectric.properties b/shell/platform/android/test_runner/src/main/resources/robolectric.properties new file mode 100644 index 0000000000000..932b01b9ebc49 --- /dev/null +++ b/shell/platform/android/test_runner/src/main/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 diff --git a/testing/run_tests.py b/testing/run_tests.py index 2fab89d1d19c1..20439a7639496 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -294,17 +294,6 @@ def EnsureDebugUnoptSkyPackagesAreBuilt(): assert os.path.exists(variant_out_dir), final_message -def EnsureJavaTestsAreBuilt(android_out_dir): - """Builds the engine variant and the test jar containing the JUnit tests""" - tmp_out_dir = os.path.join(out_dir, android_out_dir) - message = [] - message.append('gn --android --unoptimized --runtime-mode=debug --no-lto') - message.append('ninja -C %s flutter/shell/platform/android:robolectric_tests' % android_out_dir) - final_message = '%s doesn\'t exist. Please run the following commands: \n%s' % ( - android_out_dir, '\n'.join(message)) - assert os.path.exists(tmp_out_dir), final_message - - def EnsureIosTestsAreBuilt(ios_out_dir): """Builds the engine variant and the test dylib containing the XCTests""" tmp_out_dir = os.path.join(out_dir, ios_out_dir) @@ -324,41 +313,42 @@ def AssertExpectedXcodeVersion(): assert match, message -def JavaBin(): +def JavaHome(): script_path = os.path.dirname(os.path.realpath(__file__)) if IsMac(): - return os.path.join(script_path, '..', '..', 'third_party', 'java', 'openjdk', 'Contents', 'Home', 'bin', 'java') - elif IsWindows(): - return os.path.join(script_path, '..', '..', 'third_party', 'java', 'openjdk', 'bin', 'java.exe') - else : - return os.path.join(script_path, '..', '..', 'third_party', 'java', 'openjdk', 'bin', 'java') + return os.path.join(script_path, '..', '..', 'third_party', 'java', 'openjdk', 'Contents', 'Home') + else: + return os.path.join(script_path, '..', '..', 'third_party', 'java', 'openjdk') + + +def JavaBin(): + return os.path.join(JavaHome(), 'bin', 'java.exe' if IsWindows() else 'java') def RunJavaTests(filter, android_variant='android_debug_unopt'): """Runs the Java JUnit unit tests for the Android embedding""" - android_out_dir = os.path.join(out_dir, android_variant) - EnsureJavaTestsAreBuilt(android_out_dir) - - embedding_deps_dir = os.path.join(buildroot_dir, 'third_party', 'android_embedding_dependencies', 'lib') - classpath = list(map(str, [ - os.path.join(buildroot_dir, 'third_party', 'android_tools', 'sdk', 'platforms', 'android-31', 'android.jar'), - os.path.join(embedding_deps_dir, '*'), # Wildcard for all jars in the directory - os.path.join(android_out_dir, 'flutter.jar'), - os.path.join(android_out_dir, 'robolectric_tests.jar') - ])) + test_runner_dir = os.path.join(buildroot_dir, 'flutter', 'shell', 'platform', 'android', 'test_runner') + gradle_bin = os.path.join(buildroot_dir, 'gradle', 'bin', 'gradle.bat' if IsWindows() else 'gradle') + flutter_jar = os.path.join(out_dir, android_variant, 'flutter.jar') + android_home = os.path.join(buildroot_dir, 'third_party', 'android_tools', 'sdk') + build_dir = os.path.join(out_dir, android_variant, 'robolectric_tests', 'build') + gradle_cache_dir = os.path.join(out_dir, android_variant, 'robolectric_tests', '.gradle') test_class = filter if filter else 'io.flutter.FlutterTestSuite' command = [ - JavaBin(), - '-Drobolectric.offline=true', - '-Drobolectric.dependency.dir=' + embedding_deps_dir, - '-classpath', ':'.join(classpath), - '-Drobolectric.logging=stdout', - 'org.junit.runner.JUnitCore', - test_class + gradle_bin, + '-Pflutter_jar=%s' % flutter_jar, + '-Pbuild_dir=%s' % build_dir, + 'testDebugUnitTest', + '--tests=%s' % test_class, + '--rerun-tasks', + '--no-daemon', + '--project-cache-dir=%s' % gradle_cache_dir, + '--gradle-user-home=%s' % gradle_cache_dir, ] - RunCmd(command) + env = dict(os.environ, ANDROID_HOME=android_home, JAVA_HOME=JavaHome()) + RunCmd(command, cwd=test_runner_dir, env=env) def RunObjcTests(ios_variant='ios_debug_sim_unopt', test_filter=None): diff --git a/tools/licenses/lib/main.dart b/tools/licenses/lib/main.dart index 06898a8596d12..03ab0e79d886d 100644 --- a/tools/licenses/lib/main.dart +++ b/tools/licenses/lib/main.dart @@ -927,6 +927,7 @@ class _RepositoryDirectory extends _RepositoryEntry implements LicenseSource { entry.name != 'javatests' && entry.name != 'test' && entry.name != 'test.disabled' && + entry.name != 'test_runner' && entry.name != 'test_support' && entry.name != 'testdata' && entry.name != 'tests' &&