blob: acf1bca7fd12674bb884a748c2d16fdd6d1c58b5 [file] [log] [blame]
// 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.
#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/fl_view_private.h"
#include "flutter/shell/platform/linux/testing/fl_test.h"
#include "flutter/shell/platform/linux/testing/fl_test_gtk_logs.h"
#include "gtest/gtest.h"
static void first_frame_cb(FlView* view, gboolean* first_frame_emitted) {
*first_frame_emitted = TRUE;
}
TEST(FlViewTest, GetEngine) {
flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* view = fl_view_new(project);
// Check the engine is immediately available (i.e. before the widget is
// realized).
FlEngine* engine = fl_view_get_engine(view);
EXPECT_NE(engine, nullptr);
}
TEST(FlViewTest, StateUpdateDoesNotHappenInInit) {
flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* view = fl_view_new(project);
// Check that creating a view doesn't try to query the window state in
// initialization, causing a critical log to be issued.
EXPECT_EQ(
flutter::testing::fl_get_received_gtk_log_levels() & G_LOG_LEVEL_CRITICAL,
(GLogLevelFlags)0x0);
(void)view;
}
TEST(FlViewTest, FirstFrameSignal) {
flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* view = fl_view_new(project);
gboolean first_frame_emitted = FALSE;
g_signal_connect(view, "first-frame", G_CALLBACK(first_frame_cb),
&first_frame_emitted);
EXPECT_FALSE(first_frame_emitted);
fl_renderable_redraw(FL_RENDERABLE(view));
// Signal is emitted in idle, clear the main loop.
while (g_main_context_iteration(g_main_context_default(), FALSE)) {
// Repeat until nothing to iterate on.
}
// Check view has detected frame.
EXPECT_TRUE(first_frame_emitted);
}
// Check secondary view is registered with engine.
TEST(FlViewTest, SecondaryView) {
flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* implicit_view = fl_view_new(project);
FlEngine* engine = fl_view_get_engine(implicit_view);
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
FlutterViewId view_id = -1;
embedder_api->AddView = MOCK_ENGINE_PROC(
AddView, ([&view_id](auto engine, const FlutterAddViewInfo* info) {
view_id = info->view_id;
FlutterAddViewResult result = {
.struct_size = sizeof(FlutterAddViewResult),
.added = true,
.user_data = info->user_data};
info->add_view_callback(&result);
return kSuccess;
}));
FlView* secondary_view = fl_view_new_for_engine(engine);
EXPECT_EQ(view_id, fl_view_get_id(secondary_view));
}
// Check secondary view that fails registration.
TEST(FlViewTest, SecondaryViewError) {
flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* implicit_view = fl_view_new(project);
FlEngine* engine = fl_view_get_engine(implicit_view);
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
FlutterViewId view_id = -1;
embedder_api->AddView = MOCK_ENGINE_PROC(
AddView, ([&view_id](auto engine, const FlutterAddViewInfo* info) {
view_id = info->view_id;
return kInvalidArguments;
}));
FlView* secondary_view = fl_view_new_for_engine(engine);
EXPECT_EQ(view_id, fl_view_get_id(secondary_view));
}
// Check views are deregistered on destruction.
TEST(FlViewTest, ViewDestroy) {
flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* implicit_view = fl_view_new(project);
FlEngine* engine = fl_view_get_engine(implicit_view);
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
g_autoptr(GPtrArray) removed_views = g_ptr_array_new();
embedder_api->RemoveView = MOCK_ENGINE_PROC(
RemoveView,
([removed_views](auto engine, const FlutterRemoveViewInfo* info) {
g_ptr_array_add(removed_views, GINT_TO_POINTER(info->view_id));
return kSuccess;
}));
FlView* secondary_view = fl_view_new_for_engine(engine);
int64_t implicit_view_id = fl_view_get_id(implicit_view);
int64_t secondary_view_id = fl_view_get_id(secondary_view);
gtk_widget_destroy(GTK_WIDGET(secondary_view));
gtk_widget_destroy(GTK_WIDGET(implicit_view));
EXPECT_EQ(removed_views->len, 2u);
EXPECT_EQ(GPOINTER_TO_INT(g_ptr_array_index(removed_views, 0)),
secondary_view_id);
EXPECT_EQ(GPOINTER_TO_INT(g_ptr_array_index(removed_views, 1)),
implicit_view_id);
}
// Check views deregistered with errors works.
TEST(FlViewTest, ViewDestroyError) {
flutter::testing::fl_ensure_gtk_init();
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* implicit_view = fl_view_new(project);
FlEngine* engine = fl_view_get_engine(implicit_view);
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
embedder_api->RemoveView = MOCK_ENGINE_PROC(
RemoveView, ([](auto engine, const FlutterRemoveViewInfo* info) {
return kInvalidArguments;
}));
FlView* secondary_view = fl_view_new_for_engine(engine);
gtk_widget_destroy(GTK_WIDGET(secondary_view));
gtk_widget_destroy(GTK_WIDGET(implicit_view));
}