diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 334866eef92fe..605d139cef36f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5053,6 +5053,7 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_view_accessible.cc + ../../../f ORIGIN: ../../../flutter/shell/platform/linux/fl_view_accessible.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_view_accessible_test.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_view_private.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/linux/fl_view_test.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/key_mapping.g.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/key_mapping.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h + ../../../flutter/LICENSE @@ -7835,6 +7836,7 @@ FILE: ../../../flutter/shell/platform/linux/fl_view_accessible.cc FILE: ../../../flutter/shell/platform/linux/fl_view_accessible.h FILE: ../../../flutter/shell/platform/linux/fl_view_accessible_test.cc FILE: ../../../flutter/shell/platform/linux/fl_view_private.h +FILE: ../../../flutter/shell/platform/linux/fl_view_test.cc FILE: ../../../flutter/shell/platform/linux/key_mapping.g.cc FILE: ../../../flutter/shell/platform/linux/key_mapping.h FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 7d0f027159c44..48cb6a0689874 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -227,7 +227,10 @@ executable("flutter_linux_unittests") { "fl_texture_registrar_test.cc", "fl_value_test.cc", "fl_view_accessible_test.cc", + "fl_view_test.cc", "testing/fl_test.cc", + "testing/fl_test_gtk_logs.cc", + "testing/fl_test_gtk_logs.h", "testing/mock_binary_messenger.cc", "testing/mock_binary_messenger_response_handle.cc", "testing/mock_engine.cc", diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index e41bbebdabec9..183ddad15ca29 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -247,8 +247,6 @@ static void on_pre_engine_restart_cb(FlEngine* engine, gpointer user_data) { g_clear_object(&self->scrolling_manager); init_keyboard(self); init_scrolling(self); - self->window_state = - gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(self))); } // Implements FlPluginRegistry::get_registrar_for_plugin. @@ -556,6 +554,8 @@ static void realize_cb(GtkWidget* widget) { self->window_state_cb_id = g_signal_connect(toplevel_window, "window-state-event", G_CALLBACK(window_state_event_cb), self); + self->window_state = + gdk_window_get_state(gtk_widget_get_window(toplevel_window)); g_signal_connect(toplevel_window, "delete-event", G_CALLBACK(window_delete_event_cb), self); @@ -760,8 +760,6 @@ static void fl_view_class_init(FlViewClass* klass) { static void fl_view_init(FlView* self) { gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE); - self->window_state = gdk_window_get_state( - gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(self)))); } G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) { diff --git a/shell/platform/linux/fl_view_test.cc b/shell/platform/linux/fl_view_test.cc new file mode 100644 index 0000000000000..c36323cb04aca --- /dev/null +++ b/shell/platform/linux/fl_view_test.cc @@ -0,0 +1,20 @@ +// 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/linux/testing/fl_test_gtk_logs.h" + +#include "gtest/gtest.h" + +TEST(FlViewTest, StateUpdateDoesNotHappenInInit) { + flutter::testing::fl_ensure_gtk_init(); + g_autoptr(FlDartProject) project = fl_dart_project_new(); + g_autoptr(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); + g_object_ref_sink(view); +} diff --git a/shell/platform/linux/testing/fl_test_gtk_logs.cc b/shell/platform/linux/testing/fl_test_gtk_logs.cc new file mode 100644 index 0000000000000..db25f306f92e3 --- /dev/null +++ b/shell/platform/linux/testing/fl_test_gtk_logs.cc @@ -0,0 +1,54 @@ +// 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 "fl_test_gtk_logs.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +namespace { + +bool gtk_initialized = false; +GLogWriterFunc log_writer_cb = nullptr; +GLogLevelFlags fl_received_log_levels = (GLogLevelFlags)0x0; + +GLogWriterOutput log_writer(GLogLevelFlags log_level, + const GLogField* fields, + gsize n_fields, + gpointer user_data) { + fl_received_log_levels = (GLogLevelFlags)(log_level | fl_received_log_levels); + if (log_writer_cb == nullptr) { + return g_log_writer_default(log_level, fields, n_fields, user_data); + } + GLogWriterOutput result = + log_writer_cb(log_level, fields, n_fields, user_data); + if (result != G_LOG_WRITER_HANDLED) { + return g_log_writer_default(log_level, fields, n_fields, user_data); + } + return result; +} + +} // namespace + +void fl_ensure_gtk_init(GLogWriterFunc writer) { + if (!gtk_initialized) { + gtk_init(0, nullptr); + g_log_set_writer_func(log_writer, nullptr, nullptr); + gtk_initialized = true; + } + fl_reset_received_gtk_log_levels(); + log_writer_cb = writer; +} + +void fl_reset_received_gtk_log_levels() { + fl_received_log_levels = (GLogLevelFlags)0x0; +} + +GLogLevelFlags fl_get_received_gtk_log_levels() { + return fl_received_log_levels; +} + +} // namespace testing +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/linux/testing/fl_test_gtk_logs.h b/shell/platform/linux/testing/fl_test_gtk_logs.h new file mode 100644 index 0000000000000..b1fd05156784b --- /dev/null +++ b/shell/platform/linux/testing/fl_test_gtk_logs.h @@ -0,0 +1,49 @@ +// 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. + +#ifndef FLUTTER_TESTING_TEST_GTK_LOGS_H_ +#define FLUTTER_TESTING_TEST_GTK_LOGS_H_ + +#include + +namespace flutter { +namespace testing { + +/** + * Ensures that GTK has been initialized. If GTK has not been initialized, it + * will be initialized using `gtk_init()`. It will also set the GTK log writer + * function to monitor the log output, recording the log levels that have been + * received in a bitfield accessible via {@link fl_get_received_gtk_log_levels} + * + * To retrieve the bitfield of recorded log levels, use + * `fl_get_received_gtk_log_levels()`. + * + * @param[in] writer The custom log writer function to use. If `nullptr`, or it + * returns G_LOG_WRITER_UNHANDLED, the default log writer function will be + * called. + * + * @brief Ensures that GTK has been initialized and starts monitoring logs. + */ +void fl_ensure_gtk_init(GLogWriterFunc writer = nullptr); + +/** + * Resets the recorded GTK log levels to zero. + * + * @brief Resets the recorded log levels. + */ +void fl_reset_received_gtk_log_levels(); + +/** + * Returns a bitfield containing the GTK log levels that have been seen since + * the last time they were reset. + * + * @brief Returns the recorded log levels. + * + * @return A `GLogLevelFlags` bitfield representing the recorded log levels. + */ +GLogLevelFlags fl_get_received_gtk_log_levels(); + +} // namespace testing +} // namespace flutter +#endif // FLUTTER_TESTING_TEST_GTK_LOGS_H_ diff --git a/testing/BUILD.gn b/testing/BUILD.gn index cdc185aac29ac..99aaa0fc699d8 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -51,6 +51,11 @@ source_set("testing") { sources = [ "run_all_unittests.cc" ] + if (is_linux) { + # So that we can call gtk_init in main(). + configs += [ "//flutter/shell/platform/linux/config:gtk" ] + } + public_deps = [ ":testing_lib" ] public_configs = [ ":dynamic_symbols" ] } diff --git a/testing/run_tests.py b/testing/run_tests.py index dbeee78097380..003cb661da0c6 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -463,16 +463,23 @@ def make_test(name, flags=None, extra_env=None): make_test('flow_unittests', flags=repeat_flags + flow_flags), ] - for test, flags, extra_env in unittests: - run_engine_executable( - build_dir, - test, - executable_filter, - flags, - coverage=coverage, - extra_env=extra_env, - gtest=True - ) + build_name = os.path.basename(build_dir) + try: + if is_linux(): + xvfb.start_virtual_x(build_name, build_dir) + for test, flags, extra_env in unittests: + run_engine_executable( + build_dir, + test, + executable_filter, + flags, + coverage=coverage, + extra_env=extra_env, + gtest=True + ) + finally: + if is_linux(): + xvfb.stop_virtual_x(build_name) if is_mac(): # flutter_desktop_darwin_unittests uses global state that isn't handled