From 54918e32e49c26c3f6289a1743e6eb2b02eb215e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 22 Feb 2011 23:36:43 -0600 Subject: libnm-util: add nm_connection_diff() Returns a list of keys that differ between the settings in each connection. nm_connection_compare() can't do that. --- libnm-util/libnm-util.ver | 2 + libnm-util/nm-connection.c | 84 ++++++++++++++ libnm-util/nm-connection.h | 5 + libnm-util/nm-setting.c | 150 ++++++++++++++++++++++--- libnm-util/nm-setting.h | 20 ++++ libnm-util/tests/test-general.c | 242 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 490 insertions(+), 13 deletions(-) (limited to 'libnm-util') diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index 22bd5a140c..c24d7d4180 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -4,6 +4,7 @@ global: nm_connection_clear_secrets; nm_connection_compare; nm_connection_create_setting; + nm_connection_diff; nm_connection_dump; nm_connection_duplicate; nm_connection_error_get_type; @@ -123,6 +124,7 @@ global: nm_setting_connection_get_permission; nm_setting_connection_permissions_user_allowed; nm_setting_connection_remove_permission; + nm_setting_diff; nm_setting_duplicate; nm_setting_enumerate_values; nm_setting_error_get_type; diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c index aef1cc0b15..6e7e10a149 100644 --- a/libnm-util/nm-connection.c +++ b/libnm-util/nm-connection.c @@ -583,6 +583,90 @@ nm_connection_compare (NMConnection *a, return info.failed ? FALSE : TRUE; } + +static void +diff_one_connection (NMConnection *a, + NMConnection *b, + NMSettingCompareFlags flags, + gboolean invert_results, + GHashTable *diffs) +{ + NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (a); + GHashTableIter iter; + NMSetting *a_setting = NULL; + + g_hash_table_iter_init (&iter, priv->settings); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &a_setting)) { + NMSetting *b_setting = NULL; + const char *setting_name = nm_setting_get_name (a_setting); + GHashTable *results; + gboolean new_results = TRUE; + + if (b) + b_setting = nm_connection_get_setting (b, G_OBJECT_TYPE (a_setting)); + + results = g_hash_table_lookup (diffs, setting_name); + if (results) + new_results = FALSE; + + if (!nm_setting_diff (a_setting, b_setting, flags, invert_results, &results)) { + if (new_results) + g_hash_table_insert (diffs, g_strdup (setting_name), results); + } + } +} + +/** + * nm_connection_diff: + * @a: a #NMConnection + * @b: a second #NMConnection to compare with the first + * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT + * @out_settings: (element-type utf8 GHashTable): if the + * connections differ, on return a hash table mapping setting names to + * second-level GHashTable, which contains key names that differ + * + * Compares two #NMConnection objects for similarity, with comparison behavior + * modified by a set of flags. See nm_setting_compare() for a description of + * each flag's behavior. If the connections differ, settings and keys within + * each setting that differ are added to the returned @out_settings hash table. + * No values are returned, only key names. + * + * Returns: %TRUE if the connections contain the same values, %FALSE if they do + * not + **/ +gboolean +nm_connection_diff (NMConnection *a, + NMConnection *b, + NMSettingCompareFlags flags, + GHashTable **out_settings) +{ + GHashTable *diffs; + + g_return_val_if_fail (a != NULL, FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (a), FALSE); + g_return_val_if_fail (out_settings != NULL, FALSE); + g_return_val_if_fail (*out_settings == NULL, FALSE); + if (b) + g_return_val_if_fail (NM_IS_CONNECTION (b), FALSE); + + if (a == b) + return TRUE; + + diffs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy); + + /* Diff A to B, then B to A to capture keys in B that aren't in A */ + diff_one_connection (a, b, flags, FALSE, diffs); + if (b) + diff_one_connection (b, a, flags, TRUE, diffs); + + if (g_hash_table_size (diffs) == 0) + g_hash_table_destroy (diffs); + else + *out_settings = diffs; + + return *out_settings ? FALSE : TRUE; +} + /** * nm_connection_verify: * @connection: the #NMConnection to verify diff --git a/libnm-util/nm-connection.h b/libnm-util/nm-connection.h index fce715ab18..3c356a758b 100644 --- a/libnm-util/nm-connection.h +++ b/libnm-util/nm-connection.h @@ -111,6 +111,11 @@ gboolean nm_connection_compare (NMConnection *a, NMConnection *b, NMSettingCompareFlags flags); +gboolean nm_connection_diff (NMConnection *a, + NMConnection *b, + NMSettingCompareFlags flags, + GHashTable **out_settings); + gboolean nm_connection_verify (NMConnection *connection, GError **error); const char * nm_connection_need_secrets (NMConnection *connection, diff --git a/libnm-util/nm-setting.c b/libnm-util/nm-setting.c index b5a8b71f64..f134754b18 100644 --- a/libnm-util/nm-setting.c +++ b/libnm-util/nm-setting.c @@ -332,6 +332,29 @@ nm_setting_verify (NMSetting *setting, GSList *all_settings, GError **error) return TRUE; } +static inline gboolean +should_compare_prop (NMSetting *setting, + const char *prop_name, + NMSettingCompareFlags comp_flags, + GParamFlags prop_flags) +{ + /* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */ + if ( (comp_flags & NM_SETTING_COMPARE_FLAG_FUZZY) + && (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))) + return FALSE; + + if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS) + && (prop_flags & NM_SETTING_PARAM_SECRET)) + return FALSE; + + if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID) + && NM_IS_SETTING_CONNECTION (setting) + && !strcmp (prop_name, NM_SETTING_CONNECTION_ID)) + return FALSE; + + return TRUE; +} + /** * nm_setting_compare: * @a: a #NMSetting @@ -370,19 +393,8 @@ nm_setting_compare (NMSetting *a, GValue value1 = { 0 }; GValue value2 = { 0 }; - /* Fuzzy compare ignores secrets and properties defined with the - * FUZZY_IGNORE flag - */ - if ( (flags & NM_SETTING_COMPARE_FLAG_FUZZY) - && (prop_spec->flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))) - continue; - - if ((flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS) && (prop_spec->flags & NM_SETTING_PARAM_SECRET)) - continue; - - if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID) - && !strcmp (nm_setting_get_name (a), NM_SETTING_CONNECTION_SETTING_NAME) - && !strcmp (prop_spec->name, NM_SETTING_CONNECTION_ID)) + /* Handle compare flags */ + if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags)) continue; g_value_init (&value1, prop_spec->value_type); @@ -402,6 +414,118 @@ nm_setting_compare (NMSetting *a, return different == 0 ? TRUE : FALSE; } +/** + * nm_setting_diff: + * @a: a #NMSetting + * @b: a second #NMSetting to compare with the first + * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT + * @invert_results: this parameter is used internally by libnm-util and should + * be set to %FALSE. If %TRUE inverts the meaning of the #NMSettingDiffResult. + * @results: (element-type utf8 guint32): if the settings differ, on return a + * hash table mapping the differing keys to one or more #NMSettingDiffResult + * values OR-ed together. If the settings do not differ, any hash table passed + * in is unmodified. If no hash table is passed in, a new one is created. + * + * Compares two #NMSetting objects for similarity, with comparison behavior + * modified by a set of flags. See the documentation for #NMSettingCompareFlags + * for a description of each flag's behavior. If the settings differ, the keys + * of each setting that differ from the other are added to @results, mapped to + * one or more #NMSettingDiffResult values. + * + * Returns: %TRUE if the settings contain the same values, %FALSE if they do not + **/ +gboolean +nm_setting_diff (NMSetting *a, + NMSetting *b, + NMSettingCompareFlags flags, + gboolean invert_results, + GHashTable **results) +{ + GParamSpec **property_specs; + guint n_property_specs; + guint i; + NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A; + NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B; + gboolean results_created = FALSE; + + g_return_val_if_fail (results != NULL, FALSE); + g_return_val_if_fail (a != NULL, FALSE); + g_return_val_if_fail (NM_IS_SETTING (a), FALSE); + if (b) { + g_return_val_if_fail (NM_IS_SETTING (b), FALSE); + g_return_val_if_fail (G_OBJECT_TYPE (a) == G_OBJECT_TYPE (b), FALSE); + } + + /* If the caller is calling this function in a pattern like this to get + * complete diffs: + * + * nm_setting_diff (A, B, FALSE, &results); + * nm_setting_diff (B, A, TRUE, &results); + * + * and wants us to invert the results so that the second invocation comes + * out correctly, do that here. + */ + if (invert_results) { + a_result = NM_SETTING_DIFF_RESULT_IN_B; + b_result = NM_SETTING_DIFF_RESULT_IN_A; + } + + if (*results == NULL) { + *results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + results_created = TRUE; + } + + /* And now all properties */ + property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs); + + for (i = 0; i < n_property_specs; i++) { + GParamSpec *prop_spec = property_specs[i]; + GValue a_value = { 0 }, b_value = { 0 }; + NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN, tmp; + gboolean different = TRUE; + + /* Handle compare flags */ + if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags)) + continue; + if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0) + continue; + + if (b) { + g_value_init (&a_value, prop_spec->value_type); + g_object_get_property (G_OBJECT (a), prop_spec->name, &a_value); + + g_value_init (&b_value, prop_spec->value_type); + g_object_get_property (G_OBJECT (b), prop_spec->name, &b_value); + + different = !!g_param_values_cmp (prop_spec, &a_value, &b_value); + if (different) { + if (!g_param_value_defaults (prop_spec, &a_value)) + r |= a_result; + if (!g_param_value_defaults (prop_spec, &b_value)) + r |= b_result; + } + + g_value_unset (&a_value); + g_value_unset (&b_value); + } else + r = a_result; /* only in A */ + + if (different) { + tmp = GPOINTER_TO_UINT (g_hash_table_lookup (*results, prop_spec->name)); + g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (tmp | r)); + } + } + g_free (property_specs); + + /* Don't return an empty hash table */ + if (results_created && !g_hash_table_size (*results)) { + g_hash_table_destroy (*results); + *results = NULL; + } + + return !(*results); +} + /** * nm_setting_enumerate_values: * @setting: the #NMSetting diff --git a/libnm-util/nm-setting.h b/libnm-util/nm-setting.h index 067db14cbc..ef3011adbd 100644 --- a/libnm-util/nm-setting.h +++ b/libnm-util/nm-setting.h @@ -218,6 +218,26 @@ gboolean nm_setting_compare (NMSetting *a, NMSetting *b, NMSettingCompareFlags flags); +/** + * NMSettingDiffResult: + * @NM_SETTING_DIFF_RESULT_UNKNOWN: unknown result + * @NM_SETTING_DIFF_RESULT_IN_A: the property is present in setting A + * @NM_SETTING_DIFF_RESULT_IN_B: the property is present in setting B + * + * These values indicate the result of a setting difference operation. + **/ +typedef enum { + NM_SETTING_DIFF_RESULT_UNKNOWN = 0x00000000, + NM_SETTING_DIFF_RESULT_IN_A = 0x00000001, + NM_SETTING_DIFF_RESULT_IN_B = 0x00000002, +} NMSettingDiffResult; + +gboolean nm_setting_diff (NMSetting *a, + NMSetting *b, + NMSettingCompareFlags flags, + gboolean invert_results, + GHashTable **results); + void nm_setting_enumerate_values (NMSetting *setting, NMSettingValueIterFn func, gpointer user_data); diff --git a/libnm-util/tests/test-general.c b/libnm-util/tests/test-general.c index a2c7b2dcbf..653dd26800 100644 --- a/libnm-util/tests/test-general.c +++ b/libnm-util/tests/test-general.c @@ -29,8 +29,11 @@ #include "nm-setting-connection.h" #include "nm-setting-vpn.h" #include "nm-setting-gsm.h" +#include "nm-setting-wired.h" #include "nm-setting-wireless-security.h" #include "nm-setting-ip6-config.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-pppoe.h" #include "nm-dbus-glib-types.h" static void @@ -662,6 +665,241 @@ test_setting_connection_permissions_property (void) g_object_unref (s_con); } +static NMConnection * +new_test_connection (void) +{ + NMConnection *connection; + NMSetting *setting; + char *uuid; + gulong timestamp = time (NULL); + + connection = nm_connection_new (); + + setting = nm_setting_connection_new (); + uuid = nm_utils_uuid_generate (); + g_object_set (G_OBJECT (setting), + NM_SETTING_CONNECTION_ID, "foobar", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_TIMESTAMP, timestamp, + NULL); + g_free (uuid); + nm_connection_add_setting (connection, setting); + + setting = nm_setting_wired_new (); + g_object_set (G_OBJECT (setting), + NM_SETTING_WIRED_MTU, 1592, + NULL); + nm_connection_add_setting (connection, setting); + + setting = nm_setting_ip4_config_new (); + g_object_set (G_OBJECT (setting), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, "eyeofthetiger", + NULL); + nm_connection_add_setting (connection, setting); + + return connection; +} + +typedef struct { + const char *key_name; + guint32 result; +} DiffKey; + +typedef struct { + const char *name; + DiffKey keys[30]; +} DiffSetting; + +#define ARRAY_LEN(a) (sizeof (a) / sizeof (a[0])) + +static void +ensure_diffs (GHashTable *diffs, const DiffSetting *check, gsize n_check) +{ + guint i; + + g_assert (g_hash_table_size (diffs) == n_check); + + /* Loop through the settings */ + for (i = 0; i < n_check; i++) { + GHashTable *setting_hash; + guint z = 0; + + setting_hash = g_hash_table_lookup (diffs, check[i].name); + g_assert (setting_hash); + + /* Get the number of keys to check */ + while (check[i].keys[z].key_name) + z++; + g_assert (g_hash_table_size (setting_hash) == z); + + /* Now compare the actual keys */ + for (z = 0; check[i].keys[z].key_name; z++) { + NMSettingDiffResult result; + + result = GPOINTER_TO_UINT (g_hash_table_lookup (setting_hash, check[i].keys[z].key_name)); + g_assert (result == check[i].keys[z].result); + } + } +} + +static void +test_connection_diff_a_only (void) +{ + NMConnection *connection; + GHashTable *out_diffs = NULL; + gboolean same; + const DiffSetting settings[] = { + { NM_SETTING_CONNECTION_SETTING_NAME, { + { NM_SETTING_CONNECTION_ID, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_UUID, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_TYPE, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_TIMESTAMP, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_AUTOCONNECT, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_READ_ONLY, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_PERMISSIONS, NM_SETTING_DIFF_RESULT_IN_A }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN } + } }, + { NM_SETTING_WIRED_SETTING_NAME, { + { NM_SETTING_WIRED_PORT, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_SPEED, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_DUPLEX, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_AUTO_NEGOTIATE, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_CLONED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_MTU, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_S390_SUBCHANNELS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_S390_NETTYPE, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_S390_OPTIONS, NM_SETTING_DIFF_RESULT_IN_A }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + { NM_SETTING_IP4_CONFIG_SETTING_NAME, { + { NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DNS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DNS_SEARCH, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_ADDRESSES, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_ROUTES, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_IP4_CONFIG_MAY_FAIL, NM_SETTING_DIFF_RESULT_IN_A }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + }; + + connection = new_test_connection (); + + same = nm_connection_diff (connection, NULL, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs); + g_assert (same == FALSE); + g_assert (out_diffs != NULL); + g_assert (g_hash_table_size (out_diffs) > 0); + + ensure_diffs (out_diffs, settings, ARRAY_LEN (settings)); + + g_object_unref (connection); +} + +static void +test_connection_diff_same (void) +{ + NMConnection *a, *b; + GHashTable *out_diffs = NULL; + gboolean same; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs); + g_assert (same == TRUE); + g_assert (out_diffs == NULL); + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_diff_different (void) +{ + NMConnection *a, *b; + GHashTable *out_diffs = NULL; + NMSetting *s_ip4; + gboolean same; + const DiffSetting settings[] = { + { NM_SETTING_IP4_CONFIG_SETTING_NAME, { + { NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + }; + + a = new_test_connection (); + b = nm_connection_duplicate (a); + s_ip4 = nm_connection_get_setting (a, NM_TYPE_SETTING_IP4_CONFIG); + g_assert (s_ip4); + g_object_set (G_OBJECT (s_ip4), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs); + g_assert (same == FALSE); + g_assert (out_diffs != NULL); + g_assert (g_hash_table_size (out_diffs) > 0); + + ensure_diffs (out_diffs, settings, ARRAY_LEN (settings)); + + g_object_unref (a); + g_object_unref (b); +} + +static void +test_connection_diff_no_secrets (void) +{ + NMConnection *a, *b; + GHashTable *out_diffs = NULL; + NMSetting *s_pppoe; + gboolean same; + const DiffSetting settings[] = { + { NM_SETTING_PPPOE_SETTING_NAME, { + { NM_SETTING_PPPOE_PASSWORD, NM_SETTING_DIFF_RESULT_IN_B }, + { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }, + } }, + }; + + a = new_test_connection (); + s_pppoe = nm_setting_pppoe_new (); + g_object_set (G_OBJECT (s_pppoe), + NM_SETTING_PPPOE_USERNAME, "thomas", + NULL); + nm_connection_add_setting (a, s_pppoe); + + b = nm_connection_duplicate (a); + + /* Add a secret to B */ + s_pppoe = nm_connection_get_setting (b, NM_TYPE_SETTING_PPPOE); + g_assert (s_pppoe); + g_object_set (G_OBJECT (s_pppoe), + NM_SETTING_PPPOE_PASSWORD, "secretpassword", + NULL); + + /* Make sure the diff returns no results as secrets are ignored */ + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, &out_diffs); + g_assert (same == TRUE); + g_assert (out_diffs == NULL); + + /* Now make sure the diff returns results if secrets are not ignored */ + same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs); + g_assert (same == FALSE); + g_assert (out_diffs != NULL); + g_assert (g_hash_table_size (out_diffs) > 0); + + ensure_diffs (out_diffs, settings, ARRAY_LEN (settings)); + + g_object_unref (a); + g_object_unref (b); +} + int main (int argc, char **argv) { GError *error = NULL; @@ -686,6 +924,10 @@ int main (int argc, char **argv) test_connection_to_hash_setting_name (); test_setting_connection_permissions_helpers (); test_setting_connection_permissions_property (); + test_connection_diff_a_only (); + test_connection_diff_same (); + test_connection_diff_different (); + test_connection_diff_no_secrets (); base = g_path_get_basename (argv[0]); fprintf (stdout, "%s: SUCCESS\n", base); -- cgit v1.2.1