From 9cd5982c143752a26ef1179827efc561b76f154f Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 12 Apr 2022 11:08:34 +0200 Subject: [PATCH 1/2] [Linux] add MockSignalHandler for testing GObject signals This is a simple helper class that allows settings GMock expectations on GObject signals: ```c GObject* foo_object = ...; flutter::testing::MockSignalHandler foo_changed(foo_object, "changed"); EXPECT_SIGNAL(foo_changed).Times(1); foo_set_something(foo_object, ...); ``` --- shell/platform/linux/BUILD.gn | 1 + .../linux/testing/mock_signal_handler.cc | 26 ++++++ .../linux/testing/mock_signal_handler.h | 90 +++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 shell/platform/linux/testing/mock_signal_handler.cc create mode 100644 shell/platform/linux/testing/mock_signal_handler.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 8ca60fcc431ec..1601cb8ae734d 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -209,6 +209,7 @@ executable("flutter_linux_unittests") { "testing/mock_epoxy.cc", "testing/mock_plugin_registrar.cc", "testing/mock_renderer.cc", + "testing/mock_signal_handler.cc", "testing/mock_text_input_plugin.cc", "testing/mock_texture_registrar.cc", ] diff --git a/shell/platform/linux/testing/mock_signal_handler.cc b/shell/platform/linux/testing/mock_signal_handler.cc new file mode 100644 index 0000000000000..1981914432a7a --- /dev/null +++ b/shell/platform/linux/testing/mock_signal_handler.cc @@ -0,0 +1,26 @@ +// 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/testing/mock_signal_handler.h" + +namespace flutter { +namespace testing { + +SignalHandler::SignalHandler(gpointer instance, + const gchar* name, + GCallback callback) { + id_ = g_signal_connect_data(instance, name, callback, this, nullptr, + G_CONNECT_SWAPPED); + g_object_add_weak_pointer(G_OBJECT(instance), &instance_); +} + +SignalHandler::~SignalHandler() { + if (instance_) { + g_signal_handler_disconnect(instance_, id_); + g_object_remove_weak_pointer(G_OBJECT(instance_), &instance_); + } +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/linux/testing/mock_signal_handler.h b/shell/platform/linux/testing/mock_signal_handler.h new file mode 100644 index 0000000000000..e1e92895da4bd --- /dev/null +++ b/shell/platform/linux/testing/mock_signal_handler.h @@ -0,0 +1,90 @@ +// 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_SHELL_PLATFORM_LINUX_MOCK_SIGNAL_HANDLER_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_MOCK_SIGNAL_HANDLER_H_ + +#include +#include + +#include "gmock/gmock.h" + +// Expects a signal that has no arguments. +// +// MockSignalHandler timeout(timer, "timeout"); +// EXPECT_SIGNAL(timeout).Times(3); +// +#define EXPECT_SIGNAL(mock) EXPECT_CALL(mock, Handler()) + +// Expects a signal that has 1 argument. +// +// MockSignalHandler1 name_changed(object, "name-changed"); +// EXPECT_SIGNAL(name_changed, testing::StrEq("example")); +// +#define EXPECT_SIGNAL1(mock, a1) EXPECT_CALL(mock, Handler1(a1)) + +// Expects a signal that has 2 arguments. +// +// MockSignalHandler2 child_added(parent, "children::add"); +// EXPECT_SIGNAL2(child_added, testing::Eq(1), testing::A()); +// +#define EXPECT_SIGNAL2(mock, a1, a2) EXPECT_CALL(mock, Handler2(a1, a2)) + +namespace flutter { +namespace testing { + +class SignalHandler { + public: + SignalHandler(gpointer instance, const gchar* name, GCallback callback); + virtual ~SignalHandler(); + + private: + gulong id_ = 0; + gpointer instance_ = nullptr; +}; + +// A mock signal handler that has no arguments. Used with EXPECT_SIGNAL(). +class MockSignalHandler : public SignalHandler { + public: + MockSignalHandler(gpointer instance, const gchar* name) + : SignalHandler(instance, name, G_CALLBACK(OnSignal)) {} + + MOCK_METHOD0(Handler, void()); + + private: + static void OnSignal(MockSignalHandler* mock) { mock->Handler(); } +}; + +// A mock signal handler that has 1 argument. Used with EXPECT_SIGNAL1(). +template +class MockSignalHandler1 : public SignalHandler { + public: + MockSignalHandler1(gpointer instance, const gchar* name) + : SignalHandler(instance, name, G_CALLBACK(OnSignal1)) {} + + MOCK_METHOD1(Handler1, void(A1 a1)); + + private: + static void OnSignal1(MockSignalHandler1* mock, A1 a1) { mock->Handler1(a1); } +}; + +// A mock signal handler that has 2 arguments. Used with EXPECT_SIGNAL2(). +template +class MockSignalHandler2 : public SignalHandler { + public: + MockSignalHandler2(gpointer instance, const gchar* name) + : SignalHandler(instance, name, G_CALLBACK(OnSignal2)) {} + + MOCK_METHOD2(Handler2, void(A1 a1, A2 a2)); + + private: + static void OnSignal2(MockSignalHandler2* mock, A1 a1, A2 a2) { + mock->Handler2(a1, a2); + } +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_MOCK_SIGNAL_HANDLER_H_ From 1656bbd47c9bb295c440ce8f0fd47a7a2a05c797 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 14 Apr 2022 08:30:58 +0200 Subject: [PATCH 2/2] Fix signal leak g_object_add_weak_pointer() does not assign the initial value --- shell/platform/linux/testing/mock_signal_handler.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/linux/testing/mock_signal_handler.cc b/shell/platform/linux/testing/mock_signal_handler.cc index 1981914432a7a..a1b4d68856936 100644 --- a/shell/platform/linux/testing/mock_signal_handler.cc +++ b/shell/platform/linux/testing/mock_signal_handler.cc @@ -9,7 +9,8 @@ namespace testing { SignalHandler::SignalHandler(gpointer instance, const gchar* name, - GCallback callback) { + GCallback callback) + : instance_(instance) { id_ = g_signal_connect_data(instance, name, callback, this, nullptr, G_CONNECT_SWAPPED); g_object_add_weak_pointer(G_OBJECT(instance), &instance_);