lib: Add wp_settings_apply_rule() API

- add C based tests
- add lua binding for the API and corresponding lua tests
- address comments from previous commits
This commit is contained in:
Ashok Sidipotu 2022-04-25 10:45:34 +05:30 committed by Julian Bouzas
parent 229f5d0630
commit a5d62b7bbd
7 changed files with 443 additions and 93 deletions

View file

@ -101,16 +101,51 @@ gboolean wp_settings_get_boolean (WpSettings *self, const gchar *setting)
* in the conf file.
* \param client_props client props array, these properties are inputs on which
* the rules are applied.
* \param applied_props the resultant actions/properties as a result of the
* application of rules are copied here.
* \returns TRUE if there is a match for the client_props and
* returns the applied props for the match.
* \param applied_props (nullable) the resultant actions/properties as a result
* of the application of rules are copied, if this is null props will be
* appended to client_props.
* \returns TRUE if there is a match for the client_props and returns the
* applied props for the match.
*/
gboolean wp_settings_apply_rule (WpSettings *self, const gchar *rule,
WpProperties *client_props, WpProperties *applied_props)
{
/* get the rule */
return true;
g_return_val_if_fail (self, false);
g_return_val_if_fail (rule, false);
g_return_val_if_fail (client_props, false);
wp_debug_object (self, "applying rule(%s) for client props(%d)",
rule, wp_properties_get_count (client_props));
for (guint i = 0; i < self->rules->len; i++) {
Rule *r = g_ptr_array_index (self->rules, i);
if (g_str_equal (rule, r->rule)) {
for (guint j = 0; j < r->matches->len; j++) {
Match *m = g_ptr_array_index (r->matches, j);
for (guint k = 0; k < m->interests->len; k++) {
WpObjectInterest *interest = g_ptr_array_index (m->interests, k);
wp_debug_object (self, ". working on interest obj(%p)", interest);
if (wp_object_interest_matches (interest, client_props)) {
if (applied_props)
wp_properties_add (applied_props, m->actions);
else
wp_properties_add (client_props, m->actions);
wp_debug_object (self, ". match found with actions(%d)",
wp_properties_get_count(m->actions));
return TRUE;
}
}
}
}
}
return FALSE;
}
enum {
@ -178,16 +213,16 @@ wp_settings_get_instance (WpCore *core, const gchar *metadata_name)
wp_registry_register_object (registry, g_object_ref (settings));
wp_debug_object (settings, "created wpsettings object for metadata"
wp_info_object (settings, "created wpsettings object for metadata"
" name \"%s\"", name);
} else {
wp_debug_object (settings, "found this wpsettings object for metadata name"
wp_info_object (settings, "found this wpsettings object for metadata name"
" \"%s\"", name);
}
return settings;
}
void
static void
match_unref (Match * self)
{
g_clear_pointer (&self->actions, wp_properties_unref);
@ -201,6 +236,8 @@ parse_actions (const gchar *actions)
g_autofree gchar *update_props = NULL;
g_autoptr (WpProperties) a_props = wp_properties_new_empty ();
wp_debug(".. parsing actions");
if (wp_spa_json_is_object (o) &&
wp_spa_json_object_get (o,
"update-props", "s", &update_props,
@ -209,7 +246,7 @@ parse_actions (const gchar *actions)
g_autoptr (WpIterator) iter = wp_spa_json_new_iterator (json);
g_auto (GValue) item = G_VALUE_INIT;
wp_debug ("update-props=%s", update_props);
wp_debug (".. update-props=%s", update_props);
while (wp_iterator_next (iter, &item)) {
WpSpaJson *p = g_value_get_boxed (&item);
@ -224,15 +261,13 @@ parse_actions (const gchar *actions)
g_value_unset (&item);
if (prop && value) {
wp_debug ("prop=%s value=%s", prop, value);
wp_debug (".. prop=%s value=%s", prop, value);
wp_properties_set (a_props, prop, value);
}
}
} else
{
wp_info ("\"update-props\" not defined properly, skip it");
} else {
wp_warning ("malformated JSON: \"update-props\" not defined properly"
", skip it");
return NULL;
}
@ -249,13 +284,15 @@ parse_matches (const gchar *match)
g_return_val_if_fail (m, NULL);
wp_debug(".. parsing match");
m->interests = g_ptr_array_new_with_free_func
((GDestroyNotify) wp_object_interest_unref);
if (!wp_spa_json_is_array (a))
{
wp_info ("Match has to be an array JSON element, skip processing this one");
return NULL;
wp_warning ("malformated JSON: matches has to be an array JSON element"
", skip processing this one");
return NULL;
}
for (; wp_iterator_next (a_iter, &a_item); g_value_unset (&a_item)) {
@ -268,25 +305,38 @@ parse_matches (const gchar *match)
while (wp_iterator_next (o_iter, &o_item)) {
WpSpaJson *p = g_value_get_boxed (&o_item);
if (wp_spa_json_is_container (p))
{
wp_warning ("malformated JSON: misplaced container object, pls check"
" JSON formatting of .conf file, skipping this container");
continue;
}
g_autofree gchar *isubject = wp_spa_json_parse_string (p);
g_autofree gchar *ivalue = NULL;
g_autofree gchar *value = NULL;
gchar *ivalue = NULL;
WpConstraintVerb iverb = WP_CONSTRAINT_VERB_EQUALS;
g_value_unset (&o_item);
wp_iterator_next (o_iter, &o_item);
p = g_value_get_boxed (&o_item);
ivalue = wp_spa_json_parse_string (p);
ivalue = value = wp_spa_json_parse_string (p);
g_value_unset (&o_item);
if (value[0] == '~')
{
iverb = WP_CONSTRAINT_VERB_MATCHES;
ivalue = value+1;
}
if (isubject && ivalue) {
wp_object_interest_add_constraint (i, WP_CONSTRAINT_TYPE_PW_PROPERTY,
isubject, WP_CONSTRAINT_VERB_EQUALS, g_variant_new_string(ivalue));
isubject, iverb, g_variant_new_string(ivalue));
count++;
wp_info ("subject=%s value=%s of interest obj=%p",
isubject, ivalue, i);
wp_debug (".. subject=%s verb=%d value=%s of interest obj=%p",
isubject, iverb, ivalue, i);
}
}
wp_info ("loaded interest obj(%p) with (%d) constraints", i, count);
wp_debug (".. loaded interest obj(%p) with (%d) constraints", i, count);
g_ptr_array_add (m->interests, g_steal_pointer(&i));
}
return m;
@ -305,6 +355,7 @@ parse_rule (const gchar *rule, const gchar *value)
/* TBD: check for duplicate rule names and disallow them. */
r->rule = g_strdup (rule);
wp_debug (". parsing rule(%s)", r->rule);
r->matches = g_ptr_array_new_with_free_func
((GDestroyNotify) match_unref);
@ -319,16 +370,19 @@ parse_rule (const gchar *rule, const gchar *value)
"matches", "s", &match,
"actions", "s", &actions,
NULL)) {
wp_warning ("malformated JSON: either JSON object is not found or "
"an empty object with out matches or actions found, skipping it");
continue;
}
/* TBD: handle empty matches and actions */
m = parse_matches (match);
g_ptr_array_add (r->matches, m);
wp_info ("loaded (%d) interest objects for this match", m->interests->len);
wp_debug (". loaded (%d) interest objects for this match for rule(%s)",
m->interests->len, r->rule);
m->actions = parse_actions (actions);
wp_info ("loaded (%d) actions for this match",
wp_properties_get_count (m->actions));
wp_debug (". loaded (%d) actions for this match for rule(%s)",
wp_properties_get_count (m->actions), r->rule);
}
return r;
@ -344,7 +398,7 @@ parse_setting (const gchar *setting, const gchar *value, WpSettings *self)
else {
Rule *r = parse_rule (setting, value);
g_ptr_array_add (self->rules, r);
wp_info_object (self, "loaded (%d) matches for rule (%s)",
wp_debug_object (self, "loaded (%d) matches for rule (%s)",
r->matches->len, r->rule);
}
}
@ -372,7 +426,7 @@ on_metadata_added (WpObjectManager *om, WpMetadata *m, gpointer d)
wp_object_update_features (WP_OBJECT (self), WP_SETTINGS_LOADED, 0);
}
void
static void
rule_unref (Rule * self)
{
g_free (self->rule);
@ -405,7 +459,7 @@ wp_settings_activate_execute_step (WpObject * object,
G_CALLBACK (on_metadata_added), transition, 0);
wp_core_install_object_manager (core, self->metadata_om);
wp_debug_object (self, "looking for metadata object named %s",
wp_info_object (self, "looking for metadata object named %s",
self->metadata_name);
break;
}

View file

@ -32,8 +32,8 @@ WP_API
G_DECLARE_FINAL_TYPE (WpSettings, wp_settings, WP, SETTINGS, WpObject)
WP_API
WpSettings *
wp_settings_get_instance (WpCore * core, const gchar *metadata_name);
WpSettings * wp_settings_get_instance (WpCore * core,
const gchar *metadata_name);
WP_API
gboolean wp_settings_get_boolean (WpSettings *self, const gchar *setting);

View file

@ -1481,7 +1481,7 @@ get_boolean (lua_State *L)
if (lua_type (L, 2) == LUA_TSTRING)
metadata_name = luaL_checkstring (L, 2);
WpSettings *s = wp_settings_get_instance (get_wp_core (L), metadata_name);
g_autoptr (WpSettings) s = wp_settings_get_instance (get_wp_core (L), metadata_name);
if (s)
{
@ -1493,8 +1493,34 @@ get_boolean (lua_State *L)
return 1;
}
static gboolean
apply_rule (lua_State *L)
{
const char *r = luaL_checkstring (L, 1);
const char *metadata_name = NULL;
g_autoptr (WpProperties) cp = wplua_table_to_properties (L, 2);
g_autoptr (WpProperties) ap = wp_properties_new_empty ();
if (lua_type (L, -1) == LUA_TSTRING)
metadata_name = luaL_checkstring (L, -1);
g_autoptr (WpSettings) s = wp_settings_get_instance (get_wp_core (L), metadata_name);
if (s)
{
gboolean value = wp_settings_apply_rule (s, r, cp, ap);
lua_pushboolean (L, value);
wplua_properties_to_table (L, ap);
}
else
lua_pushnil (L);
return 2;
}
static const luaL_Reg settings_methods[] = {
{ "get_boolean", get_boolean },
{ "apply_rule", apply_rule },
{ NULL, NULL }
};

View file

@ -173,7 +173,7 @@ wireplumber.settings = {
}
]
actions = {
quirks = {
update-props = {
default_permissions = "rx",
}
}
@ -203,8 +203,8 @@ wireplumber.settings = {
{ device.name = "*" }
]
actions = {
quirks = {
profile_names = [off pro-audio]
update-props = {
profile_names = "off pro-audio"
}
}
}
@ -254,7 +254,7 @@ wireplumber.settings = {
matches = [
{
# This matches all cards.
{ device.name = "alsa_card.*" }
device.name = "~alsa_card.*"
}
]
actions = {

View file

@ -8,8 +8,8 @@
#include "../common/base-test-fixture.h"
/*
* tests the loading & parsing of JSON conf file(pls check the settings.conf),
* metadata updates, wpsetttings object creation and its API.
* tests the loading & parsing of JSON conf file(pls check the .conf that is
* loaded), metadata updates, wpsetttings object creation and its API.
*/
typedef struct {
WpBaseTestFixture base;
@ -112,7 +112,7 @@ test_parsing_setup (TestSettingsFixture *self, gconstpointer user_data)
self->settings = g_steal_pointer (&settings);
/* total no.of properties in the conf file */
g_assert_cmpint (data.count, ==, 4);
g_assert_cmpint (data.count, ==, 5);
}
}
@ -129,7 +129,7 @@ static void
test_parsing (TestSettingsFixture *self, gconstpointer data)
{
/* total no.of properties in the conf file */
g_assert_cmpint (wp_properties_get_count(self->settings), ==, 4);
g_assert_cmpint (wp_properties_get_count(self->settings), ==, 5);
}
static void
@ -278,20 +278,188 @@ test_wpsettings (TestSettingsFixture *self, gconstpointer data)
}
}
static void
test_rules (TestSettingsFixture *self, gconstpointer data)
{
WpSettings *s = self->s;
{
g_autoptr (WpProperties) props = wp_properties_new (
"test-string2", "juggler",
NULL);
g_assert_true (wp_settings_apply_rule (s, "rule_one", props, NULL));
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test-string2", "jugglr",
NULL);
g_assert_false (wp_settings_apply_rule (s, "rule_one", props, NULL));
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test-string1", "copper",
"test-int1", "100",
NULL);
g_assert_true (wp_settings_apply_rule (s, "rule_one", props, NULL));
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test-string1", "copper",
NULL);
g_assert_false (wp_settings_apply_rule (s, "rule_one", props, NULL));
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test-string2", "juggler",
NULL);
gint before = wp_properties_get_count (props), after = 0;
guint prop_int1 = 0;
g_assert_true (wp_settings_apply_rule (s, "rule_one", props, NULL));
after = wp_properties_get_count (props);
g_assert_cmpint (after-before, ==, 2);
g_assert_cmpstr (wp_properties_get(props, "prop-string1"), ==, "metal");
g_assert_cmpstr (wp_properties_get(props, "prop-string2"), ==, NULL);
g_assert_cmpstr (wp_properties_get(props, "prop-int1"), ==, "123");
spa_atou32 (wp_properties_get(props, "prop-int1"), &prop_int1, 0);
g_assert_cmpint (prop_int1, ==, 123);
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test-string4", "ferrous",
"test-int2", "100",
"test-string5", "blend",
NULL);
gint before = wp_properties_get_count (props), after = 0;
guint prop_int2 = 0;
g_assert_true (wp_settings_apply_rule (s, "rule_one", props, NULL));
after = wp_properties_get_count (props);
g_assert_cmpint (after-before, ==, 3);
g_assert_cmpstr (wp_properties_get(props, "prop-string1"), ==, NULL);
g_assert_cmpstr (wp_properties_get(props, "prop-string2"), ==, "standard");
g_assert_cmpstr (wp_properties_get(props, "prop-int2"), ==, "26");
spa_atou32 (wp_properties_get(props, "prop-int2"), &prop_int2, 0);
g_assert_cmpint (prop_int2, ==, 26);
g_assert_true (spa_atob(wp_properties_get(props, "prop-bool1")));
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test-string6", "ethylene",
"test-int2", "625",
"test-string5", "blend",
NULL);
g_autoptr (WpProperties) applied_props = wp_properties_new_empty ();
guint prop_int2 = 0;
g_assert_false (wp_settings_apply_rule (s, "rule_one", props, NULL));
g_assert_true (wp_settings_apply_rule (s, "rule_two", props,
applied_props));
g_assert_cmpint (wp_properties_get_count (applied_props), ==, 2);
g_assert_cmpstr (wp_properties_get(applied_props, "prop-string1"),
==, NULL);
g_assert_cmpstr (wp_properties_get(applied_props, "prop-string2"),
==, "spray");
g_assert_cmpstr (wp_properties_get(applied_props, "prop-int2"), ==, "111");
spa_atou32 (wp_properties_get(applied_props, "prop-int2"), &prop_int2, 0);
g_assert_cmpint (prop_int2, ==, 111);
g_assert_false (spa_atob(wp_properties_get(applied_props, "prop-bool1")));
}
/* test the regular experession syntax */
{
g_autoptr (WpProperties) props = wp_properties_new (
"test.string6", "metal.iron",
"test.table.entry", "true",
NULL);
g_autoptr (WpProperties) applied_props = wp_properties_new_empty ();
g_assert_false (wp_settings_apply_rule (s, "rule_one", props, NULL));
g_assert_false (wp_settings_apply_rule (s, "rule_two", props,
applied_props));
g_assert_true (wp_settings_apply_rule (s, "rule_three", props,
applied_props));
g_assert_cmpint (wp_properties_get_count (applied_props), ==, 3);
g_assert_cmpstr (wp_properties_get(applied_props, "prop.string1"),
==, NULL);
g_assert_cmpstr (wp_properties_get(applied_props, "prop.state"),
==, "solid");
g_assert_cmpstr (wp_properties_get(applied_props, "prop.example"),
==, "ferrous");
g_assert_true (spa_atob(wp_properties_get(applied_props,
"prop.electrical.conductivity")));
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test.string6", "metal.iron",
"test.table.entry", "false",
NULL);
g_assert_false (wp_settings_apply_rule (s, "rule_three", props,
NULL));
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test.string6", "metl.iron",
"test.table.entry", "true",
NULL);
g_assert_false (wp_settings_apply_rule (s, "rule_three", props,
NULL));
}
{
g_autoptr (WpProperties) props = wp_properties_new (
"test.string6", "gas.neon",
"test.table.entry", "maybe",
NULL);
g_autoptr (WpProperties) applied_props = wp_properties_new_empty ();
g_assert_false (wp_settings_apply_rule (s, "rule_one", props, NULL));
g_assert_false (wp_settings_apply_rule (s, "rule_two", props,
applied_props));
g_assert_true (wp_settings_apply_rule (s, "rule_three", props,
applied_props));
g_assert_cmpint (wp_properties_get_count (applied_props), ==, 3);
g_assert_cmpstr (wp_properties_get(applied_props, "prop.string1"),
==, NULL);
g_assert_cmpstr (wp_properties_get(applied_props, "prop.state"),
==, "gas");
g_assert_cmpstr (wp_properties_get(applied_props, "prop.example"),
==, "neon");
g_assert_false (spa_atob(wp_properties_get(applied_props,
"prop.electrical.conductivity")));
}
}
gint
main (gint argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
wp_init (WP_INIT_ALL);
/* take a close look at .conf file that is loaded, all the test work on it */
g_test_add ("/wp/settings/conf-file-loading", TestSettingsFixture, NULL,
test_conf_file_setup, test_conf_file, test_conf_file_teardown);
g_test_add ("/wp/settings/parsing", TestSettingsFixture, NULL,
test_parsing_setup, test_parsing, test_parsing_teardown);
g_test_add ("/wp/settings/metadata-creation", TestSettingsFixture, NULL,
test_metadata_setup, test_metadata, test_metadata_teardown);
g_test_add ("/wp/settings/wpsettings-creation", TestSettingsFixture, NULL,
g_test_add ("/wp/settings/wpsettings-creation-get", TestSettingsFixture, NULL,
test_wpsettings_setup, test_wpsettings, test_wpsettings_teardown);
g_test_add ("/wp/settings/wpsettings-creation-rules", TestSettingsFixture, NULL,
test_wpsettings_setup, test_rules, test_wpsettings_teardown);
return g_test_run ();
}

View file

@ -88,25 +88,20 @@ wireplumber.settings = {
{
matches = [
{
pipewire.access = "flatpak"
test-string4 = "ferrous"
test-int2 = 100
test-string5 = "blend"
}
]
actions = {
update-props = {
default_permissions = "rx"
}
}
}
{
matches = [
{
pipewire.access = "restricted"
test-string6 = "alum"
test-int3 = 24
}
]
actions = {
update-props = {
default_permissions = "rx"
prop-string2 = "standard"
prop-int2 = 26
prop-bool1 = true
}
}
}
@ -115,46 +110,52 @@ wireplumber.settings = {
{
matches = [
{
test-string1 = "copper"
test-int1 = 100
}
{
test-string2 = "juggler"
test-string6 = "ethylene"
test-int2 = 625
test-string5 = "blend"
}
]
actions = {
update-props = {
prop-string1 = "metal"
prop-int1 = 123
}
}
}
{
matches = [
{
pipewire.access = "flatpak"
}
]
actions = {
update-props = {
default_permissions = "rx"
}
}
}
{
matches = [
{
pipewire.access = "restricted"
}
]
actions = {
update-props = {
default_permissions = "rx"
prop-string2 = "spray"
prop-int2 = 111
}
}
}
]
rule_three = [
{
matches = [
{
test.string6 = "~metal*"
test.table.entry = true
}
]
actions = {
update-props = {
prop.electrical.conductivity = "true"
prop.state = "solid"
prop.example = "ferrous"
}
}
}
{
matches = [
{
test.string6 = "~gas*"
test.table.entry = "maybe"
}
]
actions = {
update-props = {
prop.electrical.conductivity = "false"
prop.example = "neon"
prop.state = "gas"
}
}
}
]
}

View file

@ -1,8 +1,109 @@
-- tests the lua API of WpSettings, this file tests the settings present in
-- settings.conf
-- .conf file that is loaded.
assert (Settings.get_boolean("test-property1", "test-settings") == false)
assert (Settings.get_boolean("test-property2", "test-settings") == true)
-- test settings
assert (Settings.get_boolean ("test-property1", "test-settings") == false)
assert (Settings.get_boolean ("test-property2", "test-settings") == true)
assert (Settings.get_boolean ("test-property1") == false)
-- test rules
-- test #1
local cp = {
["test-string2"]="juggler"
}
local ap = {}
local applied, ap = Settings.apply_rule( "rule_one", cp, "test-settings")
assert (applied == true)
assert (ap["prop-string1"] == "metal")
assert (ap["prop-int1"] == "123")
assert (ap["blah blah"] == nil)
-- test #2
local cp = {
["test-string2"]="jugler"
}
local ap = {}
local applied, ap = Settings.apply_rule ("rule_one", cp, "test-settings")
assert (applied == false)
-- test #3
local cp = {
["test-string4"] = "ferrous",
["test-int2"] = "100",
["test-string5"] = "blend"
}
local applied, ap = Settings.apply_rule ("rule_one", cp, "test-settings")
assert (applied == true)
assert (ap["prop-string1"] == nil)
assert (ap["prop-string2"] == "standard")
assert (ap["prop-int2"] == "26")
assert (ap["prop-bool1"] == "true")
-- test #4
local cp = {
["test-string6"] = "alum",
}
local applied, ap = Settings.apply_rule ("rule_one", cp, "test-settings")
assert (applied == false)
-- test #5
local cp = {
["test-string6"] = "alum",
["test-int3"] = "24",
}
local applied, ap = Settings.apply_rule ("rule_one", cp, "test-settings")
assert (applied == true)
assert (ap["prop-string1"] == nil)
assert (ap["prop-string2"] == "standard")
assert (ap["prop-int2"] == "26")
assert (ap["prop-bool1"] == "true")
-- test #6
-- test regular expression syntax
local cp = {
["test.string6"] = "metal.ferrous",
["test.table.entry"] = "yes",
}
local applied, ap = Settings.apply_rule ("rule_three", cp, "test-settings")
assert (applied == false)
-- test #7
local cp = {
["test.string6"] = "metal.ferrous",
["test.table.entry"] = "true",
}
local applied, ap = Settings.apply_rule ("rule_three", cp, "test-settings")
assert (applied == true)
assert (ap["prop.electrical.conductivity"] == "true")
assert (ap["prop.state"] == "solid")
assert (ap["prop.example"] == "ferrous")
-- test #8
local cp = {
["test.string6"] = "gas.xenon",
["test.table.entry"] = "maybe",
}
local applied, ap = Settings.apply_rule ("rule_three", cp, "test-settings")
assert (applied == true)
assert (ap["prop.electrical.conductivity"] == "false")
assert (ap["prop.state"] == "gas")
assert (ap["prop.example"] == "neon")
assert (Settings.get_boolean("test-property1") == false)