From bbb35d759efa65745edf91087e368bd29ed7f7a4 Mon Sep 17 00:00:00 2001 From: utzcoz Date: Sat, 30 Apr 2022 14:35:42 +0800 Subject: [PATCH 1/4] Bump Robolectric to 4.8.1 Bump sdk to 32. Signed-off-by: utzcoz --- shell/platform/android/test/README.md | 2 +- shell/platform/android/test_runner/build.gradle | 2 +- .../test_runner/src/main/resources/robolectric.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/android/test/README.md b/shell/platform/android/test/README.md index 794c9300a9fe4..01cbb0a48c59f 100644 --- a/shell/platform/android/test/README.md +++ b/shell/platform/android/test/README.md @@ -1,6 +1,6 @@ # Unit testing Java code -All Java code in the engine should now be able to be tested with Robolectric 4.7.3 +All Java code in the engine should now be able to be tested with Robolectric 4.8.1 and JUnit 4. The test suite has been added after the bulk of the Java code was first written, so most of these classes do not have existing tests. Ideally code after this point should be tested, either with unit tests here or with diff --git a/shell/platform/android/test_runner/build.gradle b/shell/platform/android/test_runner/build.gradle index d1dfa4cc8d37b..3e5a7c6c4c9a8 100644 --- a/shell/platform/android/test_runner/build.gradle +++ b/shell/platform/android/test_runner/build.gradle @@ -70,7 +70,7 @@ android { 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.robolectric:robolectric:4.7.3" + testImplementation "org.robolectric:robolectric:4.8.1" testImplementation "junit:junit:4.13.2" testImplementation "androidx.test.ext:junit:1.1.4-alpha07" diff --git a/shell/platform/android/test_runner/src/main/resources/robolectric.properties b/shell/platform/android/test_runner/src/main/resources/robolectric.properties index ba72a7e57eb78..ac3b918cdd7d5 100644 --- a/shell/platform/android/test_runner/src/main/resources/robolectric.properties +++ b/shell/platform/android/test_runner/src/main/resources/robolectric.properties @@ -1,2 +1,2 @@ -sdk=31 +sdk=32 shadows=io.flutter.CustomShadowContextImpl From 24d1099baa7104f104aa42e914e6d06114bdf5a8 Mon Sep 17 00:00:00 2001 From: utzcoz Date: Sat, 18 Jun 2022 19:08:39 +0800 Subject: [PATCH 2/4] Fix disposeNullAndroidView with Robolectric 4.8.1 Before Robolectric 4.8, Surface#lockHardwareCanvas will throw exception at PlatformViewWrapper#setTexture, because Robolectric doesn't support to shadow Surface#lockHardwareCanvas, and it uses real Android logic with native pointer address is 0. This failure will ensure embeddedView's parent is null, because PlatformViewsController#createForTextureLayer will fail because of previous mentioned error, and PlatformViewsController#createForTextureLayer will not add embeddedView to wrapperView. So this test can pass. From Robolectric 4.8, it supports to shadow Surface#lockHardwareCanvas and it can pass with default true valid value, and PlatformViewsController#createForTextureLayer will run correctly and add embeddedView to wrapperView, and initializePlatformViewIfNeeded will fail because embeddedView's parent is not null. So adding a new shadow class called ShadowReleasedSurface to simulate previous Surface#lockHardwareCanvas failure to ensure this test can work with Robolectric 4.8 and later versions. But it is just a workaround, the root cause is this test case depends on just-failure behavior of Surface#lockHardwareCanvas in old Robolectric. Signed-off-by: utzcoz --- .../platform/PlatformViewsControllerTest.java | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java index 824c148a70086..746be7fb52650 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -10,6 +10,7 @@ import android.content.Context; import android.content.MutableContextWrapper; import android.content.res.AssetManager; +import android.graphics.Canvas; import android.graphics.SurfaceTexture; import android.util.SparseArray; import android.view.MotionEvent; @@ -53,6 +54,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowSurface; import org.robolectric.shadows.ShadowSurfaceView; @Config(manifest = Config.NONE) @@ -691,7 +693,12 @@ public void disposeAndroidView__hybridComposition() { } @Test - @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class}) + @Config( + shadows = { + ShadowFlutterJNI.class, + ShadowReleasedSurface.class, + ShadowPlatformTaskQueue.class + }) public void disposeNullAndroidView() { PlatformViewsController platformViewsController = new PlatformViewsController(); @@ -711,6 +718,20 @@ public void disposeNullAndroidView() { attach(jni, platformViewsController); // Simulate create call from the framework. + // Before Robolectric 4.8, Surface#lockHardwareCanvas will throw exception at + // PlatformViewWrapper#setTexture, because Robolectric doesn't support to shadow + // Surface#lockHardwareCanvas, and it uses real Android logic with native pointer address is 0. + // This failure will ensure embeddedView's parent is null, because + // PlatformViewsController#createForTextureLayer will fail because of previous mentioned error, + // and PlatformViewsController#createForTextureLayer will not add embeddedView to wrapperView. + // So this test can pass. From Robolectric 4.8, it supports to shadow Surface#lockHardwareCanvas + // and it can pass with default true valid value, and + // PlatformViewsController#createForTextureLayer will run correctly and add embeddedView to + // wrapperView, and initializePlatformViewIfNeeded will fail because embeddedView's parent is + // not null. So adding a new shadow class called ShadowReleasedSurface to simulate previous + // Surface#lockHardwareCanvas failure to ensure this test can work with Robolectric 4.8 and + // later versions. But it is just a workaround, the root cause is this test case depends on + // just-failure behavior of Surface#lockHardwareCanvas in old Robolectric. createPlatformView( jni, platformViewsController, platformViewId, "testType", /* hybrid=*/ false); platformViewsController.initializePlatformViewIfNeeded(platformViewId); @@ -1339,6 +1360,22 @@ public void dispatch(Runnable runnable) { } } + /** + * The shadow class of {@link Surface} to simulate released surface. + * + *

