Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit b4e17bb

Browse files
committed
[Linux] make desktop settings testable
FlSettings is an interface that provides desktop settings and notifies changes, whereas FlSettingsPlugin merely communicates the settings to the framework. This makes it straightforward to test both separately. As a bonus, it will be easy to add FlSettings implementations for other DEs too.
1 parent 5f4262d commit b4e17bb

15 files changed

+715
-63
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,9 @@ FILE: ../../../flutter/shell/platform/linux/fl_event_channel.cc
20272027
FILE: ../../../flutter/shell/platform/linux/fl_event_channel_test.cc
20282028
FILE: ../../../flutter/shell/platform/linux/fl_gl_area.cc
20292029
FILE: ../../../flutter/shell/platform/linux/fl_gl_area.h
2030+
FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings.cc
2031+
FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings.h
2032+
FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings_test.cc
20302033
FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec.cc
20312034
FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec_test.cc
20322035
FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec.cc
@@ -2076,8 +2079,11 @@ FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.cc
20762079
FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.h
20772080
FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.cc
20782081
FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.h
2082+
FILE: ../../../flutter/shell/platform/linux/fl_settings.cc
2083+
FILE: ../../../flutter/shell/platform/linux/fl_settings.h
20792084
FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.cc
20802085
FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin.h
2086+
FILE: ../../../flutter/shell/platform/linux/fl_settings_plugin_test.cc
20812087
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc
20822088
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_private.h
20832089
FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc

