From 242519498a6110308f1d2fabf9703c87e8e972b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 5 Dec 2022 18:44:17 +0100 Subject: [PATCH] Recursively load themes Themes (other than the default theme) can specify a `parent-name` in the json file. This theme will be used as a base for the theme. Events and profiles of the children override those in the parents. For this we move the lookup code into it's separate class and also add tests. Closes: #38 --- src/fbd-feedback-manager.c | 98 +---- src/fbd-theme-expander.c | 400 ++++++++++++++++++ src/fbd-theme-expander.h | 28 ++ src/fbd.h | 1 + src/meson.build | 1 + .../user-config/feedbackd/themes/custom.json | 21 + .../xdg-data/feedbackd/themes/chainup.json | 4 + .../xdg-data/feedbackd/themes/default.json | 31 ++ .../xdg-data/feedbackd/themes/replace.json | 21 + tests/meson.build | 10 +- tests/test-fbd-theme-expander.c | 95 +++++ 11 files changed, 629 insertions(+), 81 deletions(-) create mode 100644 src/fbd-theme-expander.c create mode 100644 src/fbd-theme-expander.h create mode 100644 tests/data/user-config/feedbackd/themes/custom.json create mode 100644 tests/data/xdg-data/feedbackd/themes/chainup.json create mode 100644 tests/data/xdg-data/feedbackd/themes/default.json create mode 100644 tests/data/xdg-data/feedbackd/themes/replace.json create mode 100644 tests/test-fbd-theme-expander.c diff --git a/src/fbd-feedback-manager.c b/src/fbd-feedback-manager.c index e37cb8d..7872132 100644 --- a/src/fbd-feedback-manager.c +++ b/src/fbd-feedback-manager.c @@ -14,6 +14,10 @@ #include "fbd-feedback-vibra.h" #include "fbd-feedback-manager.h" #include "fbd-feedback-theme.h" +#include "fbd-theme-expander.h" + +#define GMOBILE_USE_UNSTABLE_API +#include #include #include @@ -21,14 +25,11 @@ #define FEEDBACKD_SCHEMA_ID "org.sigxcpu.feedbackd" #define FEEDBACKD_KEY_PROFILE "profile" -#define FEEDBACKD_THEME_VAR "FEEDBACK_THEME" #define APP_SCHEMA FEEDBACKD_SCHEMA_ID ".application" #define APP_PREFIX "/org/sigxcpu/feedbackd/application/" -#define DEVICE_TREE_PATH "/sys/firmware/devicetree/base/compatible" -#define DEVICE_NAME_MAX 1024 - +#define FEEDBACKD_THEME_VAR "FEEDBACK_THEME" /** * SECTION:fbd-feedback-manager @@ -430,65 +431,6 @@ fbd_feedback_manager_handle_end_feedback (LfbGdbusFeedback *object, return TRUE; } -static const gchar * -find_themefile (void) -{ - gint i = 0; - gsize len; - const gchar *comp; - - g_autoptr (GError) err = NULL; - g_autofree gchar *user_config_path = NULL; - gchar **xdg_data_dirs = (gchar **) g_get_system_data_dirs (); - g_autofree gchar *compatibles = NULL; - - // First look for a default file under $XDG_DATA_HOME - user_config_path = g_build_filename (g_get_user_config_dir (), "feedbackd", - "themes", "default.json", NULL); - if (g_file_test (user_config_path, (G_FILE_TEST_EXISTS))) { - g_debug ("Found user themefile at: %s", user_config_path); - return g_steal_pointer (&user_config_path); - } - - // Try to read the device name - if (g_file_test (DEVICE_TREE_PATH, (G_FILE_TEST_EXISTS))) { - g_debug ("Found device tree device compatible at %s", DEVICE_TREE_PATH); - - // Check if feedbackd has a proper config available this device - if (!g_file_get_contents (DEVICE_TREE_PATH, &compatibles, &len, &err)) - g_warning ("Unable to read: %s", err->message); - - comp = compatibles; - while (comp - compatibles < len) { - - // Iterate over $XDG_DATA_DIRS - for (i = 0; i < g_strv_length (xdg_data_dirs); i++) { - g_autofree gchar *config_path = NULL; - g_autofree gchar *theme_file_name = NULL; - - // We leave it to g_build_filename to add/remove erroneous path separators - theme_file_name = g_strconcat (comp, ".json", NULL); - config_path = g_build_filename (xdg_data_dirs[i], "feedbackd", "themes", - theme_file_name, NULL); - g_debug ("Searching for device specific themefile in %s", config_path); - - // Check if file exist - if (g_file_test (config_path, (G_FILE_TEST_EXISTS))) { - g_debug ("Found themefile for this device at: %s", config_path); - return g_steal_pointer (&config_path); - } - } - - // Next compatible - comp = strchr (comp, 0); - comp++; - } - }else { - g_debug ("Device tree path does not exist: %s", DEVICE_TREE_PATH); - } - - return NULL; -} static void fbd_feedback_manager_constructed (GObject *object) @@ -599,31 +541,31 @@ fbd_feedback_manager_get_dev_leds (FbdFeedbackManager *self) return self->leds; } -void fbd_feedback_manager_load_theme (FbdFeedbackManager *self) { - g_autoptr (GError) err = NULL; +void +fbd_feedback_manager_load_theme (FbdFeedbackManager *self) +{ + g_autoptr (FbdThemeExpander) expander = NULL; g_autoptr (FbdFeedbackTheme) theme = NULL; - const gchar *themefile; + g_autoptr (GError) err = NULL; + g_auto (GStrv) compatibles = NULL; - // Overide themefile with environment variable if requested - themefile = g_getenv (FEEDBACKD_THEME_VAR); + compatibles = gm_devicetree_get_compatibles (NULL, &err); + if (compatibles == NULL && err) { + g_debug ("Failed to get compatibles: %s", err->message); + g_clear_error (&err); + } - // Search for device-specific configuration - if (!themefile) - themefile = find_themefile (); + expander = fbd_theme_expander_new ((const char *const *)compatibles, + g_getenv (FEEDBACKD_THEME_VAR)); + theme = fbd_theme_expander_load_theme_files (expander, &err); - // Fallback to default configuration if needed - if (!themefile) - themefile = FEEDBACKD_THEME_DIR "/default.json"; - g_info ("Using themefile: %s", themefile); - - theme = fbd_feedback_theme_new_from_file (themefile, &err); if (theme) { g_set_object(&self->theme, theme); } else { if (self->theme) g_warning ("Failed to reload theme: %s", err->message); else - g_error ("Failed to load theme: %s", err->message); // No point to carry on + g_error ("Failed to load any theme: %s", err->message); // No point to carry on } } diff --git a/src/fbd-theme-expander.c b/src/fbd-theme-expander.c new file mode 100644 index 0000000..69a9cea --- /dev/null +++ b/src/fbd-theme-expander.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2022 Guido Günther + * + * SPDX-License-Identifier: GPL-3.0+ + * + * Author: Guido Günther + */ + +#define G_LOG_DOMAIN "fbd-theme-expander" + +#include "fbd.h" +#include "fbd-feedback-theme.h" +#include "fbd-theme-expander.h" + +#define DEVICE_TREE_PATH "/sys/firmware/devicetree/base/compatible" +#define DEVICE_NAME_MAX 1024 + +#define DEFAULT_THEME_NAME "default" +#define DEVICE_THEME_NAME "$device" + +#define MAX_THEME_DEPTH 10 + +/** + * SECTION:theme-expander + * @short_description: Feedback theme expander + * @Title: FbdThemeExpander + * + * The theme expander reads themes from disks and expands references + * to other themes + */ + +enum { + PROP_0, + PROP_THEME_FILE, + PROP_COMPATIBLES, + PROP_LAST_PROP +}; +static GParamSpec *props[PROP_LAST_PROP]; + +struct _FbdThemeExpander { + GObject parent; + + char *theme_file; + gboolean device_theme_loaded; + GStrv compatibles; +}; +G_DEFINE_TYPE (FbdThemeExpander, fbd_theme_expander, G_TYPE_OBJECT) + + +static void +fbd_theme_expander_set_compatibles (FbdThemeExpander *self, const char * const * compatibles) +{ + g_return_if_fail (FBD_IS_THEME_EXPANDER (self)); + + g_strfreev (self->compatibles); + self->compatibles = g_strdupv ((GStrv)compatibles); + + /* Make sure we reload the device theme */ + self->device_theme_loaded = FALSE; +} + + +static void +fbd_theme_expander_set_theme_file (FbdThemeExpander *self, const char *theme_file) +{ + g_return_if_fail (FBD_IS_THEME_EXPANDER (self)); + + g_free (self->theme_file); + self->theme_file = g_strdup (theme_file); + + /* Make sure we reload the device theme */ + self->device_theme_loaded = FALSE; +} + + +static char * +fbd_theme_expander_find_theme_in_xdg_data (const char *theme_name) +{ + const char * const *xdg_data_dirs = g_get_system_data_dirs (); + g_autofree char *theme_file_name = g_strconcat (theme_name, ".json", NULL); + + for (int i = 0; xdg_data_dirs[i] != NULL; i++) { + g_autofree char *theme_path = NULL; + + theme_path = g_build_filename (xdg_data_dirs[i], "feedbackd", "themes", + theme_file_name, NULL); + g_debug ("Looking for theme file at %s", theme_path); + + /* Check if file exist */ + if (g_file_test (theme_path, (G_FILE_TEST_EXISTS))) { + g_info ("Loading theme file at '%s'", theme_path); + return g_steal_pointer (&theme_path); + } + } + return NULL; +} + + +static char * +fbd_theme_expander_find_device_theme_path (FbdThemeExpander *self, const char *theme_name) +{ + /* Device specific lookup only for default theme */ + if (g_str_equal (theme_name, DEVICE_THEME_NAME) == FALSE && + g_str_equal (theme_name, DEFAULT_THEME_NAME) == FALSE) { + return NULL; + } + + if (self->device_theme_loaded) + return NULL; + + self->device_theme_loaded = TRUE; + + /* no compatibles found */ + if (self->compatibles == NULL) + return NULL; + + for (int i = 0; i < g_strv_length (self->compatibles); i++) { + const char *compatible = self->compatibles[i]; + g_autofree char *theme_path = NULL; + + theme_path = fbd_theme_expander_find_theme_in_xdg_data (compatible); + if (theme_path) { + g_info ("Loading themefile for compatible '%s' at: %s", compatible, theme_path); + return g_steal_pointer (&theme_path); + } + } + + g_debug ("No device theme found"); + return NULL; +} + + +static char * +fbd_theme_expander_find_user_theme_path (const char *theme_name) +{ + g_autofree char *filename = g_strdup_printf ("%s.json", theme_name); + g_autofree char *user_config_path = NULL; + + user_config_path = g_build_filename (g_get_user_config_dir (), "feedbackd", + "themes", filename, NULL); + if (g_file_test (user_config_path, (G_FILE_TEST_EXISTS))) { + g_info ("Found theme file at: %s", user_config_path); + return g_steal_pointer (&user_config_path); + } + + g_debug ("No user theme found for '%s'", theme_name); + return NULL; +} + + +static char * +fbd_theme_expander_find_theme_path (FbdThemeExpander *self, const char *theme_name) +{ + g_autofree char *file_name = NULL; + char *theme_path; + + g_assert (theme_name); + + theme_path = fbd_theme_expander_find_user_theme_path (theme_name); + if (theme_path) + return theme_path; + + theme_path = fbd_theme_expander_find_device_theme_path (self, theme_name); + if (theme_path) + return theme_path; + + if (g_str_equal (theme_name, DEFAULT_THEME_NAME) == FALSE) + g_critical ("Theme '%s' not found, falling back to default theme", theme_name); + + theme_path = fbd_theme_expander_find_theme_in_xdg_data (DEFAULT_THEME_NAME); + if (theme_path) + return theme_path; + + /* Last resort is shipped default config */ + file_name = g_strdup_printf ("%s.json", theme_name); + g_info ("Using theme_file: %s", file_name); + return g_build_filename (FEEDBACKD_THEME_DIR, file_name, NULL); +} + + +static void +fbd_theme_expander_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + FbdThemeExpander *self = FBD_THEME_EXPANDER (object); + + switch (property_id) { + case PROP_THEME_FILE: + fbd_theme_expander_set_theme_file (self, g_value_get_string (value)); + break; + case PROP_COMPATIBLES: + fbd_theme_expander_set_compatibles (self, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +fbd_theme_expander_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + FbdThemeExpander *self = FBD_THEME_EXPANDER (object); + + switch (property_id) { + case PROP_THEME_FILE: + g_value_set_string (value, fbd_theme_expander_get_theme_file (self)); + break; + case PROP_COMPATIBLES: + g_value_set_boxed (value, fbd_theme_expander_get_compatibles (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +fbd_theme_expander_finalize (GObject *object) +{ + FbdThemeExpander *self = FBD_THEME_EXPANDER(object); + + g_clear_pointer (&self->theme_file, g_free); + g_clear_pointer (&self->compatibles, g_strfreev); + + G_OBJECT_CLASS (fbd_theme_expander_parent_class)->finalize (object); +} + + +static void +fbd_theme_expander_class_init (FbdThemeExpanderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = fbd_theme_expander_get_property; + object_class->set_property = fbd_theme_expander_set_property; + object_class->finalize = fbd_theme_expander_finalize; + + /** + * FbdThemeExpander:theme-file: + * + * Specifies the theme-file to load an expand. Takes preference over `theme`. + */ + props[PROP_THEME_FILE] = + g_param_spec_string ("theme-file", "", "", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_STATIC_STRINGS); + /** + * FbdThemeExpander:compatibles: + * + * Specifies the device types this device is compatible with. The + * device compatbiles to look for. When unset the value is retrieved + * from /sysfs. + * + * The `compatibles` defines which device specific theme bits are loaded. + */ + props[PROP_COMPATIBLES] = + g_param_spec_boxed ("compatibles", "", "", + G_TYPE_STRV, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PROP_LAST_PROP, props); +} + + +static void +fbd_theme_expander_init (FbdThemeExpander *self) +{ +} + + +FbdThemeExpander * +fbd_theme_expander_new (const char * const *compatibles, const char *theme_file) +{ + return FBD_THEME_EXPANDER (g_object_new (FBD_TYPE_THEME_EXPANDER, + "theme-file", theme_file, + "compatibles", compatibles, + NULL)); +} + + +static void +update_theme (gpointer data, gpointer user_data) +{ + FbdFeedbackTheme *theme = FBD_FEEDBACK_THEME (data); + FbdFeedbackTheme *merged = FBD_FEEDBACK_THEME (user_data); + + g_assert (FBD_IS_FEEDBACK_THEME (theme)); + g_assert (FBD_IS_FEEDBACK_THEME (merged)); + + fbd_feedback_theme_update (merged, theme); +} + + +/** + * fbd_theme_expander_load_theme_files: + * @self: The theme expander + * @err: return location for error or %NULL + * + * Parses a theme recursively taking the theme's `parent-name` relations into + * account as well as the expander's list of `compatibles`. + * + * Returns: (transfer full)(allow-none): The parsed theme or %NULL on error + */ +FbdFeedbackTheme * +fbd_theme_expander_load_theme_files (FbdThemeExpander *self, GError **err) +{ + g_autoqueue (FbdFeedbackTheme) queue = g_queue_new (); + g_autoptr (FbdFeedbackTheme) merged = fbd_feedback_theme_new ("merged-theme"); + g_autoptr (FbdFeedbackTheme) theme = NULL; + g_autofree char *theme_file = NULL; + guint len = 0; + + g_return_val_if_fail (FBD_IS_THEME_EXPANDER (self), NULL); + g_return_val_if_fail (err == NULL || *err == NULL, NULL); + + if (self->theme_file) + theme_file = g_strdup (self->theme_file); + else { + theme_file = fbd_theme_expander_find_theme_path (self, DEFAULT_THEME_NAME); + if (g_strcmp0 (self->theme_file, theme_file)) { + self->theme_file = g_steal_pointer (&theme_file); + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_THEME_FILE]); + } + } + + theme = fbd_feedback_theme_new_from_file (self->theme_file, err); + if (theme == NULL) + return NULL; + + /* Build a list of themes */ + while (TRUE) { + g_autofree char *parent_path = NULL; + const char *parent_name, *theme_name; + + if (len > MAX_THEME_DEPTH) { + g_set_error (err, fbd_error_quark(), FBD_ERROR_THEME_EXPAND, "Theme depth exceeded"); + return NULL; + } + + theme_name = fbd_feedback_theme_get_name (theme); + if (theme_name == NULL || theme_name[0] == '\0') { + g_set_error (err, fbd_error_quark(), FBD_ERROR_THEME_EXPAND, + "Theme name of %s can't be empty", self->theme_file); + return NULL; + } + + parent_name = fbd_feedback_theme_get_parent_name (theme); + + if (parent_name && + g_str_equal (fbd_feedback_theme_get_name (theme), DEFAULT_THEME_NAME)) { + g_set_error (err, fbd_error_quark(), FBD_ERROR_THEME_EXPAND, + "Default theme can't specify a parent"); + return NULL; + } + + parent_name = fbd_feedback_theme_get_parent_name (theme); + + g_queue_push_head (queue, g_steal_pointer (&theme)); + + if (parent_name == NULL) + break; + + parent_path = fbd_theme_expander_find_theme_path (self, parent_name); + theme = fbd_feedback_theme_new_from_file (parent_path, err); + if (theme == NULL) + return NULL; + + len++; + } + + /* Merge themes bottom to top */ + g_queue_foreach (queue, update_theme, merged); + + return g_steal_pointer (&merged); +} + +const char * +fbd_theme_expander_get_theme_file (FbdThemeExpander *self) +{ + g_return_val_if_fail (FBD_IS_THEME_EXPANDER (self), NULL); + + return self->theme_file; +} + +const char * const * +fbd_theme_expander_get_compatibles (FbdThemeExpander *self) +{ + g_return_val_if_fail (FBD_IS_THEME_EXPANDER (self), NULL); + + return (const char * const*)self->compatibles; +} diff --git a/src/fbd-theme-expander.h b/src/fbd-theme-expander.h new file mode 100644 index 0000000..8d2db79 --- /dev/null +++ b/src/fbd-theme-expander.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 Guido Günther + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Author: Guido Günther + */ + +#pragma once + +#include "fbd-feedback-theme.h" + +#include + +G_BEGIN_DECLS + +#define FBD_TYPE_THEME_EXPANDER (fbd_theme_expander_get_type ()) + +G_DECLARE_FINAL_TYPE (FbdThemeExpander, fbd_theme_expander, FBD, THEME_EXPANDER, GObject) + +FbdThemeExpander *fbd_theme_expander_new (const char * const *compatibles, + const char *theme_file); +FbdFeedbackTheme *fbd_theme_expander_load_theme_files (FbdThemeExpander *self, + GError **err); +const char *fbd_theme_expander_get_theme_file (FbdThemeExpander *self); +const char * const *fbd_theme_expander_get_compatibles (FbdThemeExpander *self); + +G_END_DECLS diff --git a/src/fbd.h b/src/fbd.h index c5a1e12..82da4e2 100644 --- a/src/fbd.h +++ b/src/fbd.h @@ -13,6 +13,7 @@ G_BEGIN_DECLS typedef enum { FBD_ERROR_FAILED = 0, + FBD_ERROR_THEME_EXPAND = 1, } FbdError; GQuark fbd_error_quark (void); diff --git a/src/meson.build b/src/meson.build index cb507de..4a2db09 100644 --- a/src/meson.build +++ b/src/meson.build @@ -27,6 +27,7 @@ sources = [ 'fbd-feedback-vibra.c', 'fbd-feedback-vibra-periodic.c', 'fbd-feedback-vibra-rumble.c', + 'fbd-theme-expander.c', 'fbd-udev.c', ] diff --git a/tests/data/user-config/feedbackd/themes/custom.json b/tests/data/user-config/feedbackd/themes/custom.json new file mode 100644 index 0000000..d208710 --- /dev/null +++ b/tests/data/user-config/feedbackd/themes/custom.json @@ -0,0 +1,21 @@ +{ + "name" : "custom", + "parent-name": "default", + "profiles" : [ + { + "name" : "full", + "feedbacks" : [ + { + "event-name" : "test-dummy-0", + "type" : "Dummy", + "duration" : 0x10 + }, + { + "event-name" : "test-dummy-2", + "type" : "Dummy", + "duration" : 0x30 + } + ] + } + ] +} diff --git a/tests/data/xdg-data/feedbackd/themes/chainup.json b/tests/data/xdg-data/feedbackd/themes/chainup.json new file mode 100644 index 0000000..e0b3278 --- /dev/null +++ b/tests/data/xdg-data/feedbackd/themes/chainup.json @@ -0,0 +1,4 @@ +{ + "name" : "$device", + "parent-name": "default" +} diff --git a/tests/data/xdg-data/feedbackd/themes/default.json b/tests/data/xdg-data/feedbackd/themes/default.json new file mode 100644 index 0000000..0b5d87d --- /dev/null +++ b/tests/data/xdg-data/feedbackd/themes/default.json @@ -0,0 +1,31 @@ +{ + "name" : "default", + "profiles" : [ + { + "name" : "full", + "feedbacks" : [ + { + "event-name" : "test-dummy-0", + "type" : "Dummy" + }, + { + "event-name" : "test-dummy-1", + "type" : "Dummy" + } + ] + }, + { + "name" : "quiet", + "feedbacks" : [ + { + "event-name" : "test-dummy-0", + "type" : "Dummy" + }, + { + "event-name" : "test-dummy-1", + "type" : "Dummy" + } + ] + } + ] +} diff --git a/tests/data/xdg-data/feedbackd/themes/replace.json b/tests/data/xdg-data/feedbackd/themes/replace.json new file mode 100644 index 0000000..1fc0e26 --- /dev/null +++ b/tests/data/xdg-data/feedbackd/themes/replace.json @@ -0,0 +1,21 @@ +{ + "name" : "$device", + "parent-name": "default", + "profiles" : [ + { + "name" : "full", + "feedbacks" : [ + { + "event-name" : "test-dummy-0", + "type" : "Dummy", + "duration" : 0x10 + }, + { + "event-name" : "test-dummy-2", + "type" : "Dummy", + "duration" : 0x20 + } + ] + } + ] +} diff --git a/tests/meson.build b/tests/meson.build index 85b1d7e..c776cf0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -11,7 +11,7 @@ test_env.set('MALLOC_CHECK_', '2') test_env.set('XDG_CONFIG_HOME', meson.current_source_dir()) test_env.set('XDG_CONFIG_DIRS', meson.current_source_dir()) -# Shared lfbrary Unit tests +# Shared library Unit tests test_lfb_cflags = [ '-DTEST_APP_ID="org.sigxcpu.feedbackd_test"', @@ -52,10 +52,13 @@ endforeach # Daemon Unit tests if get_option('daemon') +test_env_fbd = test_env +test_env_fbd.set('XDG_DATA_DIRS', meson.current_source_dir() / 'data' / 'xdg-data') + test_fbd_cflags = [ '-DTEST_APP_ID="org.sigxcpu.feedbackd_test"', '-DTEST_DATA_DIR="@0@"'.format(join_paths(meson.current_source_dir(), 'data')), -] + ] test_fbd_link_args = [ '-fPIC', @@ -69,6 +72,7 @@ fbd_tests = [ 'fbd-feedback-profile', 'fbd-feedback-theme', 'fbd-event', + 'fbd-theme-expander', ] foreach test : fbd_tests @@ -79,7 +83,7 @@ foreach test : fbd_tests link_args : test_fbd_link_args, include_directories : fbd_inc, dependencies : test_fbd_deps) - test(test, t, env : test_env) + test(test, t, env : test_env_fbd) endforeach diff --git a/tests/test-fbd-theme-expander.c b/tests/test-fbd-theme-expander.c new file mode 100644 index 0000000..1bde3cd --- /dev/null +++ b/tests/test-fbd-theme-expander.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022 Guido Günther + * + * SPDX-License-Identifier: GPL-3.0+ + */ + +#include "fbd-feedback-dummy.h" +#include "fbd-theme-expander.h" + +#include + + +static void +test_fbd_theme_expander_object (void) +{ + g_autoptr (FbdThemeExpander) expander = g_object_new (FBD_TYPE_THEME_EXPANDER, NULL); + + g_assert_null (fbd_theme_expander_get_theme_file (expander)); + g_assert_null (fbd_theme_expander_get_compatibles (expander)); +} + +static void +test_fbd_theme_expander_device (void) +{ + g_autoptr (GError) err = NULL; + const char *compatibles[] = { NULL, NULL }; + FbdFeedbackProfile *profile; + FbdThemeExpander *expander; + FbdFeedbackTheme *theme; + FbdFeedbackBase *fb; + const char *const *compatibles2; + + /* just the default profile */ + compatibles[0] = "doesnotexist"; + expander = fbd_theme_expander_new (compatibles, NULL); + theme = fbd_theme_expander_load_theme_files (expander, &err); + compatibles2 = fbd_theme_expander_get_compatibles (expander); + g_assert_cmpstrv (compatibles2, compatibles); + g_assert_no_error (err); + profile = fbd_feedback_theme_get_profile (theme, "silent"); + g_assert_null (profile); + profile = fbd_feedback_theme_get_profile (theme, "quiet"); + g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile)); + profile = fbd_feedback_theme_get_profile (theme, "full"); + g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile)); + g_assert_finalize_object (theme); + g_assert_finalize_object (expander); + + /* Empty device theme that just chains up */ + compatibles[0] = "chainup"; + expander = fbd_theme_expander_new (compatibles, NULL); + compatibles2 = fbd_theme_expander_get_compatibles (expander); + g_assert_cmpstrv (compatibles2, compatibles); + theme = fbd_theme_expander_load_theme_files (expander, &err); + g_assert_no_error (err); + profile = fbd_feedback_theme_get_profile (theme, "silent"); + g_assert_null (profile); + profile = fbd_feedback_theme_get_profile (theme, "quiet"); + g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile)); + profile = fbd_feedback_theme_get_profile (theme, "full"); + g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile)); + g_assert_finalize_object (theme); + g_assert_finalize_object (expander); + + /* Replace an existing event */ + compatibles[0] = "replace"; + expander = fbd_theme_expander_new (compatibles, NULL); + compatibles2 = fbd_theme_expander_get_compatibles (expander); + g_assert_cmpstrv (compatibles2, compatibles); + theme = fbd_theme_expander_load_theme_files (expander, &err); + g_assert_no_error (err); + profile = fbd_feedback_theme_get_profile (theme, "full"); + g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile)); + fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-0"); + g_assert_cmpstr ("test-dummy-0", ==, fbd_feedback_get_event_name (fb)); + g_assert_cmpint (fbd_feedback_dummy_get_duration (FBD_FEEDBACK_DUMMY (fb)), ==, 0x10); + g_assert_true (FBD_IS_FEEDBACK_PROFILE(profile)); + fb = fbd_feedback_profile_get_feedback (profile, "test-dummy-2"); + g_assert_cmpstr ("test-dummy-2", ==, fbd_feedback_get_event_name (fb)); + g_assert_cmpint (fbd_feedback_dummy_get_duration (FBD_FEEDBACK_DUMMY (fb)), ==, 0x20); + g_assert_finalize_object (theme); + g_assert_finalize_object (expander); +} + + +gint +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func("/feedbackd/fbd/theme-expander/object", test_fbd_theme_expander_object); + g_test_add_func("/feedbackd/fbd/theme-expander/device", test_fbd_theme_expander_device); + + return g_test_run(); +}