This shadow class's usage is restricted, not for normal purpose. + */ + @Implements(Surface.class) + public static class ShadowReleasedSurface extends ShadowSurface { + public ShadowReleasedSurface() {} + + @Implementation + @Override + protected Canvas lockHardwareCanvas() { + throw new IllegalStateException("Surface has already been released."); + } + } + @Implements(FlutterJNI.class) public static class ShadowFlutterJNI { private static SparseArray replies = new SparseArray<>(); From 1ad4b4d1753fc01ce7697eb9e4e5ef5e939cdc72 Mon Sep 17 00:00:00 2001 From: utzcoz Date: Sat, 1 Oct 2022 11:26:40 +0800 Subject: [PATCH 3/4] Add custom ShadowPresentation to avoid crash for tests Signed-off-by: utzcoz --- .../platform/PlatformViewsControllerTest.java | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java index 746be7fb52650..4a331942da30b 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -7,6 +7,7 @@ import static org.mockito.Mockito.*; import static org.robolectric.Shadows.shadowOf; +import android.app.Presentation; import android.content.Context; import android.content.MutableContextWrapper; import android.content.res.AssetManager; @@ -54,6 +55,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowDialog; import org.robolectric.shadows.ShadowSurface; import org.robolectric.shadows.ShadowSurfaceView; @@ -458,7 +460,7 @@ public void createHybridPlatformViewMessage__throwsIfViewIsNull() { } @Test - @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class}) + @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class, ShadowPresentation.class}) public void onDetachedFromJNI_clearsPlatformViewContext() { PlatformViewsController platformViewsController = new PlatformViewsController(); @@ -489,7 +491,7 @@ public void onDetachedFromJNI_clearsPlatformViewContext() { } @Test - @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class}) + @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class, ShadowPresentation.class}) public void onPreEngineRestart_clearsPlatformViewContext() { PlatformViewsController platformViewsController = new PlatformViewsController(); @@ -1376,6 +1378,34 @@ protected Canvas lockHardwareCanvas() { } } + /** + * The shadow class of {@link Presentation} to simulate Presentation showing logic. + * + *

Robolectric doesn't support VirtualDisplay creating correctly now, so this shadow + * class is used to simulate custom logic for Presentation. + */ + @Implements(Presentation.class) + public static class ShadowPresentation extends ShadowDialog { + private boolean isShowing = false; + + public ShadowPresentation() {} + + @Implementation + protected void show() { + isShowing = true; + } + + @Implementation + protected void dismiss() { + isShowing = false; + } + + @Implementation + protected boolean isShowing() { + return isShowing; + } + } + @Implements(FlutterJNI.class) public static class ShadowFlutterJNI { private static SparseArray replies = new SparseArray<>(); From f0bee40397b28a602ee6ceb7cb159676a7e8cedc Mon Sep 17 00:00:00 2001 From: utzcoz Date: Sat, 1 Oct 2022 12:55:59 +0800 Subject: [PATCH 4/4] Fix code formatting Signed-off-by: utzcoz --- .../platform/PlatformViewsControllerTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java index 4a331942da30b..6703377d97fda 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -460,7 +460,8 @@ public void createHybridPlatformViewMessage__throwsIfViewIsNull() { } @Test - @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class, ShadowPresentation.class}) + @Config( + shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class, ShadowPresentation.class}) public void onDetachedFromJNI_clearsPlatformViewContext() { PlatformViewsController platformViewsController = new PlatformViewsController(); @@ -491,7 +492,8 @@ public void onDetachedFromJNI_clearsPlatformViewContext() { } @Test - @Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class, ShadowPresentation.class}) + @Config( + shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class, ShadowPresentation.class}) public void onPreEngineRestart_clearsPlatformViewContext() { PlatformViewsController platformViewsController = new PlatformViewsController(); @@ -1380,9 +1382,9 @@ protected Canvas lockHardwareCanvas() { /** * The shadow class of {@link Presentation} to simulate Presentation showing logic. - * - *

Robolectric doesn't support VirtualDisplay creating correctly now, so this shadow - * class is used to simulate custom logic for Presentation. + * + *

Robolectric doesn't support VirtualDisplay creating correctly now, so this shadow class is + * used to simulate custom logic for Presentation. */ @Implements(Presentation.class) public static class ShadowPresentation extends ShadowDialog { @@ -1392,7 +1394,7 @@ public ShadowPresentation() {} @Implementation protected void show() { - isShowing = true; + isShowing = true; } @Implementation