shell/platform/linux/BUILD.gn

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ source_set("flutter_linux_sources") {
107107
"fl_engine.cc",
108108
"fl_event_channel.cc",
109109
"fl_gl_area.cc",
110+
"fl_gnome_settings.cc",
110111
"fl_json_message_codec.cc",
111112
"fl_json_method_codec.cc",
112113
"fl_key_channel_responder.cc",
@@ -128,6 +129,7 @@ source_set("flutter_linux_sources") {
128129
"fl_renderer.cc",
129130
"fl_renderer_gl.cc",
130131
"fl_renderer_headless.cc",
132+
"fl_settings.cc",
131133
"fl_settings_plugin.cc",
132134
"fl_standard_message_codec.cc",
133135
"fl_standard_method_codec.cc",
@@ -175,6 +177,13 @@ test_fixtures("flutter_linux_fixtures") {
175177
fixtures = []
176178
}
177179

180+
copy("flutter_linux_gschemas") {
181+
testonly = true
182+
183+
sources = [ "testing/gschemas/ubuntu-20.04.compiled" ]
184+
outputs = [ "$target_gen_dir/assets/{{source_name_part}}/gschemas.compiled" ]
185+
}
186+
178187
executable("flutter_linux_unittests") {
179188
testonly = true
180189

@@ -186,6 +195,7 @@ executable("flutter_linux_unittests") {
186195
"fl_dart_project_test.cc",
187196
"fl_engine_test.cc",
188197
"fl_event_channel_test.cc",
198+
"fl_gnome_settings_test.cc",
189199
"fl_json_message_codec_test.cc",
190200
"fl_json_method_codec_test.cc",
191201
"fl_key_channel_responder_test.cc",
@@ -197,6 +207,7 @@ executable("flutter_linux_unittests") {
197207
"fl_method_response_test.cc",
198208
"fl_pixel_buffer_texture_test.cc",
199209
"fl_plugin_registrar_test.cc",
210+
"fl_settings_plugin_test.cc",
200211
"fl_standard_message_codec_test.cc",
201212
"fl_standard_method_codec_test.cc",
202213
"fl_string_codec_test.cc",
@@ -210,6 +221,7 @@ executable("flutter_linux_unittests") {
210221
"testing/mock_epoxy.cc",
211222
"testing/mock_plugin_registrar.cc",
212223
"testing/mock_renderer.cc",
224+
"testing/mock_settings.cc",
213225
"testing/mock_signal_handler.cc",
214226
"testing/mock_text_input_plugin.cc",
215227
"testing/mock_texture_registrar.cc",
@@ -229,6 +241,7 @@ executable("flutter_linux_unittests") {
229241

230242
deps = [
231243
":flutter_linux_fixtures",
244+
":flutter_linux_gschemas",
232245
":flutter_linux_sources",
233246
"//flutter/runtime:libdart",
234247
"//flutter/shell/platform/embedder:embedder_headers",

shell/platform/linux/fl_engine.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,9 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
516516

517517
setup_locales(self);
518518

519+
g_autoptr(FlSettings) settings = fl_settings_new();
519520
self->settings_plugin = fl_settings_plugin_new(self->binary_messenger);
520-
fl_settings_plugin_start(self->settings_plugin);
521+
fl_settings_plugin_start(self->settings_plugin, settings);
521522

522523
result = self->embedder_api.UpdateSemanticsEnabled(self->engine, TRUE);
523524
if (result != kSuccess) {
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/shell/platform/linux/fl_gnome_settings.h"
6+
7+
#include <glib.h>
8+
9+
static constexpr char kDesktopInterfaceSchema[] = "org.gnome.desktop.interface";
10+
static constexpr char kDesktopTextScalingFactorKey[] = "text-scaling-factor";
11+
static constexpr char kDesktopClockFormatKey[] = "clock-format";
12+
static constexpr char kDesktopGtkThemeKey[] = "gtk-theme";
13+
14+
static constexpr char kClockFormat12Hour[] = "12h";
15+
static constexpr char kGtkThemeDarkSuffix[] = "-dark";
16+
17+
G_DECLARE_FINAL_TYPE(FlGnomeSettings,
18+
fl_gnome_settings,
19+
FL,
20+
GNOME_SETTINGS,
21+
GObject);
22+
23+
struct _FlGnomeSettings {
24+
GObject parent_instance;
25+
26+
GSettings* interface_settings;
27+
};
28+
29+
static void fl_gnome_settings_iface_init(FlSettingsInterface* iface);
30+
31+
#define FL_UNUSED(x) (void)x;
32+
33+
G_DEFINE_TYPE_WITH_CODE(FlGnomeSettings,
34+
fl_gnome_settings,
35+
G_TYPE_OBJECT,
36+
G_IMPLEMENT_INTERFACE(fl_settings_get_type(),
37+
fl_gnome_settings_iface_init)
38+
FL_UNUSED(FL_IS_GNOME_SETTINGS))
39+
40+
static FlClockFormat fl_gnome_settings_get_clock_format(FlSettings* settings) {
41+
FlGnomeSettings* self = FL_GNOME_SETTINGS(settings);
42+
43+
FlClockFormat clock_format = FL_CLOCK_FORMAT_24H;
44+
45+
if (self->interface_settings != nullptr) {
46+
g_autofree gchar* value =
47+
g_settings_get_string(self->interface_settings, kDesktopClockFormatKey);
48+
if (g_strcmp0(value, kClockFormat12Hour) == 0) {
49+
clock_format = FL_CLOCK_FORMAT_12H;
50+
}
51+
}
52+
return clock_format;
53+
}
54+
55+
static FlColorScheme fl_gnome_settings_get_color_scheme(FlSettings* settings) {
56+
FlGnomeSettings* self = FL_GNOME_SETTINGS(settings);
57+
58+
FlColorScheme color_scheme = FL_COLOR_SCHEME_LIGHT;
59+
60+
if (self->interface_settings != nullptr) {
61+
// check whether org.gnome.desktop.interface.gtk-theme ends with "-dark"
62+
g_autofree gchar* value =
63+
g_settings_get_string(self->interface_settings, kDesktopGtkThemeKey);
64+
if (g_str_has_suffix(value, kGtkThemeDarkSuffix)) {
65+
color_scheme = FL_COLOR_SCHEME_DARK;
66+
}
67+
}
68+
return color_scheme;
69+
}
70+
71+
static gdouble fl_gnome_settings_get_text_scaling_factor(FlSettings* settings) {
72+
FlGnomeSettings* self = FL_GNOME_SETTINGS(settings);
73+
74+
gdouble scaling_factor = 1.0;
75+
76+
if (self->interface_settings != nullptr) {
77+
scaling_factor = g_settings_get_double(self->interface_settings,
78+
kDesktopTextScalingFactorKey);
79+
}
80+
return scaling_factor;
81+
}
82+
83+
static void fl_gnome_settings_dispose(GObject* object) {
84+
FlGnomeSettings* self = FL_GNOME_SETTINGS(object);
85+
86+
g_clear_object(&self->interface_settings);
87+
88+
G_OBJECT_CLASS(fl_gnome_settings_parent_class)->dispose(object);
89+
}
90+
91+
static void fl_gnome_settings_class_init(FlGnomeSettingsClass* klass) {
92+
G_OBJECT_CLASS(klass)->dispose = fl_gnome_settings_dispose;
93+
}
94+
95+
static void fl_gnome_settings_iface_init(FlSettingsInterface* iface) {
96+
iface->get_clock_format = fl_gnome_settings_get_clock_format;
97+
iface->get_color_scheme = fl_gnome_settings_get_color_scheme;
98+
iface->get_text_scaling_factor = fl_gnome_settings_get_text_scaling_factor;
99+
}
100+
101+
static void fl_gnome_settings_init(FlGnomeSettings* self) {}
102+
103+
static GSettings* default_interface_settings() {
104+
GSettings* interface_settings = nullptr;
105+
GSettingsSchemaSource* source = g_settings_schema_source_get_default();
106+
if (source != nullptr) {
107+
g_autoptr(GSettingsSchema) schema =
108+
g_settings_schema_source_lookup(source, kDesktopInterfaceSchema, TRUE);
109+
if (schema != nullptr) {
110+
interface_settings = g_settings_new_full(schema, nullptr, nullptr);
111+
}
112+
}
113+
return interface_settings;
114+
}
115+
116+
FlSettings* fl_gnome_settings_new(GSettings* interface_settings) {
117+
FlGnomeSettings* self =
118+
FL_GNOME_SETTINGS(g_object_new(fl_gnome_settings_get_type(), nullptr));
119+
120+
if (interface_settings != nullptr) {
121+
self->interface_settings = G_SETTINGS(g_object_ref(interface_settings));
122+
} else {
123+
self->interface_settings = default_interface_settings();
124+
}
125+
126+
if (self->interface_settings != nullptr) {
127+
g_signal_connect_object(self->interface_settings, "changed::clock-format",
128+
G_CALLBACK(fl_settings_emit_changed), self,
129+
G_CONNECT_SWAPPED);
130+
g_signal_connect_object(self->interface_settings, "changed::gtk-theme",
131+
G_CALLBACK(fl_settings_emit_changed), self,
132+
G_CONNECT_SWAPPED);
133+
g_signal_connect_object(
134+
self->interface_settings, "changed::text-scaling-factor",
135+
G_CALLBACK(fl_settings_emit_changed), self, G_CONNECT_SWAPPED);
136+
}
137+
138+
return FL_SETTINGS(self);
139+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_GNOME_SETTINGS_H_
6+
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_GNOME_SETTINGS_H_
7+
8+
#include "flutter/shell/platform/linux/fl_settings.h"
9+
10+
#include <gio/gio.h>
11+
12+
G_BEGIN_DECLS
13+
14+
/**
15+
* fl_gnome_settings_new:
16+
* @interface_settings: (allow-none): #GSettings for testing purposes, or %NULL.
17+
*
18+
* Creates a new settings instance for GNOME.
19+
*
20+
* Returns: a new #FlSettings.
21+
*/
22+
FlSettings* fl_gnome_settings_new(GSettings* interface_settings);
23+
24+
G_END_DECLS
25+
26+
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_GNOME_SETTINGS_H_
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/shell/platform/linux/fl_gnome_settings.h"
6+
#include "flutter/shell/platform/linux/testing/fl_test.h"
7+
#include "flutter/shell/platform/linux/testing/mock_settings.h"
8+
#include "flutter/shell/platform/linux/testing/mock_signal_handler.h"
9+
#include "flutter/testing/testing.h"
10+
11+
#include <gio/gio.h>
12+
#define G_SETTINGS_ENABLE_BACKEND
13+
#include <gio/gsettingsbackend.h>
14+
15+
#include "gmock/gmock.h"
16+
#include "gtest/gtest.h"
17+
18+
class FlGnomeSettingsTest : public ::testing::Test {
19+
protected:
20+
void SetUp() override {
21+
// force _g_io_modules_ensure_extension_points_registered() to get called
22+
g_settings_backend_get_default();
23+
}
24+
};
25+
26+
static GSettings* create_interface_settings(const gchar* name) {
27+
g_autofree gchar* path =
28+
g_build_filename(flutter::testing::GetFixturesPath(), name, nullptr);
29+
g_autoptr(GSettingsSchemaSource) source =
30+
g_settings_schema_source_new_from_directory(path, nullptr, false,
31+
nullptr);
32+
g_autoptr(GSettingsSchema) schema = g_settings_schema_source_lookup(
33+
source, "org.gnome.desktop.interface", false);
34+
g_autoptr(GSettingsBackend) backend = g_memory_settings_backend_new();
35+
return g_settings_new_full(schema, backend, nullptr);
36+
}
37+
38+
TEST_F(FlGnomeSettingsTest, ClockFormat) {
39+
g_autoptr(GSettings) interface_settings =
40+
create_interface_settings("ubuntu-20.04");
41+
g_settings_set_string(interface_settings, "clock-format", "24h");
42+
43+
g_autoptr(FlSettings) settings = fl_gnome_settings_new(interface_settings);
44+
EXPECT_EQ(fl_settings_get_clock_format(settings), FL_CLOCK_FORMAT_24H);
45+
46+
flutter::testing::MockSignalHandler settings_changed(settings, "changed");
47+
EXPECT_SIGNAL(settings_changed).Times(1);
48+
49+
g_settings_set_string(interface_settings, "clock-format", "12h");
50+
EXPECT_EQ(fl_settings_get_clock_format(settings), FL_CLOCK_FORMAT_12H);
51+
}
52+
53+
TEST_F(FlGnomeSettingsTest, GtkTheme) {
54+
g_autoptr(GSettings) interface_settings =
55+
create_interface_settings("ubuntu-20.04");
56+
g_settings_set_string(interface_settings, "gtk-theme", "Yaru");
57+
58+
g_autoptr(FlSettings) settings = fl_gnome_settings_new(interface_settings);
59+
EXPECT_EQ(fl_settings_get_color_scheme(settings), FL_COLOR_SCHEME_LIGHT);
60+
61+
flutter::testing::MockSignalHandler settings_changed(settings, "changed");
62+
EXPECT_SIGNAL(settings_changed).Times(1);
63+
64+
g_settings_set_string(interface_settings, "gtk-theme", "Yaru-dark");
65+
EXPECT_EQ(fl_settings_get_color_scheme(settings), FL_COLOR_SCHEME_DARK);
66+
}
67+
68+
TEST_F(FlGnomeSettingsTest, TextScalingFactor) {
69+
g_autoptr(GSettings) interface_settings =
70+
create_interface_settings("ubuntu-20.04");
71+
g_settings_set_double(interface_settings, "text-scaling-factor", 1.0);
72+
73+
g_autoptr(FlSettings) settings = fl_gnome_settings_new(interface_settings);
74+
EXPECT_EQ(fl_settings_get_text_scaling_factor(settings), 1.0);
75+
76+
flutter::testing::MockSignalHandler settings_changed(settings, "changed");
77+
EXPECT_SIGNAL(settings_changed).Times(1);
78+
79+
g_settings_set_double(interface_settings, "text-scaling-factor", 1.5);
80+
EXPECT_EQ(fl_settings_get_text_scaling_factor(settings), 1.5);
81+
}
82+
83+
TEST_F(FlGnomeSettingsTest, SignalHandlers) {
84+
g_autoptr(GSettings) interface_settings =
85+
create_interface_settings("ubuntu-20.04");
86+
87+
FlSettings* settings = fl_gnome_settings_new(interface_settings);
88+
flutter::testing::MockSignalHandler settings_changed(settings, "changed");
89+
90+
EXPECT_SIGNAL(settings_changed).Times(3);
91+
92+
g_settings_set_string(interface_settings, "clock-format", "12h");
93+
g_settings_set_string(interface_settings, "gtk-theme", "Yaru-dark");
94+
g_settings_set_double(interface_settings, "text-scaling-factor", 1.5);
95+
96+
EXPECT_SIGNAL(settings_changed).Times(0);
97+
98+
g_clear_object(&settings);
99+
100+
// destroyed FlSettings object must have disconnected its signal handlers
101+
g_settings_set_string(interface_settings, "clock-format", "24h");
102+
g_settings_set_string(interface_settings, "gtk-theme", "Yaru");
103+
g_settings_set_double(interface_settings, "text-scaling-factor", 2.0);
104+
}

0 commit comments

Comments
 (0)