From 3f7b0d884366227334f83de11eacb1748e16b419 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Mon, 20 Sep 2021 15:59:18 -0700 Subject: [PATCH 1/3] added unit tests that run on android devices --- BUILD.gn | 5 ++ ci/licenses_golden/licenses_flutter | 3 + shell/common/platform_view.h | 2 +- shell/platform/android/BUILD.gn | 28 ++++++-- shell/platform/android/android_context_gl.cc | 19 +++-- .../android/android_context_gl_unittests.cc | 11 +++ .../android/android_shell_holder_unittests.cc | 70 +++++++++++++++++++ .../android/flutter_shell_native_unittests.cc | 6 ++ .../android/surface/android_native_window.cc | 6 +- .../android/surface/android_native_window.h | 7 ++ testing/run_tests.py | 12 ++++ 11 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 shell/platform/android/android_context_gl_unittests.cc create mode 100644 shell/platform/android/android_shell_holder_unittests.cc create mode 100644 shell/platform/android/flutter_shell_native_unittests.cc diff --git a/BUILD.gn b/BUILD.gn index e84bece9519de..07095b6a708d5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -145,6 +145,11 @@ group("flutter") { "//flutter/third_party/txt:txt_unittests", ] + if (is_android) { + public_deps += + [ "//flutter/shell/platform/android:flutter_shell_native_unittests" ] + } + # The accessibility library only supports Mac and Windows at the moment. if (is_mac || is_win) { public_deps += diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b776f69941a82..ca1141917564a 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -770,6 +770,7 @@ FILE: ../../../flutter/shell/gpu/gpu_surface_vulkan_delegate.h FILE: ../../../flutter/shell/platform/android/AndroidManifest.xml FILE: ../../../flutter/shell/platform/android/android_context_gl.cc FILE: ../../../flutter/shell/platform/android/android_context_gl.h +FILE: ../../../flutter/shell/platform/android/android_context_gl_unittests.cc FILE: ../../../flutter/shell/platform/android/android_environment_gl.cc FILE: ../../../flutter/shell/platform/android/android_environment_gl.h FILE: ../../../flutter/shell/platform/android/android_exports.lst @@ -779,6 +780,7 @@ FILE: ../../../flutter/shell/platform/android/android_image_generator.cc FILE: ../../../flutter/shell/platform/android/android_image_generator.h FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc FILE: ../../../flutter/shell/platform/android/android_shell_holder.h +FILE: ../../../flutter/shell/platform/android/android_shell_holder_unittests.cc FILE: ../../../flutter/shell/platform/android/android_surface_gl.cc FILE: ../../../flutter/shell/platform/android/android_surface_gl.h FILE: ../../../flutter/shell/platform/android/android_surface_software.cc @@ -795,6 +797,7 @@ FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_poo FILE: ../../../flutter/shell/platform/android/external_view_embedder/surface_pool_unittests.cc FILE: ../../../flutter/shell/platform/android/flutter_main.cc FILE: ../../../flutter/shell/platform/android/flutter_main.h +FILE: ../../../flutter/shell/platform/android/flutter_shell_native_unittests.cc FILE: ../../../flutter/shell/platform/android/io/flutter/FlutterInjector.java FILE: ../../../flutter/shell/platform/android/io/flutter/Log.java FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivity.java diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 1cbf73ce4abea..2b17b5066cc5c 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -8,9 +8,9 @@ #include #include -#include "flow/embedded_views.h" #include "flutter/common/graphics/texture.h" #include "flutter/common/task_runners.h" +#include "flutter/flow/embedded_views.h" #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 6d76a91c87573..2780a734ef590 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -35,10 +35,30 @@ source_set("image_generator") { ] } -shared_library("flutter_shell_native") { - visibility = [ ":*" ] +executable("flutter_shell_native_unittests") { + visibility = [ "*" ] + testonly = true + sources = [ + "android_context_gl_unittests.cc", + "android_shell_holder_unittests.cc", + "flutter_shell_native_unittests.cc", + ] + public_configs = [ "//flutter:config" ] + deps = [ + ":flutter_shell_native_src", + "//third_party/googletest:gmock", + "//third_party/googletest:gtest", + ] +} +shared_library("flutter_shell_native") { output_name = "flutter" + deps = [ ":flutter_shell_native_src" ] + ldflags = [ "-Wl,--version-script=" + rebase_path("android_exports.lst") ] +} + +source_set("flutter_shell_native_src") { + visibility = [ ":*" ] sources = [ "$root_build_dir/flutter_icu/icudtl.o", @@ -69,7 +89,7 @@ shared_library("flutter_shell_native") { "vsync_waiter_android.h", ] - deps = [ + public_deps = [ ":android_gpu_configuration", ":icudtl_object", ":image_generator", @@ -100,8 +120,6 @@ shared_library("flutter_shell_native") { "EGL", "GLESv2", ] - - ldflags = [ "-Wl,--version-script=" + rebase_path("android_exports.lst") ] } action("gen_android_build_config_java") { diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index ee056925d05a4..3fb96a77412f1 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -206,12 +206,19 @@ std::unique_ptr AndroidContextGL::CreateOnscreenSurface( fml::RefPtr window) const { EGLDisplay display = environment_->Display(); - const EGLint attribs[] = {EGL_NONE}; - - EGLSurface surface = eglCreateWindowSurface( - display, config_, reinterpret_cast(window->handle()), - attribs); - return std::make_unique(surface, display, context_); + if (window->IsOffscreen()) { + const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; + + EGLSurface surface = eglCreatePbufferSurface(display, config_, attribs); + return std::make_unique(surface, display, context_); + } else { + const EGLint attribs[] = {EGL_NONE}; + + EGLSurface surface = eglCreateWindowSurface( + display, config_, + reinterpret_cast(window->handle()), attribs); + return std::make_unique(surface, display, context_); + } } std::unique_ptr AndroidContextGL::CreateOffscreenSurface() diff --git a/shell/platform/android/android_context_gl_unittests.cc b/shell/platform/android/android_context_gl_unittests.cc new file mode 100644 index 0000000000000..f31b023643c9c --- /dev/null +++ b/shell/platform/android/android_context_gl_unittests.cc @@ -0,0 +1,11 @@ +#include +#include "flutter/shell/platform/android/android_context_gl.h" +#include "flutter/shell/platform/android/android_environment_gl.h" +#include "gtest/gtest.h" + +TEST(AndroidContextGl, Create) { + auto environment = fml::MakeRefCounted(); + auto context = std::make_unique( + flutter::AndroidRenderingAPI::kOpenGLES, environment); + EXPECT_NE(context.get(), nullptr); +} diff --git a/shell/platform/android/android_shell_holder_unittests.cc b/shell/platform/android/android_shell_holder_unittests.cc new file mode 100644 index 0000000000000..eca2eed8bc067 --- /dev/null +++ b/shell/platform/android/android_shell_holder_unittests.cc @@ -0,0 +1,70 @@ +#include +#include "flutter/shell/platform/android/android_shell_holder.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { +namespace { +class MockPlatformViewAndroidJNI : public PlatformViewAndroidJNI { + MOCK_METHOD2(FlutterViewHandlePlatformMessage, + void(std::unique_ptr message, + int responseId)); + MOCK_METHOD2(FlutterViewHandlePlatformMessageResponse, + void(int responseId, std::unique_ptr data)); + MOCK_METHOD3(FlutterViewUpdateSemantics, + void(std::vector buffer, + std::vector strings, + std::vector> string_attribute_args)); + MOCK_METHOD2(FlutterViewUpdateCustomAccessibilityActions, + void(std::vector actions_buffer, + std::vector strings)); + MOCK_METHOD0(FlutterViewOnFirstFrame, void()); + MOCK_METHOD0(FlutterViewOnPreEngineRestart, void()); + MOCK_METHOD2(SurfaceTextureAttachToGLContext, + void(JavaLocalRef surface_texture, int textureId)); + MOCK_METHOD1(SurfaceTextureUpdateTexImage, + void(JavaLocalRef surface_texture)); + MOCK_METHOD2(SurfaceTextureGetTransformMatrix, + void(JavaLocalRef surface_texture, SkMatrix& transform)); + MOCK_METHOD1(SurfaceTextureDetachFromGLContext, + void(JavaLocalRef surface_texture)); + MOCK_METHOD8(FlutterViewOnDisplayPlatformView, + void(int view_id, + int x, + int y, + int width, + int height, + int viewWidth, + int viewHeight, + MutatorsStack mutators_stack)); + MOCK_METHOD5(FlutterViewDisplayOverlaySurface, + void(int surface_id, int x, int y, int width, int height)); + MOCK_METHOD0(FlutterViewBeginFrame, void()); + MOCK_METHOD0(FlutterViewEndFrame, void()); + MOCK_METHOD0(FlutterViewCreateOverlaySurface, + std::unique_ptr()); + MOCK_METHOD0(FlutterViewDestroyOverlaySurfaces, void()); + MOCK_METHOD1(FlutterViewComputePlatformResolvedLocale, + std::unique_ptr>( + std::vector supported_locales_data)); + MOCK_METHOD0(GetDisplayRefreshRate, double()); + MOCK_METHOD1(RequestDartDeferredLibrary, bool(int loading_unit_id)); +}; +} // namespace + +TEST(AndroidShellHolder, Create) { + Settings settings; + settings.enable_software_rendering = false; + auto jni = std::make_shared(); + auto holder = std::make_unique( + settings, jni, /*is_background_view=*/false); + EXPECT_NE(holder.get(), nullptr); + EXPECT_TRUE(holder->IsValid()); + EXPECT_NE(holder->GetPlatformView().get(), nullptr); + auto window = + fml::MakeRefCounted(nullptr, /*is_offscreen=*/true); + holder->GetPlatformView()->NotifyCreated(window); +} +} // namespace testing +} // namespace flutter diff --git a/shell/platform/android/flutter_shell_native_unittests.cc b/shell/platform/android/flutter_shell_native_unittests.cc new file mode 100644 index 0000000000000..fbebc29e38923 --- /dev/null +++ b/shell/platform/android/flutter_shell_native_unittests.cc @@ -0,0 +1,6 @@ +#include "gtest/gtest.h" + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/shell/platform/android/surface/android_native_window.cc b/shell/platform/android/surface/android_native_window.cc index e701b986f48e9..0d3e441792883 100644 --- a/shell/platform/android/surface/android_native_window.cc +++ b/shell/platform/android/surface/android_native_window.cc @@ -6,7 +6,11 @@ namespace flutter { -AndroidNativeWindow::AndroidNativeWindow(Handle window) : window_(window) {} +AndroidNativeWindow::AndroidNativeWindow(Handle window, bool is_offscreen) + : window_(window), is_offscreen_(is_offscreen) {} + +AndroidNativeWindow::AndroidNativeWindow(Handle window) + : AndroidNativeWindow(window, /*is_offscreen=*/false) {} AndroidNativeWindow::~AndroidNativeWindow() { if (window_ != nullptr) { diff --git a/shell/platform/android/surface/android_native_window.h b/shell/platform/android/surface/android_native_window.h index e2f385303403e..b8d1beaea2026 100644 --- a/shell/platform/android/surface/android_native_window.h +++ b/shell/platform/android/surface/android_native_window.h @@ -32,13 +32,20 @@ class AndroidNativeWindow SkISize GetSize() const; + /// Returns true when this AndroidNativeWindow is not backed by a real window + /// (used for testing). + bool IsOffscreen() const { return is_offscreen_; } + private: Handle window_; + const bool is_offscreen_; /// Creates a native window with the given handle. Handle ownership is assumed /// by this instance of the native window. explicit AndroidNativeWindow(Handle window); + explicit AndroidNativeWindow(Handle window, bool is_offscreen); + ~AndroidNativeWindow(); FML_FRIEND_MAKE_REF_COUNTED(AndroidNativeWindow); diff --git a/testing/run_tests.py b/testing/run_tests.py index f8916dfd93087..3d1fc40bd74ba 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -351,6 +351,14 @@ def RunJavaTests(filter, android_variant='android_debug_unopt'): RunCmd(command, cwd=test_runner_dir, env=env) +def RunAndroidTests(android_variant='android_debug_unopt'): + test_runner_name = 'flutter_shell_native_unittests' + tests_path = os.path.join(out_dir, android_variant, test_runner_name) + remote_path = '/data/local/tmp' + remote_tests_path = os.path.join(remote_path, test_runner_name) + RunCmd(['adb', 'push', tests_path, remote_path], cwd=buildroot_dir) + RunCmd(['adb', 'shell', remote_tests_path]) + def RunObjcTests(ios_variant='ios_debug_sim_unopt', test_filter=None): """Runs Objective-C XCTest unit tests for the iOS embedding""" AssertExpectedXcodeVersion() @@ -592,6 +600,10 @@ def main(): java_filter = None RunJavaTests(java_filter, args.android_variant) + if 'android' in types: + assert not IsWindows(), "Android engine files can't be compiled on Windows." + RunAndroidTests(args.android_variant) + if 'objc' in types: assert IsMac(), "iOS embedding tests can only be run on macOS." RunObjcTests(args.ios_variant, args.objc_filter) From c4eb82ad889078dd6abd73b539799dab6e7ab310 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 23 Sep 2021 11:50:12 -0700 Subject: [PATCH 2/3] review feedback --- shell/platform/android/android_context_gl.cc | 2 +- shell/platform/android/surface/android_native_window.cc | 6 +++--- shell/platform/android/surface/android_native_window.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index 3fb96a77412f1..804f3cac57f4e 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -206,7 +206,7 @@ std::unique_ptr AndroidContextGL::CreateOnscreenSurface( fml::RefPtr window) const { EGLDisplay display = environment_->Display(); - if (window->IsOffscreen()) { + if (window->IsFakeWindow()) { const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; EGLSurface surface = eglCreatePbufferSurface(display, config_, attribs); diff --git a/shell/platform/android/surface/android_native_window.cc b/shell/platform/android/surface/android_native_window.cc index 0d3e441792883..5e4ebef7840b5 100644 --- a/shell/platform/android/surface/android_native_window.cc +++ b/shell/platform/android/surface/android_native_window.cc @@ -6,11 +6,11 @@ namespace flutter { -AndroidNativeWindow::AndroidNativeWindow(Handle window, bool is_offscreen) - : window_(window), is_offscreen_(is_offscreen) {} +AndroidNativeWindow::AndroidNativeWindow(Handle window, bool is_fake_window) + : window_(window), is_fake_window_(is_fake_window) {} AndroidNativeWindow::AndroidNativeWindow(Handle window) - : AndroidNativeWindow(window, /*is_offscreen=*/false) {} + : AndroidNativeWindow(window, /*is_fake_window=*/false) {} AndroidNativeWindow::~AndroidNativeWindow() { if (window_ != nullptr) { diff --git a/shell/platform/android/surface/android_native_window.h b/shell/platform/android/surface/android_native_window.h index b8d1beaea2026..ac684b955eb4b 100644 --- a/shell/platform/android/surface/android_native_window.h +++ b/shell/platform/android/surface/android_native_window.h @@ -34,17 +34,17 @@ class AndroidNativeWindow /// Returns true when this AndroidNativeWindow is not backed by a real window /// (used for testing). - bool IsOffscreen() const { return is_offscreen_; } + bool IsFakeWindow() const { return is_fake_window_; } private: Handle window_; - const bool is_offscreen_; + const bool is_fake_window_; /// Creates a native window with the given handle. Handle ownership is assumed /// by this instance of the native window. explicit AndroidNativeWindow(Handle window); - explicit AndroidNativeWindow(Handle window, bool is_offscreen); + explicit AndroidNativeWindow(Handle window, bool is_fake_window); ~AndroidNativeWindow(); From 34f4f8604014ddf108a88aaddbc2bf333d8837c0 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 23 Sep 2021 11:52:44 -0700 Subject: [PATCH 3/3] feedback2 --- shell/platform/android/android_context_gl.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index 804f3cac57f4e..1d0f83cafa7e7 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -204,14 +204,11 @@ AndroidContextGL::~AndroidContextGL() { std::unique_ptr AndroidContextGL::CreateOnscreenSurface( fml::RefPtr window) const { - EGLDisplay display = environment_->Display(); - if (window->IsFakeWindow()) { - const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; - - EGLSurface surface = eglCreatePbufferSurface(display, config_, attribs); - return std::make_unique(surface, display, context_); + return CreatePbufferSurface(); } else { + EGLDisplay display = environment_->Display(); + const EGLint attribs[] = {EGL_NONE}; EGLSurface surface = eglCreateWindowSurface(