diff --git a/.ci.yaml b/.ci.yaml index 5e1b77939a137..9d7d0797cd019 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -100,16 +100,24 @@ targets: timeout: 60 - name: Linux Android Emulator Tests - bringup: true # Recipe issue https://github.com/flutter/flutter/issues/86427 recipe: engine/scenarios + enabled_branches: + - main + - master properties: dependencies: >- [ - {"dependency": "android_virtual_device", "version": "31"} + {"dependency": "android_virtual_device", "version": "31"}, + {"dependency": "goldctl"} ] upload_packages: "true" clobber: "true" timeout: 60 + runIf: + - DEPS + - .ci.yaml + - testing/** + - shell/platforms/android/** - name: Linux Benchmarks enabled_branches: diff --git a/shell/platform/android/android_context_gl_unittests.cc b/shell/platform/android/android_context_gl_unittests.cc index 6edb19ff5067c..bf1c7a78407ec 100644 --- a/shell/platform/android/android_context_gl_unittests.cc +++ b/shell/platform/android/android_context_gl_unittests.cc @@ -164,7 +164,8 @@ TEST(AndroidSurfaceGL, CreateSnapshopSurfaceWhenOnscreenSurfaceIsNull) { EXPECT_NE(android_surface->GetOnscreenSurface(), nullptr); } -TEST(AndroidContextGl, MSAAx4) { +// TODO(https://github.com/flutter/flutter/issues/104463): Flaky test. +TEST(AndroidContextGl, DISABLED_MSAAx4) { GrMockOptions main_context_options; sk_sp main_context = GrDirectContext::MakeMock(&main_context_options); diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index b099be6182018..a9417ebb16211 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -98,6 +98,9 @@ void AndroidExternalViewEmbedder::SubmitFrame( for (size_t i = 0; i < current_frame_view_count; i++) { int64_t view_id = composition_order_[i]; + if (picture_recorders_.at(view_id)->getRecordingCanvas() == nullptr) { + continue; + } sk_sp picture = picture_recorders_.at(view_id)->finishRecordingAsPicture(); diff --git a/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenariosui/ScreenshotUtil.java b/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenariosui/ScreenshotUtil.java index 09e7605ca2147..10272513d053b 100644 --- a/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenariosui/ScreenshotUtil.java +++ b/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenariosui/ScreenshotUtil.java @@ -37,8 +37,10 @@ private static class Connection { } synchronized void writeFile(String name, byte[] fileContent) throws IOException { - final ByteBuffer buffer = ByteBuffer.allocate(name.length() + fileContent.length + 4); + final ByteBuffer buffer = ByteBuffer.allocate(name.length() + fileContent.length + 8); + // See ScreenshotBlobTransformer#bind in screenshot_transformer.dart for consumer side. buffer.putInt(name.length()); + buffer.putInt(fileContent.length); buffer.put(name.getBytes()); buffer.put(fileContent); final byte[] bytes = buffer.array(); @@ -118,6 +120,9 @@ public static void capture(@NonNull TestableFlutterActivity activity, @NonNull S final Bitmap bitmap = InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot(); + if (bitmap == null) { + throw new RuntimeException("failed to capture screenshot"); + } final ByteArrayOutputStream out = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); ScreenshotUtil.writeFile(captureName, out.toByteArray()); diff --git a/testing/scenario_app/android/app/src/main/AndroidManifest.xml b/testing/scenario_app/android/app/src/main/AndroidManifest.xml index 31339af1c5c58..bbd62a00015cc 100644 --- a/testing/scenario_app/android/app/src/main/AndroidManifest.xml +++ b/testing/scenario_app/android/app/src/main/AndroidManifest.xml @@ -14,7 +14,6 @@ android:hardwareAccelerated="true" android:launchMode="singleTop" android:windowSoftInputMode="adjustResize" - android:theme="@style/FullScreenScreenshot" android:exported="true"> @@ -32,7 +31,6 @@ android:hardwareAccelerated="true" android:launchMode="singleTop" android:windowSoftInputMode="adjustResize" - android:theme="@style/FullScreenScreenshot" android:exported="true"> @@ -45,7 +43,6 @@ android:hardwareAccelerated="true" android:launchMode="singleTop" android:windowSoftInputMode="adjustResize" - android:theme="@style/FullScreenScreenshot" android:exported="true"> diff --git a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TestActivity.java b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TestActivity.java index adf776d8ac638..80323a9a41944 100644 --- a/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TestActivity.java +++ b/testing/scenario_app/android/app/src/main/java/dev/flutter/scenarios/TestActivity.java @@ -12,8 +12,12 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.view.Window; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.view.WindowInsetsControllerCompat; import io.flutter.Log; import io.flutter.embedding.engine.FlutterShellArgs; import io.flutter.embedding.engine.loader.FlutterLoader; @@ -35,6 +39,8 @@ public abstract class TestActivity extends TestableFlutterActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + hideSystemBars(getWindow()); + final Intent launchIntent = getIntent(); if ("com.google.intent.action.TEST_LOOP".equals(launchIntent.getAction())) { if (Build.VERSION.SDK_INT > 22) { @@ -158,4 +164,12 @@ public void run() { } }); } + + private static void hideSystemBars(Window window) { + final WindowInsetsControllerCompat insetController = + WindowCompat.getInsetsController(window, window.getDecorView()); + insetController.setSystemBarsBehavior( + WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + insetController.hide(WindowInsetsCompat.Type.systemBars()); + } } diff --git a/testing/scenario_app/android/app/src/main/res/values/styles.xml b/testing/scenario_app/android/app/src/main/res/values/styles.xml index 641ad62a6137b..5885930df6d10 100644 --- a/testing/scenario_app/android/app/src/main/res/values/styles.xml +++ b/testing/scenario_app/android/app/src/main/res/values/styles.xml @@ -8,11 +8,4 @@ @color/colorAccent - - diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCanvasSurface.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCanvasSurface.png deleted file mode 100644 index 63887d8374c76..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCanvasSurface.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedMediaSurface_bottomLeft.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedMediaSurface_bottomLeft.png deleted file mode 100644 index 264de7bf41057..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedMediaSurface_bottomLeft.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedMediaSurface_topRight.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedMediaSurface_topRight.png deleted file mode 100644 index 5a3c9edfe31db..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedMediaSurface_topRight.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedRotatedMediaSurface_bottomLeft_90.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedRotatedMediaSurface_bottomLeft_90.png deleted file mode 100644 index cb69c0ac77860..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testCroppedRotatedMediaSurface_bottomLeft_90.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testMediaSurface.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testMediaSurface.png deleted file mode 100644 index f1b664cadb03b..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testMediaSurface.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_180.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_180.png deleted file mode 100644 index cb4f76db9bbc3..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_180.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_270.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_270.png deleted file mode 100644 index aa0a6c12d4ed4..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_270.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_90.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_90.png deleted file mode 100644 index 07f046beee1df..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.ExternalTextureTests__testRotatedMediaSurface_90.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformView.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformView.png deleted file mode 100644 index e306303d2df03..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformView.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewClippath.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewClippath.png deleted file mode 100644 index 8b3ce0b747cbf..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewClippath.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewCliprect.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewCliprect.png deleted file mode 100644 index 8711e785359f9..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewCliprect.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewCliprrect.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewCliprrect.png deleted file mode 100644 index bd84e34a7d81b..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewCliprrect.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultiple.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultiple.png deleted file mode 100644 index 2efaa30868b5a..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultiple.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultipleBackgroundForeground.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultipleBackgroundForeground.png deleted file mode 100644 index 0383f6a2cdc70..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultipleBackgroundForeground.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultipleWithoutOverlays.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultipleWithoutOverlays.png deleted file mode 100644 index 8a7fa8caf5829..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewMultipleWithoutOverlays.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewOpacity.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewOpacity.png deleted file mode 100644 index 8d37c9dff3a9e..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewOpacity.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewRotate.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewRotate.png deleted file mode 100644 index 3639a3b20be56..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewRotate.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewTransform.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewTransform.png deleted file mode 100644 index 5f5f2b5131365..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewTransform.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewTwoIntersectingOverlays.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewTwoIntersectingOverlays.png deleted file mode 100644 index f127ffcaa22cf..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewTwoIntersectingOverlays.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewWithoutOverlayIntersection.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewWithoutOverlayIntersection.png deleted file mode 100644 index 7970d1375e0a9..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformTextureUiTests__testPlatformViewWithoutOverlayIntersection.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformView.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformView.png deleted file mode 100644 index 9ea668b4cfaaf..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformView.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewClippath.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewClippath.png deleted file mode 100644 index 57de58c271a97..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewClippath.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewCliprect.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewCliprect.png deleted file mode 100644 index df00de86f3f3d..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewCliprect.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewCliprrect.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewCliprrect.png deleted file mode 100644 index 4b39abeb9b196..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewCliprrect.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultiple.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultiple.png deleted file mode 100644 index 9d88a5df52c6c..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultiple.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultipleBackgroundForeground.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultipleBackgroundForeground.png deleted file mode 100644 index ded8f3d9d0231..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultipleBackgroundForeground.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultipleWithoutOverlays.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultipleWithoutOverlays.png deleted file mode 100644 index 8e6da85fa9448..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewMultipleWithoutOverlays.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewOpacity.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewOpacity.png deleted file mode 100644 index 433522b13023f..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewOpacity.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewRotate.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewRotate.png deleted file mode 100644 index 6fb9339445833..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewRotate.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewTransform.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewTransform.png deleted file mode 100644 index 628443c49aa72..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewTransform.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewTwoIntersectingOverlays.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewTwoIntersectingOverlays.png deleted file mode 100644 index 1c6ed1dc1df84..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewTwoIntersectingOverlays.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewWithoutOverlayIntersection.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewWithoutOverlayIntersection.png deleted file mode 100644 index f0e71fb9ed8c7..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.PlatformViewUiTests__testPlatformViewWithoutOverlayIntersection.png and /dev/null differ diff --git a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.SpawnEngineTests__testSpawnedEngine.png b/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.SpawnEngineTests__testSpawnedEngine.png deleted file mode 100644 index 381f4e75d311e..0000000000000 Binary files a/testing/scenario_app/android/reports/screenshots/dev.flutter.scenariosui.SpawnEngineTests__testSpawnedEngine.png and /dev/null differ diff --git a/testing/scenario_app/bin/android_integration_tests.dart b/testing/scenario_app/bin/android_integration_tests.dart index 4623b0880a1f5..6d6ed4d3025f6 100644 --- a/testing/scenario_app/bin/android_integration_tests.dart +++ b/testing/scenario_app/bin/android_integration_tests.dart @@ -3,16 +3,17 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:args/args.dart'; import 'package:path/path.dart'; import 'package:process/process.dart'; +import 'package:skia_gold_client/skia_gold_client.dart'; import 'utils/logs.dart'; import 'utils/process_manager_extension.dart'; +import 'utils/screenshot_transformer.dart'; const int tcpPort = 3001; @@ -34,9 +35,12 @@ void main(List args) async { panic(['cannot find adb: $adb', 'make sure to run gclient sync']); } - final String apkOut = join(outDir.path, 'scenario_app', 'app', 'outputs', 'apk'); - final File testApk = File(join(apkOut, 'androidTest', 'debug', 'app-debug-androidTest.apk')); - final File appApk = File(join(apkOut, 'debug', 'app-debug.apk')); + final String scenarioAppPath = join(outDir.path, 'scenario_app'); + final String logcatPath = join(scenarioAppPath, 'logcat.txt'); + final String screenshotPath = join(scenarioAppPath, 'screenshots'); + final String apkOutPath = join(scenarioAppPath, 'app', 'outputs', 'apk'); + final File testApk = File(join(apkOutPath, 'androidTest', 'debug', 'app-debug-androidTest.apk')); + final File appApk = File(join(apkOutPath, 'debug', 'app-debug.apk')); if (!testApk.existsSync()) { panic(['test apk does not exist: ${testApk.path}', 'make sure to build the selected engine variant']); @@ -50,30 +54,97 @@ void main(List args) async { // This allows the test process to start a connection with the host, and write the bytes // for the screenshots. // On LUCI, the host uploads the screenshots to Skia Gold. + SkiaGoldClient? skiaGoldClient; late ServerSocket server; + final List> pendingComparisons = >[]; await step('Starting server...', () async { server = await ServerSocket.bind(InternetAddress.anyIPv4, tcpPort); stdout.writeln('listening on host ${server.address.address}:${server.port}'); server.listen((Socket client) { stdout.writeln('client connected ${client.remoteAddress.address}:${client.remotePort}'); - - client.listen((Uint8List data) { - final int fnameLen = data.buffer.asByteData().getInt32(0); - final String fileName = utf8.decode(data.buffer.asUint8List(4, fnameLen)); - final Uint8List fileContent = data.buffer.asUint8List(4 + fnameLen); + client.transform(const ScreenshotBlobTransformer()).listen((Screenshot screenshot) { + final String fileName = screenshot.filename; + final Uint8List fileContent = screenshot.fileContent; log('host received ${fileContent.lengthInBytes} bytes for screenshot `$fileName`'); - }); + assert(skiaGoldClient != null, 'expected Skia Gold client'); + late File goldenFile; + try { + goldenFile = File(join(screenshotPath, fileName))..writeAsBytesSync(fileContent, flush: true); + } on FileSystemException catch (err) { + panic(['failed to create screenshot $fileName: ${err.toString()}']); + } + log('wrote ${goldenFile.absolute.path}'); + if (isSkiaGoldClientAvailable) { + final Future comparison = skiaGoldClient! + .addImg(fileName, goldenFile, screenshotSize: fileContent.lengthInBytes) + .catchError((dynamic err) { + panic(['skia gold comparison failed: ${err.toString()}']); + }); + pendingComparisons.add(comparison); + } + }, + onError: (dynamic err) { + panic(['error while receiving bytes: ${err.toString()}']); + }, + cancelOnError: true); }); }); late Process logcatProcess; - final StringBuffer logcat = StringBuffer(); + final IOSink logcat = File(logcatPath).openWrite(); try { + await step('Creating screenshot directory...', () async { + Directory(screenshotPath).createSync(recursive: true); + }); + await step('Starting logcat...', () async { - logcatProcess = await pm.start([adb.path, 'logcat', '*:E', '-T', '1']); + final int exitCode = await pm.runAndForward([adb.path, 'logcat', '-c']); + if (exitCode != 0) { + panic(['could not clear logs']); + } + logcatProcess = await pm.start([adb.path, 'logcat', '-T', '1']); unawaited(pipeProcessStreams(logcatProcess, out: logcat)); }); + await step('Configuring emulator...', () async { + final int exitCode = await pm.runAndForward([ + adb.path, + 'shell', + 'settings', + 'put', + 'secure', + 'immersive_mode_confirmations', + 'confirmed', + ]); + if (exitCode != 0) { + panic(['could not configure emulator']); + } + }); + + await step('Get API level of connected device...', () async { + final ProcessResult apiLevelProcessResult = await pm.run([adb.path, 'shell', 'getprop', 'ro.build.version.sdk']); + if (apiLevelProcessResult.exitCode != 0) { + panic(['could not get API level of the connected device']); + } + final String connectedDeviceAPILevel = (apiLevelProcessResult.stdout as String).trim(); + log('using API level $connectedDeviceAPILevel'); + skiaGoldClient = SkiaGoldClient( + outDir, + dimensions: { + 'AndroidAPILevel': connectedDeviceAPILevel, + }, + ); + }); + + await step('Skia Gold auth...', () async { + if (isSkiaGoldClientAvailable) { + await skiaGoldClient!.auth(); + log('skia gold client is available'); + } else { + log('skia gold client is unavailable'); + } + }); + await step('Reverse port...', () async { final int exitCode = await pm.runAndForward([adb.path, 'reverse', 'tcp:3000', 'tcp:$tcpPort']); if (exitCode != 0) { @@ -136,11 +207,18 @@ void main(List args) async { }); await step('Killing logcat process...', () async { - logcatProcess.kill(); + final bool delivered = logcatProcess.kill(ProcessSignal.sigkill); + assert(delivered); }); - await step('Dumping logcat (Errors only)...', () async { - stdout.write(logcat); + await step('Wait for Skia gold comparisons...', () async { + await Future.wait(pendingComparisons); }); + + await step('Flush logcat...', () async { + await logcat.flush(); + }); + + exit(0); } } diff --git a/testing/scenario_app/bin/utils/process_manager_extension.dart b/testing/scenario_app/bin/utils/process_manager_extension.dart index 818bf19b6c84a..6d77c99f7cc46 100644 --- a/testing/scenario_app/bin/utils/process_manager_extension.dart +++ b/testing/scenario_app/bin/utils/process_manager_extension.dart @@ -8,15 +8,26 @@ import 'dart:io'; import 'package:process/process.dart'; -/// Pipes the [process] streams and writes them to [out]. -Future pipeProcessStreams(Process process, {StringSink? out}) async { +/// Pipes the [process] streams and writes them to [out] sink. +/// If [out] is null, then the current [Process.stdout] is used as the sink. +/// If [includePrefix] is true, then the prefix `[stdout]` or `[stderr]` is +/// added before writting to the [out] sink. +Future pipeProcessStreams( + Process process, { + StringSink? out, + bool includePrefix = true, +}) async { out ??= stdout; final Completer stdoutCompleter = Completer(); final StreamSubscription stdoutSub = process.stdout .transform(utf8.decoder) .transform(const LineSplitter()) .listen((String line) { - out!.writeln('[stdout] $line'); + if (includePrefix) { + out!.writeln('[stdout] $line'); + } else { + out!.writeln(line); + } }, onDone: stdoutCompleter.complete); final Completer stderrCompleter = Completer(); @@ -24,15 +35,19 @@ Future pipeProcessStreams(Process process, {StringSink? out}) async { .transform(utf8.decoder) .transform(const LineSplitter()) .listen((String line) { - out!.writeln('[stderr] $line'); + if (includePrefix) { + out!.writeln('[stderr] $line'); + } else { + out!.writeln(line); + } }, onDone: stderrCompleter.complete); final int exitCode = await process.exitCode; + await stderrSub.cancel(); + await stdoutSub.cancel(); + await stdoutCompleter.future; await stderrCompleter.future; - - stderrSub.cancel(); - stdoutSub.cancel(); return exitCode; } diff --git a/testing/scenario_app/bin/utils/screenshot_transformer.dart b/testing/scenario_app/bin/utils/screenshot_transformer.dart new file mode 100644 index 0000000000000..9b8df01805136 --- /dev/null +++ b/testing/scenario_app/bin/utils/screenshot_transformer.dart @@ -0,0 +1,66 @@ +// Copyright 2013 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:async'; +import 'dart:convert'; +import 'dart:typed_data'; + +/// A screenshot from the Android emulator. +class Screenshot { + Screenshot(this.filename, this.fileContent); + + /// The name of the screenshot. + final String filename; + + /// The binary content of the screenshot. + final Uint8List fileContent; +} + +/// Takes the input stream and transforms it into [Screenshot]s. +class ScreenshotBlobTransformer extends StreamTransformerBase { + const ScreenshotBlobTransformer(); + + @override + Stream bind(Stream stream) async* { + final BytesBuilder pending = BytesBuilder(); + + await for (final Uint8List blob in stream) { + pending.add(blob); + + if (pending.length < 8) { + continue; + } + + // See ScreenshotUtil#writeFile in ScreenshotUtil.java for producer side. + final Uint8List bytes = pending.toBytes(); + final ByteData byteData = bytes.buffer.asByteData(); + + int off = 0; + final int fnameLen = byteData.getInt32(off); + off += 4; + final int fcontentLen = byteData.getInt32(off); + off += 4; + + assert(fnameLen > 0); + assert(fcontentLen > 0); + + if (pending.length < off + fnameLen) { + continue; + } + + final String filename = utf8.decode(bytes.buffer.asUint8List(off, fnameLen)); + off += fnameLen; + if (pending.length < off + fcontentLen) { + continue; + } + + final Uint8List fileContent = bytes.buffer.asUint8List(off, fcontentLen); + off += fcontentLen; + pending.clear(); + pending.add(bytes.buffer.asUint8List(off)); + + yield Screenshot('$filename.png', fileContent); + } + } +} diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_non_full_screen_flutter_view_platform_view_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_non_full_screen_flutter_view_platform_view_iPhone 8_13.0_simulator.png index a8cf03250295a..e0b47938d81e6 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_non_full_screen_flutter_view_platform_view_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_non_full_screen_flutter_view_platform_view_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_13.0_simulator.png index 30072dc621646..3ef6f2d11242b 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_iPhone 8_13.0_simulator.png index 05eff9045b0af..88949fa984cad 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprect_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_iPhone 8_13.0_simulator.png index 69ba03a131136..b91db0cea100e 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_cliprrect_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_iPhone 8_13.0_simulator.png index ac6a65f57158b..94fdf8971cbdf 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_opacity_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_opacity_iPhone 8_13.0_simulator.png index f0683547ccf7a..eedbca75223a4 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_opacity_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_opacity_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone 8_13.0_simulator.png index 2c5f5b8accd85..517330b8dbd17 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_rotate_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_13.0_simulator.png index 38ed2177955e9..0fd4774537618 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_with_other_backdrop_filter_iPhone 8_13.0_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_with_other_backdrop_filter_iPhone 8_13.0_simulator.png index 16417fb5f2a10..6ea69adb6d0a6 100644 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_with_other_backdrop_filter_iPhone 8_13.0_simulator.png and b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_with_other_backdrop_filter_iPhone 8_13.0_simulator.png differ diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 598149849ddaa..a7a457be6524c 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - +import 'dart:async'; import 'dart:convert'; import 'dart:developer' as developer; import 'dart:io'; @@ -11,7 +11,6 @@ import 'dart:ui'; import 'src/scenarios.dart'; - void main() { assert(window.locale != null); window @@ -84,15 +83,15 @@ Future> _getJson(Uri uri) async { } void _onBeginFrame(Duration duration) { - currentScenario?.onBeginFrame(duration); - // Render an empty frame to signal first frame in the platform side. if (currentScenario == null) { final SceneBuilder builder = SceneBuilder(); final Scene scene = builder.build(); window.render(scene); scene.dispose(); + return; } + currentScenario!.onBeginFrame(duration); } void _onDrawFrame() { diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 488b284c179a3..4f99d01244933 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; @@ -33,12 +34,11 @@ class PlatformViewScenario extends Scenario /// Creates the PlatformView scenario. /// /// The [dispatcher] parameter must not be null. - PlatformViewScenario(PlatformDispatcher dispatcher, String text, - {required this.id}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, id); - } + PlatformViewScenario( + PlatformDispatcher dispatcher, { + required this.id, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier. final int id; @@ -46,8 +46,12 @@ class PlatformViewScenario extends Scenario @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - - finishBuilderByAddingPlatformViewAndPicture(builder, id); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + finishBuilder(builder); } } @@ -58,12 +62,10 @@ class NonFullScreenFlutterViewPlatformViewScenario extends Scenario /// /// The [dispatcher] parameter must not be null. NonFullScreenFlutterViewPlatformViewScenario( - PlatformDispatcher dispatcher, String text, - {required this.id}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, id); - } + PlatformDispatcher dispatcher, { + required this.id, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier. final int id; @@ -71,8 +73,12 @@ class NonFullScreenFlutterViewPlatformViewScenario extends Scenario @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - - finishBuilderByAddingPlatformViewAndPicture(builder, id); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + finishBuilder(builder); } } @@ -83,12 +89,10 @@ class PlatformViewNoOverlayIntersectionScenario extends Scenario /// /// The [dispatcher] parameter must not be null. PlatformViewNoOverlayIntersectionScenario( - PlatformDispatcher dispatcher, String text, - {required this.id}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, id); - } + PlatformDispatcher dispatcher, { + required this.id, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier. final int id; @@ -97,9 +101,14 @@ class PlatformViewNoOverlayIntersectionScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - finishBuilderByAddingPlatformViewAndPicture( - builder, + addPlatformView( id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + + finishBuilder( + builder, overlayOffset: const Offset(150, 350), ); } @@ -112,12 +121,10 @@ class PlatformViewPartialIntersectionScenario extends Scenario /// /// The [dispatcher] parameter must not be null. PlatformViewPartialIntersectionScenario( - PlatformDispatcher dispatcher, String text, - {required this.id}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, id); - } + PlatformDispatcher dispatcher, { + required this.id, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier . final int id; @@ -126,9 +133,14 @@ class PlatformViewPartialIntersectionScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - finishBuilderByAddingPlatformViewAndPicture( - builder, + addPlatformView( id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + + finishBuilder( + builder, overlayOffset: const Offset(150, 250), ); } @@ -141,12 +153,10 @@ class PlatformViewTwoIntersectingOverlaysScenario extends Scenario /// /// The [dispatcher] parameter must not be null. PlatformViewTwoIntersectingOverlaysScenario( - PlatformDispatcher dispatcher, String text, - {required this.id}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, id); - } + PlatformDispatcher dispatcher, { + required this.id, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier. final int id; @@ -155,7 +165,11 @@ class PlatformViewTwoIntersectingOverlaysScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - _addPlatformViewToScene(builder, id, 500, 500); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( @@ -183,12 +197,10 @@ class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario /// /// The [dispatcher] parameter must not be null. PlatformViewOneOverlayTwoIntersectingOverlaysScenario( - PlatformDispatcher dispatcher, String text, - {required this.id}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, id); - } + PlatformDispatcher dispatcher, { + required this.id, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier. final int id; @@ -197,7 +209,12 @@ class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - _addPlatformViewToScene(builder, id, 500, 500); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( @@ -230,13 +247,11 @@ class MultiPlatformViewWithoutOverlaysScenario extends Scenario /// /// The [dispatcher] parameter must not be null. MultiPlatformViewWithoutOverlaysScenario( - PlatformDispatcher dispatcher, String text, - {required this.firstId, required this.secondId}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, firstId); - createPlatformView(dispatcher, text, secondId); - } + PlatformDispatcher dispatcher, { + required this.firstId, + required this.secondId, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier to use for the first platform view. final int firstId; @@ -249,10 +264,22 @@ class MultiPlatformViewWithoutOverlaysScenario extends Scenario final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0, 600); - _addPlatformViewToScene(builder, firstId, 500, 500); + + addPlatformView( + firstId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 1', + ); + builder.pop(); - _addPlatformViewToScene(builder, secondId, 500, 500); + addPlatformView( + secondId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 2', + ); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); @@ -276,12 +303,11 @@ class PlatformViewMaxOverlaysScenario extends Scenario /// Creates the PlatformView scenario. /// /// The [dispatcher] parameter must not be null. - PlatformViewMaxOverlaysScenario(PlatformDispatcher dispatcher, String text, - {required this.id}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, id); - } + PlatformViewMaxOverlaysScenario( + PlatformDispatcher dispatcher, { + required this.id, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier. final int id; @@ -290,7 +316,11 @@ class PlatformViewMaxOverlaysScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - _addPlatformViewToScene(builder, id, 500, 500); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( @@ -327,13 +357,12 @@ class MultiPlatformViewScenario extends Scenario /// Creates the PlatformView scenario. /// /// The [dispatcher] parameter must not be null. - MultiPlatformViewScenario(PlatformDispatcher dispatcher, - {required this.firstId, required this.secondId}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, 'platform view 1', firstId); - createPlatformView(dispatcher, 'platform view 2', secondId); - } + MultiPlatformViewScenario( + PlatformDispatcher dispatcher, { + required this.firstId, + required this.secondId, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier to use for the first platform view. final int firstId; @@ -346,10 +375,24 @@ class MultiPlatformViewScenario extends Scenario final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0, 600); - _addPlatformViewToScene(builder, firstId, 500, 500); + + addPlatformView( + firstId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 1', + ); + builder.pop(); - finishBuilderByAddingPlatformViewAndPicture(builder, secondId); + addPlatformView( + secondId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 2', + ); + + finishBuilder(builder); } } @@ -363,13 +406,13 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario /// Creates the PlatformView scenario. /// /// The [dispatcher] parameter must not be null. - MultiPlatformViewBackgroundForegroundScenario(PlatformDispatcher dispatcher, - {required this.firstId, required this.secondId}) - : assert(dispatcher != null), + MultiPlatformViewBackgroundForegroundScenario( + PlatformDispatcher dispatcher, { + required this.firstId, + required this.secondId, + }) : assert(dispatcher != null), super(dispatcher) { _nextFrame = _firstFrame; - createPlatformView(dispatcher, 'platform view 1', firstId); - createPlatformView(dispatcher, 'platform view 2', secondId); } /// The platform view identifier to use for the first platform view. @@ -378,22 +421,36 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario /// The platform view identifier to use for the second platform view. final int secondId; + late void Function() _nextFrame; + @override void onBeginFrame(Duration duration) { _nextFrame(); } - late VoidCallback _nextFrame; - void _firstFrame() { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(50, 600); - _addPlatformViewToScene(builder, firstId, 500, 500); + + addPlatformView( + firstId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 1', + ); + builder.pop(); builder.pushOffset(50, 0); - _addPlatformViewToScene(builder, secondId, 500, 500); + + addPlatformView( + secondId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 2', + ); + builder.pop(); final PictureRecorder recorder = PictureRecorder(); @@ -414,10 +471,22 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0, 600); - _addPlatformViewToScene(builder, firstId, 500, 500); + + addPlatformView( + firstId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 1', + ); + builder.pop(); - _addPlatformViewToScene(builder, secondId, 500, 500); + addPlatformView( + secondId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 2', + ); final Scene scene = builder.build(); window.render(scene); @@ -447,15 +516,13 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario } /// Platform view with clip rect. -class PlatformViewClipRectScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Constructs a platform view with clip rect scenario. - PlatformViewClipRectScenario(PlatformDispatcher dispatcher, String text, - {required this.id}) - : assert(dispatcher != null), - super(dispatcher) { - createPlatformView(dispatcher, text, id); - } + PlatformViewClipRectScenario( + PlatformDispatcher dispatcher, { + required this.id, + }) : assert(dispatcher != null), + super(dispatcher); /// The platform view identifier. final int id; @@ -465,16 +532,23 @@ class PlatformViewClipRectScenario extends Scenario final SceneBuilder builder = SceneBuilder() ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); - finishBuilderByAddingPlatformViewAndPicture(builder, id); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + + finishBuilder(builder); } } /// Platform view with clip rrect. class PlatformViewClipRRectScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect scenario. - PlatformViewClipRRectScenario(PlatformDispatcher dispatcher, String text, - {int id = 0}) - : super(dispatcher, text, id: id); + PlatformViewClipRRectScenario( + PlatformDispatcher dispatcher, { + int id = 0, + }) : super(dispatcher, id: id); @override void onBeginFrame(Duration duration) { @@ -490,16 +564,24 @@ class PlatformViewClipRRectScenario extends PlatformViewScenario { bottomLeft: const Radius.circular(50), ), ); - finishBuilderByAddingPlatformViewAndPicture(builder, id); + + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + + finishBuilder(builder); } } /// Platform view with clip path. class PlatformViewClipPathScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect scenario. - PlatformViewClipPathScenario(PlatformDispatcher dispatcher, String text, - {int id = 0}) - : super(dispatcher, text, id: id); + PlatformViewClipPathScenario( + PlatformDispatcher dispatcher, { + int id = 0, + }) : super(dispatcher, id: id); @override void onBeginFrame(Duration duration) { @@ -511,16 +593,22 @@ class PlatformViewClipPathScenario extends PlatformViewScenario { ..close(); final SceneBuilder builder = SceneBuilder()..pushClipPath(path); - finishBuilderByAddingPlatformViewAndPicture(builder, id); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + finishBuilder(builder); } } /// Platform view with transform. class PlatformViewTransformScenario extends PlatformViewScenario { /// Constructs a platform view with transform scenario. - PlatformViewTransformScenario(PlatformDispatcher dispatcher, String text, - {int id = 0}) - : super(dispatcher, text, id: id); + PlatformViewTransformScenario( + PlatformDispatcher dispatcher, { + int id = 0, + }) : super(dispatcher, id: id); @override void onBeginFrame(Duration duration) { @@ -530,22 +618,32 @@ class PlatformViewTransformScenario extends PlatformViewScenario { ..translate(1000.0, 100.0, 0.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); - - finishBuilderByAddingPlatformViewAndPicture(builder, id); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + finishBuilder(builder); } } /// Platform view with opacity. class PlatformViewOpacityScenario extends PlatformViewScenario { /// Constructs a platform view with transform scenario. - PlatformViewOpacityScenario(PlatformDispatcher dispatcher, String text, - {int id = 0}) - : super(dispatcher, text, id: id); + PlatformViewOpacityScenario( + PlatformDispatcher dispatcher, { + int id = 0, + }) : super(dispatcher, id: id); @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder()..pushOpacity(150); - finishBuilderByAddingPlatformViewAndPicture(builder, id); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + finishBuilder(builder); } } @@ -555,24 +653,21 @@ class PlatformViewForTouchIOSScenario extends Scenario /// Creates the PlatformView scenario. /// /// The [dispatcher] parameter must not be null. - PlatformViewForTouchIOSScenario(PlatformDispatcher dispatcher, String text, - {int id = 0, required bool accept, bool rejectUntilTouchesEnded = false}) - : assert(dispatcher != null), - _accept = accept, - _viewId = id, - super(dispatcher) { - if (rejectUntilTouchesEnded) { - createPlatformView(dispatcher, text, id, - viewType: 'scenarios/textPlatformView_blockPolicyUntilTouchesEnded'); - } else { - createPlatformView(dispatcher, text, id); - } + PlatformViewForTouchIOSScenario( + PlatformDispatcher dispatcher, { + this.id = 0, + this.rejectUntilTouchesEnded = false, + required this.accept, + }) : assert(dispatcher != null), + super(dispatcher) { _nextFrame = _firstFrame; } - int _viewId; - bool _accept; - late VoidCallback _nextFrame; + late void Function() _nextFrame; + + final bool accept; + final int id; + final bool rejectUntilTouchesEnded; @override void onBeginFrame(Duration duration) { @@ -595,7 +690,7 @@ class PlatformViewForTouchIOSScenario extends Scenario void onPointerDataPacket(PointerDataPacket packet) { if (packet.data.first.change == PointerChange.add) { String method = 'rejectGesture'; - if (_accept) { + if (accept) { method = 'acceptGesture'; } const int _valueString = 7; @@ -611,7 +706,7 @@ class PlatformViewForTouchIOSScenario extends Scenario 'id'.length, ...utf8.encode('id'), _valueInt32, - ..._to32(_viewId), + ..._to32(id), ]); window.sendPlatformMessage( 'flutter/platform_views', @@ -623,12 +718,41 @@ class PlatformViewForTouchIOSScenario extends Scenario void _firstFrame() { final SceneBuilder builder = SceneBuilder(); - finishBuilderByAddingPlatformViewAndPicture(builder, _viewId); + + if (rejectUntilTouchesEnded) { + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + viewType: 'scenarios/textPlatformView_blockPolicyUntilTouchesEnded', + ); + } else { + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + } + finishBuilder(builder); } void _secondFrame() { final SceneBuilder builder = SceneBuilder()..pushOffset(5, 5); - finishBuilderByAddingPlatformViewAndPicture(builder, _viewId); + if (rejectUntilTouchesEnded) { + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + viewType: 'scenarios/textPlatformView_blockPolicyUntilTouchesEnded', + ); + } else { + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + } + finishBuilder(builder); } } @@ -636,9 +760,10 @@ class PlatformViewForTouchIOSScenario extends Scenario /// For example, it simulates a video being played. class PlatformViewWithContinuousTexture extends PlatformViewScenario { /// Constructs a platform view with continuous texture layer. - PlatformViewWithContinuousTexture(PlatformDispatcher dispatcher, String text, - {int id = 0}) - : super(dispatcher, text, id: id); + PlatformViewWithContinuousTexture( + PlatformDispatcher dispatcher, { + int id = 0, + }) : super(dispatcher, id: id); @override void onBeginFrame(Duration duration) { @@ -647,7 +772,13 @@ class PlatformViewWithContinuousTexture extends PlatformViewScenario { builder.addTexture(0, width: 300, height: 300, offset: const Offset(200, 200)); - finishBuilderByAddingPlatformViewAndPicture(builder, id); + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + + finishBuilder(builder); } } @@ -660,9 +791,9 @@ class PlatformViewWithContinuousTexture extends PlatformViewScenario { class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { /// Constructs the scenario. PlatformViewWithOtherBackDropFilter( - PlatformDispatcher dispatcher, String text, - {int id = 0}) - : super(dispatcher, text, id: id); + PlatformDispatcher dispatcher, { + int id = 0, + }) : super(dispatcher, id: id); @override void onBeginFrame(Duration duration) { @@ -700,7 +831,13 @@ class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { builder.pop(); builder.pushOffset(0, 600); - _addPlatformViewToScene(builder, id, 500, 500); + + addPlatformView( + id, + dispatcher: dispatcher, + sceneBuilder: builder, + ); + builder.pop(); final PictureRecorder recorder3 = PictureRecorder(); @@ -729,14 +866,13 @@ class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { class TwoPlatformViewsWithOtherBackDropFilter extends Scenario with _BasePlatformViewScenarioMixin { /// Constructs the scenario. - TwoPlatformViewsWithOtherBackDropFilter(PlatformDispatcher dispatcher, - {required int firstId, required int secondId}) - : _firstId = firstId, - _secondId = secondId, - super(dispatcher) { - createPlatformView(dispatcher, 'platform view 1', firstId); - createPlatformView(dispatcher, 'platform view 2', secondId); - } + TwoPlatformViewsWithOtherBackDropFilter( + PlatformDispatcher dispatcher, { + required int firstId, + required int secondId, + }) : _firstId = firstId, + _secondId = secondId, + super(dispatcher); final int _firstId; final int _secondId; @@ -761,7 +897,15 @@ class TwoPlatformViewsWithOtherBackDropFilter extends Scenario builder.addPicture(const Offset(0, 0), picture1); builder.pushOffset(0, 200); - _addPlatformViewToScene(builder, _firstId, 100, 100); + + addPlatformView( + _firstId, + dispatcher: dispatcher, + sceneBuilder: builder, + width: 100, + height: 100, + text: 'platform view 1' + ); final PictureRecorder recorder2 = PictureRecorder(); final Canvas canvas2 = Canvas(recorder2); @@ -778,7 +922,14 @@ class TwoPlatformViewsWithOtherBackDropFilter extends Scenario builder.pushBackdropFilter(filter); builder.pushOffset(0, 600); - _addPlatformViewToScene(builder, _secondId, 500, 500); + + addPlatformView( + _secondId, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view 2', + ); + builder.pop(); builder.pop(); @@ -807,16 +958,14 @@ class PlatformViewScrollingUnderWidget extends Scenario /// Creates the PlatformView scenario. /// /// The [dispatcher] parameter must not be null. - PlatformViewScrollingUnderWidget(PlatformDispatcher dispatcher, - {required int firstPlatformViewId, required int lastPlatformViewId}) - : _firstPlatformViewId = firstPlatformViewId, - _lastPlatformViewId = lastPlatformViewId, - assert(dispatcher != null), - super(dispatcher) { - for (int i = _firstPlatformViewId; i <= _lastPlatformViewId; i++) { - createPlatformView(dispatcher, 'platform view', i); - } - } + PlatformViewScrollingUnderWidget( + PlatformDispatcher dispatcher, { + required int firstPlatformViewId, + required int lastPlatformViewId, + }) : _firstPlatformViewId = firstPlatformViewId, + _lastPlatformViewId = lastPlatformViewId, + assert(dispatcher != null), + super(dispatcher); final int _firstPlatformViewId; @@ -849,7 +998,7 @@ class PlatformViewScrollingUnderWidget extends Scenario super.onDrawFrame(); } - void _buildOneFrame(double offset) { + Future _buildOneFrame(double offset) async { const double cellWidth = 1000; double localOffset = offset; final SceneBuilder builder = SceneBuilder(); @@ -857,7 +1006,14 @@ class PlatformViewScrollingUnderWidget extends Scenario for (int i = _firstPlatformViewId; i <= _lastPlatformViewId; i++) { // Build a list view with platform views. builder.pushOffset(0, localOffset); - _addPlatformViewToScene(builder, i, cellWidth, cellHeight); + addPlatformView( + i, + dispatcher: dispatcher, + sceneBuilder: builder, + text: 'platform view $i', + width: cellWidth, + height: cellHeight, + ); builder.pop(); localOffset += cellHeight; } @@ -878,131 +1034,156 @@ class PlatformViewScrollingUnderWidget extends Scenario } } -mixin _BasePlatformViewScenarioMixin on Scenario { - int? _textureId; +final Map _createdPlatformViews = {}; - bool get usesAndroidHybridComposition { - return (scenarioParams['use_android_view'] as bool?) == true; +/// Adds the platform view to the scene. +/// +/// First, the platform view is created by calling the corresponding platform channel, +/// then a new frame is scheduled, finally the platform view is added to the scene builder. +void addPlatformView( + int id, { + required PlatformDispatcher dispatcher, + required SceneBuilder sceneBuilder, + String text = 'platform view', + double width = 500, + double height = 500, + String viewType = 'scenarios/textPlatformView', +}) { + final String platformViewKey = '$viewType-$id'; + if (_createdPlatformViews.containsKey(platformViewKey)) { + addPlatformViewToSceneBuilder( + id, + sceneBuilder: sceneBuilder, + textureId: _createdPlatformViews[platformViewKey]!, + width: width, + height: height, + ); + return; + } + bool usesAndroidHybridComposition = false; + if (scenarioParams['use_android_view'] != null) { + usesAndroidHybridComposition = scenarioParams['use_android_view'] as bool; } - /// Construct the platform view related scenario - /// - /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. - /// Call this method in the constructor of the platform view related scenarios - /// to perform necessary set up. - void createPlatformView(PlatformDispatcher dispatcher, String text, int id, - {String viewType = 'scenarios/textPlatformView'}) { - const int _valueTrue = 1; - const int _valueInt32 = 3; - const int _valueFloat64 = 6; - const int _valueString = 7; - const int _valueUint8List = 8; - const int _valueMap = 13; - - final Uint8List message = Uint8List.fromList([ + const int _valueTrue = 1; + const int _valueInt32 = 3; + const int _valueFloat64 = 6; + const int _valueString = 7; + const int _valueUint8List = 8; + const int _valueMap = 13; + + final Uint8List message = Uint8List.fromList([ + _valueString, + 'create'.length, // this won't work if we use multi-byte characters. + ...utf8.encode('create'), + _valueMap, + if (Platform.isIOS) 3, // 3 entries in map for iOS. + if (Platform.isAndroid && !usesAndroidHybridComposition) + 6, // 6 entries in map for virtual displays on Android. + if (Platform.isAndroid && usesAndroidHybridComposition) + 5, // 5 entries in map for Android views. + _valueString, + 'id'.length, + ...utf8.encode('id'), + _valueInt32, + ..._to32(id), + _valueString, + 'viewType'.length, + ...utf8.encode('viewType'), + _valueString, + viewType.length, + ...utf8.encode(viewType), + if (Platform.isAndroid && !usesAndroidHybridComposition) ...[ _valueString, - 'create'.length, // this won't work if we use multi-byte characters. - ...utf8.encode('create'), - _valueMap, - if (Platform.isIOS) 3, // 3 entries in map for iOS. - if (Platform.isAndroid && !usesAndroidHybridComposition) - 6, // 6 entries in map for virtual displays on Android. - if (Platform.isAndroid && usesAndroidHybridComposition) - 5, // 5 entries in map for Android views. + 'width'.length, + ...utf8.encode('width'), + _valueFloat64, + ..._to64(width), _valueString, - 'id'.length, - ...utf8.encode('id'), - _valueInt32, - ..._to32(id), + 'height'.length, + ...utf8.encode('height'), + _valueFloat64, + ..._to64(height), _valueString, - 'viewType'.length, - ...utf8.encode('viewType'), + 'direction'.length, + ...utf8.encode('direction'), + _valueInt32, + ..._to32(0), // LTR + ], + if (Platform.isAndroid && usesAndroidHybridComposition) ...[ _valueString, - viewType.length, - ...utf8.encode(viewType), - if (Platform.isAndroid && !usesAndroidHybridComposition) ...[ - _valueString, - 'width'.length, - ...utf8.encode('width'), - _valueFloat64, - ..._to64(500.0), - _valueString, - 'height'.length, - ...utf8.encode('height'), - _valueFloat64, - ..._to64(500.0), - _valueString, - 'direction'.length, - ...utf8.encode('direction'), - _valueInt32, - ..._to32(0), // LTR - ], - if (Platform.isAndroid && usesAndroidHybridComposition) ...[ - _valueString, - 'hybrid'.length, - ...utf8.encode('hybrid'), - _valueTrue, - _valueString, - 'direction'.length, - ...utf8.encode('direction'), - _valueInt32, - ..._to32(0), // LTR - ], + 'hybrid'.length, + ...utf8.encode('hybrid'), + _valueTrue, _valueString, - 'params'.length, - ...utf8.encode('params'), - _valueUint8List, - text.length, - ...utf8.encode(text), - ]); - - dispatcher.sendPlatformMessage( - 'flutter/platform_views', - message.buffer.asByteData(), - (ByteData? response) { - if (response != null && - Platform.isAndroid && - !usesAndroidHybridComposition) { - // Envelope. - _textureId = response.getUint8(0); - } - }, - ); - } - - void _addPlatformViewToScene( - SceneBuilder sceneBuilder, - int viewId, - double width, - double height, - ) { - if (Platform.isIOS) { - sceneBuilder.addPlatformView(viewId, width: width, height: height); - } else if (Platform.isAndroid) { - if (usesAndroidHybridComposition) { - sceneBuilder.addPlatformView(viewId, width: width, height: height); - } else if (_textureId != null) { - sceneBuilder.addTexture(_textureId!, width: width, height: height); + 'direction'.length, + ...utf8.encode('direction'), + _valueInt32, + ..._to32(0), // LTR + ], + _valueString, + 'params'.length, + ...utf8.encode('params'), + _valueUint8List, + text.length, + ...utf8.encode(text), + ]); + + dispatcher.sendPlatformMessage( + 'flutter/platform_views', + message.buffer.asByteData(), + (ByteData? response) { + late int textureId; + if (response != null && + Platform.isAndroid && + !usesAndroidHybridComposition) { + // This is the texture ID. + assert(response.getUint8(0) == 0, 'expected envelope'); + final int type = response.getUint8(1); + assert(type == 4, 'expected int64'); + textureId = response.getInt64(2, Endian.host); + } else { + // There no texture ID. + textureId = -1; } + _createdPlatformViews[platformViewKey] = textureId; + dispatcher.scheduleFrame(); + }, + ); +} + +/// Adds the platform view to the scene builder. +Future addPlatformViewToSceneBuilder( + int id, { + required SceneBuilder sceneBuilder, + required int textureId, + double width = 500, + double height = 500, +}) async { + if (Platform.isIOS) { + sceneBuilder.addPlatformView(id, width: width, height: height); + } else if (Platform.isAndroid) { + final bool? usesAndroidHybridComposition = scenarioParams['use_android_view'] as bool?; + if (usesAndroidHybridComposition != null && usesAndroidHybridComposition) { + sceneBuilder.addPlatformView(id, width: width, height: height); + } else if (textureId != -1) { + sceneBuilder.addTexture(textureId, width: width, height: height); } else { - throw UnsupportedError( - 'Platform ${Platform.operatingSystem} is not supported'); + throw UnsupportedError('Invalid texture id $textureId'); } + } else { + throw UnsupportedError( + 'Platform ${Platform.operatingSystem} is not supported'); } +} - // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture( - SceneBuilder sceneBuilder, - int viewId, { +mixin _BasePlatformViewScenarioMixin on Scenario { + // Add a picture and finishes the `sceneBuilder`. + void finishBuilder( + SceneBuilder sceneBuilder, { Offset? overlayOffset, }) { overlayOffset ??= const Offset(50, 50); - _addPlatformViewToScene( - sceneBuilder, - viewId, - 500, - 500, - ); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index 5515e17d877b5..95554d8a796f9 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -22,32 +22,32 @@ int _viewId = 0; Map _scenarios = { 'animated_color_square': () => AnimatedColorSquareScenario(PlatformDispatcher.instance), 'locale_initialization': () => LocaleInitialization(PlatformDispatcher.instance), - 'platform_view': () => PlatformViewScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: _viewId++), - 'platform_view_no_overlay_intersection': () => PlatformViewNoOverlayIntersectionScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: _viewId++), - 'platform_view_partial_intersection': () => PlatformViewPartialIntersectionScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: _viewId++), - 'platform_view_two_intersecting_overlays': () => PlatformViewTwoIntersectingOverlaysScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: _viewId++), - 'platform_view_one_overlay_two_intersecting_overlays': () => PlatformViewOneOverlayTwoIntersectingOverlaysScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: _viewId++), - 'platform_view_multiple_without_overlays': () => MultiPlatformViewWithoutOverlaysScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', firstId: _viewId++, secondId: _viewId++), - 'platform_view_max_overlays': () => PlatformViewMaxOverlaysScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: _viewId++), - 'platform_view_cliprect': () => PlatformViewClipRectScenario(PlatformDispatcher.instance, 'PlatformViewClipRect', id: _viewId++), - 'platform_view_cliprrect': () => PlatformViewClipRRectScenario(PlatformDispatcher.instance, 'PlatformViewClipRRect', id: _viewId++), - 'platform_view_clippath': () => PlatformViewClipPathScenario(PlatformDispatcher.instance, 'PlatformViewClipPath', id: _viewId++), - 'platform_view_transform': () => PlatformViewTransformScenario(PlatformDispatcher.instance, 'PlatformViewTransform', id: _viewId++), - 'platform_view_opacity': () => PlatformViewOpacityScenario(PlatformDispatcher.instance, 'PlatformViewOpacity', id: _viewId++), - 'platform_view_with_other_backdrop_filter': () => PlatformViewWithOtherBackDropFilter(PlatformDispatcher.instance, 'PlatformViewWithOtherBackDropFilter', id: _viewId++), + 'platform_view': () => PlatformViewScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_no_overlay_intersection': () => PlatformViewNoOverlayIntersectionScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_partial_intersection': () => PlatformViewPartialIntersectionScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_two_intersecting_overlays': () => PlatformViewTwoIntersectingOverlaysScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_one_overlay_two_intersecting_overlays': () => PlatformViewOneOverlayTwoIntersectingOverlaysScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_multiple_without_overlays': () => MultiPlatformViewWithoutOverlaysScenario(PlatformDispatcher.instance, firstId: _viewId++, secondId: _viewId++), + 'platform_view_max_overlays': () => PlatformViewMaxOverlaysScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_cliprect': () => PlatformViewClipRectScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_cliprrect': () => PlatformViewClipRRectScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_clippath': () => PlatformViewClipPathScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_transform': () => PlatformViewTransformScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_opacity': () => PlatformViewOpacityScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_with_other_backdrop_filter': () => PlatformViewWithOtherBackDropFilter(PlatformDispatcher.instance, id: _viewId++), 'two_platform_views_with_other_backdrop_filter': () => TwoPlatformViewsWithOtherBackDropFilter(PlatformDispatcher.instance, firstId: _viewId++, secondId: _viewId++), - 'platform_view_multiple': () => MultiPlatformViewScenario(PlatformDispatcher.instance, firstId: 6, secondId: _viewId++), + 'platform_view_multiple': () => MultiPlatformViewScenario(PlatformDispatcher.instance, firstId: _viewId++, secondId: _viewId++), 'platform_view_multiple_background_foreground': () => MultiPlatformViewBackgroundForegroundScenario(PlatformDispatcher.instance, firstId: _viewId++, secondId: _viewId++), - 'non_full_screen_flutter_view_platform_view': () => NonFullScreenFlutterViewPlatformViewScenario(PlatformDispatcher.instance, 'Hello from Scenarios (Platform View)', id: _viewId++), + 'non_full_screen_flutter_view_platform_view': () => NonFullScreenFlutterViewPlatformViewScenario(PlatformDispatcher.instance, id: _viewId++), 'poppable_screen': () => PoppableScreenScenario(PlatformDispatcher.instance), - 'platform_view_rotate': () => PlatformViewScenario(PlatformDispatcher.instance, 'Rotate Platform View', id: _viewId++), - 'platform_view_gesture_reject_eager': () => PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, 'platform view touch', id: _viewId++, accept: false), - 'platform_view_gesture_accept': () => PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, 'platform view touch', id: _viewId++, accept: true), - 'platform_view_gesture_reject_after_touches_ended': () => PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, 'platform view touch', id: _viewId++, accept: false, rejectUntilTouchesEnded: true), + 'platform_view_rotate': () => PlatformViewScenario(PlatformDispatcher.instance, id: _viewId++), + 'platform_view_gesture_reject_eager': () => PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, id: _viewId++, accept: false), + 'platform_view_gesture_accept': () => PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, id: _viewId++, accept: true), + 'platform_view_gesture_reject_after_touches_ended': () => PlatformViewForTouchIOSScenario(PlatformDispatcher.instance, id: _viewId++, accept: false, rejectUntilTouchesEnded: true), 'platform_view_scrolling_under_widget':()=>PlatformViewScrollingUnderWidget(PlatformDispatcher.instance, firstPlatformViewId: _viewId++, lastPlatformViewId: _viewId+=16), 'tap_status_bar': () => TouchesScenario(PlatformDispatcher.instance), 'initial_route_reply': () => InitialRouteReply(PlatformDispatcher.instance), - 'platform_view_with_continuous_texture': () => PlatformViewWithContinuousTexture(PlatformDispatcher.instance, 'Platform View', id: _viewId++), + 'platform_view_with_continuous_texture': () => PlatformViewWithContinuousTexture(PlatformDispatcher.instance, id: _viewId++), 'bogus_font_text': () => BogusFontText(PlatformDispatcher.instance), 'spawn_engine_works' : () => BogusFontText(PlatformDispatcher.instance), 'pointer_events': () => TouchesScenario(PlatformDispatcher.instance), diff --git a/testing/scenario_app/pubspec.yaml b/testing/scenario_app/pubspec.yaml index aa17f919d189f..00b4a2b1cb1b7 100644 --- a/testing/scenario_app/pubspec.yaml +++ b/testing/scenario_app/pubspec.yaml @@ -20,10 +20,15 @@ dependencies: process: any sky_engine: any vector_math: any + skia_gold_client: any dependency_overrides: args: path: ../../../third_party/dart/third_party/pkg/args + collection: + path: ../../../third_party/dart/third_party/pkg/collection + crypto: + path: ../../../third_party/dart/third_party/pkg/crypto file: path: ../../../third_party/pkg/file/packages/file meta: @@ -34,7 +39,11 @@ dependency_overrides: path: ../../../third_party/pkg/platform process: path: ../../../third_party/pkg/process + skia_gold_client: + path: ../../testing/skia_gold_client sky_engine: path: ../../sky/packages/sky_engine + typed_data: + path: ../../../third_party/dart/third_party/pkg/typed_data vector_math: path: ../../../third_party/pkg/vector_math diff --git a/testing/scenario_app/run_android_tests.sh b/testing/scenario_app/run_android_tests.sh index 1f9b6154da30c..eae473cf4cbdd 100755 --- a/testing/scenario_app/run_android_tests.sh +++ b/testing/scenario_app/run_android_tests.sh @@ -36,9 +36,33 @@ SCRIPT_DIR=$(follow_links "$(dirname -- "${BASH_SOURCE[0]}")") SRC_DIR="$(cd "$SCRIPT_DIR/../../.."; pwd -P)" OUT_DIR="$SRC_DIR/out/$BUILD_VARIANT" +# Dump the logcat and symbolize stack traces before exiting. +function dumpLogcat { + ndkstack="windows-x86_64" + if [ "$(uname)" == "Darwin" ]; then + ndkstack="darwin-x86_64" + elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + ndkstack="linux-x86_64" + fi + + echo "-> Symbolize stack traces" + "$SRC_DIR"/third_party/android_tools/ndk/prebuilt/"$ndkstack"/bin/ndk-stack \ + -sym "$OUT_DIR" \ + -dump "$OUT_DIR"/scenario_app/logcat.txt + echo "<- Done" + + echo "-> Dump full logcat" + cat "$OUT_DIR"/scenario_app/logcat.txt + echo "<- Done" +} + +trap dumpLogcat EXIT + cd $SCRIPT_DIR -"$SRC_DIR/third_party/dart/tools/sdks/dart-sdk/bin/dart" run \ - "$SCRIPT_DIR/bin/android_integration_tests.dart" \ +"$SRC_DIR"/third_party/dart/tools/sdks/dart-sdk/bin/dart pub get + +"$SRC_DIR"/third_party/dart/tools/sdks/dart-sdk/bin/dart run \ + "$SCRIPT_DIR"/bin/android_integration_tests.dart \ --adb="$SRC_DIR"/third_party/android_tools/sdk/platform-tools/adb \ --out-dir="$OUT_DIR" diff --git a/testing/skia_gold_client/lib/skia_gold_client.dart b/testing/skia_gold_client/lib/skia_gold_client.dart index 83197c314832b..c6b8ab40286e6 100644 --- a/testing/skia_gold_client/lib/skia_gold_client.dart +++ b/testing/skia_gold_client/lib/skia_gold_client.dart @@ -423,7 +423,7 @@ class SkiaGoldClient { final File currentScript = File.fromUri(Platform.script); final ProcessResult revParse = await process.run( ['git', 'rev-parse', 'HEAD'], - workingDirectory: currentScript.path, + workingDirectory: currentScript.parent.absolute.path, ); if (revParse.exitCode != 0) { throw Exception('Current commit of the engine can not be found from path ${currentScript.path}.');