libnm/team: merge branch 'th/libnm-team-fixes'

https://github.com/NetworkManager/NetworkManager/pull/318
This commit is contained in:
Thomas Haller 2019-03-25 08:09:29 +01:00
commit c7864fc1b2
9 changed files with 386 additions and 194 deletions

View file

@ -284,7 +284,6 @@ char ** _nm_utils_slist_to_strv (GSList *slist, gboolean deep_copy);
GPtrArray * _nm_utils_strv_to_ptrarray (char **strv);
char ** _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray);
gboolean _nm_utils_strv_equal (char **strv1, char **strv2);
gboolean _nm_utils_check_file (const char *filename,
gint64 check_owner,
@ -538,6 +537,8 @@ gboolean _nm_utils_inet6_is_token (const struct in6_addr *in6addr);
/*****************************************************************************/
gboolean _nm_team_link_watchers_equal (GPtrArray *a, GPtrArray *b, gboolean ignore_order);
gboolean _nm_utils_team_config_equal (const char *conf1, const char *conf2, gboolean port);
GValue *_nm_utils_team_config_get (const char *conf,
const char *key,

View file

@ -396,31 +396,18 @@ compare_property (const NMSettInfoSetting *sett_info,
{
NMSettingTeamPortPrivate *a_priv;
NMSettingTeamPortPrivate *b_priv;
guint i, j;
if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_PORT_LINK_WATCHERS)) {
if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
return NM_TERNARY_DEFAULT;
if (other) {
a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting);
b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (other);
if (a_priv->link_watchers->len != b_priv->link_watchers->len)
return FALSE;
for (i = 0; i < a_priv->link_watchers->len; i++) {
for (j = 0; j < b_priv->link_watchers->len; j++) {
if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i],
b_priv->link_watchers->pdata[j])) {
break;
}
}
if (j == b_priv->link_watchers->len)
return FALSE;
}
}
return TRUE;
if (!other)
return TRUE;
a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting);
b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (other);
return _nm_team_link_watchers_equal (a_priv->link_watchers,
b_priv->link_watchers,
TRUE);
}
if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_PORT_CONFIG)) {

View file

@ -396,6 +396,34 @@ nm_team_link_watcher_equal (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other
return TRUE;
}
gboolean
_nm_team_link_watchers_equal (GPtrArray *a, GPtrArray *b, gboolean ignore_order)
{
guint i, j;
if (a->len != b->len)
return FALSE;
if (ignore_order) {
/* FIXME: comparing this way is O(n^2). Don't do that, instead
* add nm_team_link_watcher_cmp(), sort both lists, and
* compare step by step. */
for (i = 0; i < a->len; i++) {
for (j = 0; j < b->len; j++) {
if (nm_team_link_watcher_equal (a->pdata[i], b->pdata[j]))
break;
}
if (j == b->len)
return FALSE;
}
} else {
for (i = 0; i < a->len; i++) {
if (!nm_team_link_watcher_equal (a->pdata[i], b->pdata[i]))
return FALSE;
}
}
return TRUE;
}
/**
* nm_team_link_watcher_dup:
* @watcher: the #NMTeamLinkWatcher
@ -929,13 +957,14 @@ nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting,
g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE);
g_return_val_if_fail (txhash != NULL, FALSE);
g_return_val_if_fail (txhash[0] != '\0', FALSE);
for (i = 0; i < priv->runner_tx_hash->len; i++) {
if (nm_streq (txhash, priv->runner_tx_hash->pdata[i])) {
g_ptr_array_remove_index (priv->runner_tx_hash, i);
_notify (setting, PROP_RUNNER_TX_HASH);
return TRUE;
if (priv->runner_tx_hash) {
for (i = 0; i < priv->runner_tx_hash->len; i++) {
if (nm_streq (txhash, priv->runner_tx_hash->pdata[i])) {
g_ptr_array_remove_index (priv->runner_tx_hash, i);
_notify (setting, PROP_RUNNER_TX_HASH);
return TRUE;
}
}
}
return FALSE;
@ -1239,9 +1268,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
if (!name) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING,
_("missing link watcher name"));
_("missing link watcher name"));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting),
NM_SETTING_TEAM_LINK_WATCHERS);
NM_SETTING_TEAM_LINK_WATCHERS);
return FALSE;
}
if (!NM_IN_STRSET (name,
@ -1249,9 +1278,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
NM_TEAM_LINK_WATCHER_ARP_PING,
NM_TEAM_LINK_WATCHER_NSNA_PING)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
_("unknown link watcher \"%s\""), name);
_("unknown link watcher \"%s\""), name);
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting),
NM_SETTING_TEAM_LINK_WATCHERS);
NM_SETTING_TEAM_LINK_WATCHERS);
return FALSE;
}
@ -1260,17 +1289,17 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
NM_TEAM_LINK_WATCHER_NSNA_PING)
&& !nm_team_link_watcher_get_target_host (link_watcher)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING,
_("missing target host"));
_("missing target host"));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting),
NM_SETTING_TEAM_LINK_WATCHERS);
NM_SETTING_TEAM_LINK_WATCHERS);
return FALSE;
}
if (nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING)
&& !nm_team_link_watcher_get_source_host (link_watcher)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING,
_("missing source address"));
_("missing source address"));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting),
NM_SETTING_TEAM_LINK_WATCHERS);
NM_SETTING_TEAM_LINK_WATCHERS);
return FALSE;
}
}
@ -1288,31 +1317,17 @@ compare_property (const NMSettInfoSetting *sett_info,
NMSettingCompareFlags flags)
{
NMSettingTeamPrivate *a_priv, *b_priv;
guint i, j;
if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_LINK_WATCHERS)) {
if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
return NM_TERNARY_DEFAULT;
if (other) {
a_priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
b_priv = NM_SETTING_TEAM_GET_PRIVATE (other);
if (a_priv->link_watchers->len != b_priv->link_watchers->len)
return FALSE;
for (i = 0; i < a_priv->link_watchers->len; i++) {
for (j = 0; j < b_priv->link_watchers->len; j++) {
if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i],
b_priv->link_watchers->pdata[j])) {
break;
}
}
if (j == b_priv->link_watchers->len)
return FALSE;
}
}
return TRUE;
if (!other)
return TRUE;
a_priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
b_priv = NM_SETTING_TEAM_GET_PRIVATE (other);
return _nm_team_link_watchers_equal (a_priv->link_watchers,
b_priv->link_watchers,
TRUE);
}
if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_CONFIG)) {
@ -1350,7 +1365,7 @@ _align_team_properties (NMSettingTeam *setting)
{
NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
char **strv;
int i;
gsize i;
priv->notify_peers_count = JSON_TO_VAL (int, PROP_NOTIFY_PEERS_COUNT);
priv->notify_peers_interval = JSON_TO_VAL (int, PROP_NOTIFY_PEERS_INTERVAL);
@ -1372,16 +1387,24 @@ _align_team_properties (NMSettingTeam *setting)
priv->runner_tx_balancer = JSON_TO_VAL (string, PROP_RUNNER_TX_BALANCER);
priv->runner_agg_select_policy = JSON_TO_VAL (string, PROP_RUNNER_AGG_SELECT_POLICY);
if (priv->runner_tx_hash) {
g_ptr_array_unref (priv->runner_tx_hash);
priv->runner_tx_hash = NULL;
}
strv = JSON_TO_VAL (strv, PROP_RUNNER_TX_HASH);
if (strv) {
for (i = 0; strv[i]; i++)
nm_setting_team_add_runner_tx_hash (setting, strv[i]);
g_strfreev (strv);
if (_nm_utils_strv_cmp_n (( priv->runner_tx_hash
? (const char *const*) priv->runner_tx_hash->pdata
: NULL),
( priv->runner_tx_hash
? (gssize) priv->runner_tx_hash->len
: (gssize) -1),
NM_CAST_STRV_CC (strv),
-1) != 0) {
nm_clear_pointer (&priv->runner_tx_hash, g_ptr_array_unref);
if (strv) {
priv->runner_tx_hash = g_ptr_array_new_full (NM_PTRARRAY_LEN (strv), g_free);
for (i = 0; strv[i]; i++)
g_ptr_array_add (priv->runner_tx_hash, strv[i]);
nm_clear_g_free (&strv);
}
}
nm_clear_pointer (&strv, g_strfreev);
g_ptr_array_unref (priv->link_watchers);
priv->link_watchers = JSON_TO_VAL (ptr_array, PROP_LINK_WATCHERS);
@ -1419,8 +1442,10 @@ get_property (GObject *object, guint prop_id,
g_value_set_string (value, nm_setting_team_get_runner_hwaddr_policy (setting));
break;
case PROP_RUNNER_TX_HASH:
g_value_take_boxed (value, priv->runner_tx_hash ?
_nm_utils_ptrarray_to_strv (priv->runner_tx_hash): NULL);
g_value_take_boxed (value,
priv->runner_tx_hash
? _nm_utils_ptrarray_to_strv (priv->runner_tx_hash)
: NULL);
break;
case PROP_RUNNER_TX_BALANCER:
g_value_set_string (value, nm_setting_team_get_runner_tx_balancer (setting));
@ -1772,9 +1797,9 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
obj_properties[PROP_RUNNER_TX_HASH] =
g_param_spec_boxed (NM_SETTING_TEAM_RUNNER_TX_HASH, "", "",
G_TYPE_STRV,
G_PARAM_READWRITE |
G_PARAM_READWRITE |
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS);
G_PARAM_STATIC_STRINGS);
/**
* NMSettingTeam:runner-tx-balancer:

View file

@ -105,6 +105,16 @@ gboolean _nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **e
/* JSON to GValue conversion macros */
static inline void
_nm_auto_unset_and_free_gvalue (GValue **ptr)
{
if (*ptr) {
g_value_unset (*ptr);
g_free (*ptr);
}
}
#define nm_auto_unset_and_free_gvalue nm_auto(_nm_auto_unset_and_free_gvalue)
typedef struct {
const char *key1;
const char *key2;
@ -121,16 +131,13 @@ _nm_utils_json_extract_int (char *conf,
_NMUtilsTeamPropertyKeys key,
gboolean is_port)
{
gs_free GValue *t_value = NULL;
int ret;
nm_auto_unset_and_free_gvalue GValue *t_value = NULL;
t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port);
if (!t_value)
if ( !t_value
|| !G_VALUE_HOLDS_INT (t_value))
return key.default_int;
ret = g_value_get_int (t_value);
g_value_unset (t_value);
return ret;
return g_value_get_int (t_value);
}
static inline gboolean
@ -138,16 +145,13 @@ _nm_utils_json_extract_boolean (char *conf,
_NMUtilsTeamPropertyKeys key,
gboolean is_port)
{
gs_free GValue *t_value = NULL;
gboolean ret;
nm_auto_unset_and_free_gvalue GValue *t_value = NULL;
t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port);
if (!t_value)
if ( !t_value
|| !G_VALUE_HOLDS_BOOLEAN (t_value))
return key.default_bool;
ret = g_value_get_boolean (t_value);
g_value_unset (t_value);
return ret;
return g_value_get_boolean (t_value);
}
static inline char *
@ -155,16 +159,13 @@ _nm_utils_json_extract_string (char *conf,
_NMUtilsTeamPropertyKeys key,
gboolean is_port)
{
gs_free GValue *t_value = NULL;
char *ret;
nm_auto_unset_and_free_gvalue GValue *t_value = NULL;
t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port);
if (!t_value)
if ( !t_value
|| !G_VALUE_HOLDS_STRING (t_value))
return g_strdup (key.default_str);
ret = g_value_dup_string (t_value);
g_value_unset (t_value);
return ret;
return g_value_dup_string (t_value);
}
static inline char **
@ -172,39 +173,37 @@ _nm_utils_json_extract_strv (char *conf,
_NMUtilsTeamPropertyKeys key,
gboolean is_port)
{
gs_free GValue *t_value = NULL;
char **ret;
nm_auto_unset_and_free_gvalue GValue *t_value = NULL;
t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port);
if (!t_value)
if ( !t_value
|| !G_TYPE_CHECK_VALUE_TYPE (t_value, G_TYPE_STRV))
return NULL;
ret = g_strdupv (g_value_get_boxed (t_value));
g_value_unset (t_value);
return ret;
return g_strdupv (g_value_get_boxed (t_value))
?: g_new0 (char *, 1);
}
static inline GPtrArray *
_nm_utils_json_extract_ptr_array (char *conf,
_NMUtilsTeamPropertyKeys key,
gboolean is_port)
_NMUtilsTeamPropertyKeys key,
gboolean is_port)
{
gs_free GValue *t_value = NULL;
nm_auto_unset_and_free_gvalue GValue *t_value = NULL;
GPtrArray *data, *ret;
guint i;
ret = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port);
if (!t_value)
if ( !t_value
|| !G_TYPE_CHECK_VALUE_TYPE (t_value, G_TYPE_PTR_ARRAY))
return ret;
data = g_value_get_boxed (t_value);
if (!data)
return ret;
for (i = 0; i < data->len; i++)
g_ptr_array_add (ret, nm_team_link_watcher_dup (data->pdata[i]));
g_value_unset (t_value);
return ret;
}

View file

@ -1034,30 +1034,6 @@ _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray)
return strv;
}
/**
* _nm_utils_strv_equal:
* @strv1: a string array
* @strv2: a string array
*
* Compare NULL-terminated string arrays for equality.
*
* Returns: %TRUE if the arrays are equal, %FALSE otherwise.
**/
gboolean
_nm_utils_strv_equal (char **strv1, char **strv2)
{
if (strv1 == strv2)
return TRUE;
if (!strv1 || !strv2)
return FALSE;
for ( ; *strv1 && *strv2 && !strcmp (*strv1, *strv2); strv1++, strv2++)
;
return !*strv1 && !*strv2;
}
static gboolean
device_supports_ap_ciphers (guint32 dev_caps,
guint32 ap_flags,
@ -5430,11 +5406,11 @@ _json_team_add_defaults (json_t *json,
if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) {
_json_add_object (json, "notify_peers", "count", NULL,
json_integer (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT));
json_integer (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT));
_json_add_object (json, "mcast_rejoin", "count", NULL,
json_integer (NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT));
json_integer (NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT));
} else if ( nm_streq (runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE)
|| nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) {
|| nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) {
json_element = json_array ();
json_array_append_new (json_element, json_string ("eth"));
json_array_append_new (json_element, json_string ("ipv4"));
@ -5922,11 +5898,9 @@ _nm_utils_team_config_get (const char *conf,
if (json_is_string (str_element))
g_ptr_array_add (data, g_strdup (json_string_value (str_element)));
}
if (data->len) {
g_value_init (value, G_TYPE_STRV);
g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (data));
}
g_ptr_array_free (data, TRUE);
g_ptr_array_add (data, NULL);
g_value_init (value, G_TYPE_STRV);
g_value_take_boxed (value, g_ptr_array_free (data, FALSE));
} else {
g_assert_not_reached ();
g_free (value);
@ -5948,14 +5922,13 @@ _nm_utils_team_config_set (char **conf,
const char *key3,
const GValue *value)
{
json_t *json, *json_element, *json_link, *json_value = NULL;
nm_auto_decref_json json_t *json = NULL;
nm_auto_decref_json json_t *json_value = NULL;
json_t *json_element;
json_t *json_link;
json_error_t jerror;
gboolean updated = FALSE;
char **strv;
GPtrArray *array;
const char *iter_key = key;
int i;
NMTeamLinkWatcher *watcher;
gs_free char *conf_new = NULL;
g_return_val_if_fail (key, FALSE);
@ -5966,14 +5939,12 @@ _nm_utils_team_config_set (char **conf,
if (!json)
return FALSE;
/* no new value? delete element */
if (!value) {
updated = _json_del_object (json, key, key2, key3);
if (!_json_del_object (json, key, key2, key3))
return FALSE;
goto done;
}
/* insert new value */
updated = TRUE;
if (G_VALUE_HOLDS_STRING (value))
json_value = json_string (g_value_get_string (value));
else if (G_VALUE_HOLDS_INT (value))
@ -5982,53 +5953,56 @@ _nm_utils_team_config_set (char **conf,
json_value = json_boolean (g_value_get_boolean (value));
else if (G_VALUE_HOLDS_BOXED (value)) {
if (nm_streq (key, "link_watch")) {
array = g_value_get_boxed (value);
if (!array || !array->len) {
updated = FALSE;
goto done;
}
gboolean has_array = FALSE;
GPtrArray *array;
guint i;
array = g_value_get_boxed (value);
if (!array || !array->len)
return FALSE;
/*
* json_value: will hold the final link_watcher json (array) object
* json_element: is the next link_watcher to append to json_value
* json_link: used to transit the json_value from a single link_watcher
* object to an array of link watcher objects
*/
json_value = NULL;
for (i = 0; i < array->len; i++) {
watcher = array->pdata[i];
json_element = _nm_utils_team_link_watcher_to_json (watcher);
if (!json_element)
json_t *el;
el = _nm_utils_team_link_watcher_to_json (array->pdata[i]);
if (!el)
continue;
/* if there is only one watcher, it is added as-is. If there
* are multiple watchers, they are added in an array. */
if (!json_value) {
json_value = json_element;
json_value = el;
continue;
}
if (!json_is_array (json_value)) {
json_link = json_value;
json_value = json_array ();
json_array_append_new (json_value, json_link);
if (!has_array) {
json_t *el_arr;
has_array = TRUE;
el_arr = json_array();
json_array_append_new (el_arr, json_value);
json_value = el_arr;
}
json_array_append_new (json_value, json_element);
json_array_append_new (json_value, el);
}
} else if ( nm_streq (key, "runner")
&& nm_streq0 (key2, "tx_hash")) {
const char *const*strv;
gsize i;
strv = g_value_get_boxed (value);
if (!strv) {
updated = FALSE;
goto done;
}
if (!strv)
return FALSE;
json_value = json_array ();
for (i = 0; strv[i]; i++)
json_array_append_new (json_value, json_string (strv[i]));
} else {
updated = FALSE;
goto done;
nm_assert_not_reached ();
return FALSE;
}
} else { /* G_VALUE_HOLDS_? */
g_assert_not_reached ();
updated = FALSE;
goto done;
nm_assert_not_reached ();
return FALSE;
}
/* Simplest case: first level key only */
@ -6054,22 +6028,19 @@ _nm_utils_team_config_set (char **conf,
iter_key = key3;
}
json_object_set_new (json_element, iter_key, json_value);
json_object_set_new (json_element, iter_key, g_steal_pointer (&json_value));
done:
if (updated) {
_json_team_normalize_defaults (json, ( nm_streq0 (key, "runner")
&& nm_streq0 (key2, "name")));
g_free (*conf);
*conf = json_dumps (json, JSON_PRESERVE_ORDER);
/* Don't save an empty config */
if (nm_streq0 (*conf, "{}")) {
g_free (*conf);
*conf = NULL;
}
}
json_decref (json);
return updated;
_json_team_normalize_defaults (json, ( nm_streq0 (key, "runner")
&& nm_streq0 (key2, "name")));
conf_new = json_dumps (json, JSON_PRESERVE_ORDER);
if (nm_streq0 (conf_new, "{}"))
nm_clear_g_free (&conf_new);
if (nm_streq0 (conf_new, *conf))
return FALSE;
g_free (*conf);
*conf = g_steal_pointer (&conf_new);
return TRUE;
}
#else /* !WITH_JSON_VALIDATION */

View file

@ -2318,6 +2318,57 @@ _nm_utils_strv_sort (const char **strv, gssize len)
NULL);
}
/**
* _nm_utils_strv_cmp_n:
* @strv1: a string array
* @len1: the length of @strv1, or -1 for NULL terminated array.
* @strv2: a string array
* @len2: the length of @strv2, or -1 for NULL terminated array.
*
* Note that
* - len == -1 && strv == NULL
* is treated like a %NULL argument and compares differently from
* other arrays.
*
* Note that an empty array can be represented as
* - len == -1 && strv && !strv[0]
* - len == 0 && !strv
* - len == 0 && strv
* These 3 forms all compare equal.
* It also means, if length is 0, then it is permissible for strv to be %NULL.
*
* The strv arrays may contain %NULL strings (if len is positive).
*
* Returns: 0 if the arrays are equal (using strcmp).
**/
int
_nm_utils_strv_cmp_n (const char *const*strv1,
gssize len1,
const char *const*strv2,
gssize len2)
{
gsize n, n2;
if (len1 < 0) {
if (!strv1)
return (len2 < 0 && !strv2) ? 0 : -1;
n = NM_PTRARRAY_LEN (strv1);
} else
n = len1;
if (len2 < 0) {
if (!strv2)
return 1;
n2 = NM_PTRARRAY_LEN (strv2);
} else
n2 = len2;
NM_CMP_DIRECT (n, n2);
for (; n > 0; n--, strv1++, strv2++)
NM_CMP_DIRECT_STRCMP0 (*strv1, *strv2);
return 0;
}
/*****************************************************************************/
gpointer

View file

@ -941,6 +941,18 @@ gboolean nm_utils_hash_table_equal (const GHashTable *a,
void _nm_utils_strv_sort (const char **strv, gssize len);
#define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort (NM_CAST_STRV_MC (strv), len)
int _nm_utils_strv_cmp_n (const char *const*strv1,
gssize len1,
const char *const*strv2,
gssize len2);
static inline gboolean
_nm_utils_strv_equal (char **strv1, char **strv2)
{
return _nm_utils_strv_cmp_n ((const char *const*) strv1, -1,
(const char *const*) strv2, -1) == 0;
}
/*****************************************************************************/
#define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000)

View file

@ -1136,12 +1136,13 @@ nmtst_uuid_generate (void)
#endif
#define NMTST_SWAP(x,y) \
#define NMTST_SWAP(x, y) \
G_STMT_START { \
char __nmtst_swap_temp[sizeof(x) == sizeof(y) ? (signed) sizeof(x) : -1]; \
memcpy(__nmtst_swap_temp, &y, sizeof(x)); \
memcpy(&y, &x, sizeof(x)); \
memcpy(&x, __nmtst_swap_temp, sizeof(x)); \
char __nmtst_swap_temp[sizeof((x)) == sizeof((y)) ? (signed) sizeof((x)) : -1]; \
\
memcpy(__nmtst_swap_temp, &(y), sizeof (__nmtst_swap_temp)); \
memcpy(&(y), &(x), sizeof (__nmtst_swap_temp)); \
memcpy(&(x), __nmtst_swap_temp, sizeof (__nmtst_swap_temp)); \
} G_STMT_END
#define nmtst_assert_str_has_substr(str, substr) \

View file

@ -248,6 +248,150 @@ test_unaligned (void)
/*****************************************************************************/
static void
_strv_cmp_fuzz_input (const char *const*in,
gssize l,
const char ***out_strv_free_shallow,
char ***out_strv_free_deep,
const char *const* *out_s1,
const char *const* *out_s2)
{
const char **strv;
gsize i;
/* Fuzz the input argument. It will return two output arrays that are semantically
* equal the input. */
if (nmtst_get_rand_bool ()) {
char **ss;
if (l < 0)
ss = g_strdupv ((char **) in);
else if (l == 0) {
ss = nmtst_get_rand_bool ()
? NULL
: g_new0 (char *, 1);
} else {
ss = nm_memdup (in, sizeof (const char *) * l);
for (i = 0; i < (gsize) l; i++)
ss[i] = g_strdup (ss[i]);
}
strv = (const char **) ss;
*out_strv_free_deep = ss;
} else {
if (l < 0) {
strv = in
? nm_memdup (in, sizeof (const char *) * (NM_PTRARRAY_LEN (in) + 1))
: NULL;
} else if (l == 0) {
strv = nmtst_get_rand_bool ()
? NULL
: g_new0 (const char *, 1);
} else
strv = nm_memdup (in, sizeof (const char *) * l);
*out_strv_free_shallow = strv;
}
*out_s1 = in;
*out_s2 = strv;
if (nmtst_get_rand_bool ()) {
/* randomly swap the original and the clone. That means, out_s1 is either
* the input argument (as-is) or the sementically equal clone. */
NMTST_SWAP (*out_s1, *out_s2);
}
if (nmtst_get_rand_bool ()) {
/* randomly make s1 and s2 the same. This is for testing that
* comparing two identical pointers yields the same result. */
*out_s2 = *out_s1;
}
}
static void
_strv_cmp_free_deep (char **strv,
gssize len)
{
gssize i;
if (strv) {
if (len < 0)
g_strfreev (strv);
else {
for (i = 0; i < len; i++)
g_free (strv[i]);
g_free (strv);
}
}
}
static void
test_strv_cmp (void)
{
const char *const strv0[1] = { };
const char *const strv1[2] = { "", };
#define _STRV_CMP(a1, l1, a2, l2, equal) \
G_STMT_START { \
gssize _l1 = (l1); \
gssize _l2 = (l2); \
const char *const*_a1; \
const char *const*_a2; \
const char *const*_a1x; \
const char *const*_a2x; \
char **_a1_free_deep = NULL; \
char **_a2_free_deep = NULL; \
gs_free const char **_a1_free_shallow = NULL; \
gs_free const char **_a2_free_shallow = NULL; \
int _c1, _c2; \
\
_strv_cmp_fuzz_input ((a1), _l1, &_a1_free_shallow, &_a1_free_deep, &_a1, &_a1x); \
_strv_cmp_fuzz_input ((a2), _l2, &_a2_free_shallow, &_a2_free_deep, &_a2, &_a2x); \
\
_c1 = _nm_utils_strv_cmp_n (_a1, _l1, _a2, _l2); \
_c2 = _nm_utils_strv_cmp_n (_a2, _l2, _a1, _l1); \
if (equal) { \
g_assert_cmpint (_c1, ==, 0); \
g_assert_cmpint (_c2, ==, 0); \
} else { \
g_assert_cmpint (_c1, ==, -1); \
g_assert_cmpint (_c2, ==, 1); \
} \
\
/* Compare with self. _strv_cmp_fuzz_input() randomly swapped the arguments (_a1 and _a1x).
* Either way, the arrays must compare equal to their semantically equal alternative. */ \
g_assert_cmpint (_nm_utils_strv_cmp_n (_a1, _l1, _a1x, _l1), ==, 0); \
g_assert_cmpint (_nm_utils_strv_cmp_n (_a2, _l2, _a2x, _l2), ==, 0); \
\
_strv_cmp_free_deep (_a1_free_deep, _l1); \
_strv_cmp_free_deep (_a2_free_deep, _l2); \
} G_STMT_END
_STRV_CMP (NULL, -1, NULL, -1, TRUE);
_STRV_CMP (NULL, -1, NULL, 0, FALSE);
_STRV_CMP (NULL, -1, strv0, 0, FALSE);
_STRV_CMP (NULL, -1, strv0, -1, FALSE);
_STRV_CMP (NULL, 0, NULL, 0, TRUE);
_STRV_CMP (NULL, 0, strv0, 0, TRUE);
_STRV_CMP (NULL, 0, strv0, -1, TRUE);
_STRV_CMP (strv0, 0, strv0, 0, TRUE);
_STRV_CMP (strv0, 0, strv0, -1, TRUE);
_STRV_CMP (strv0, -1, strv0, -1, TRUE);
_STRV_CMP (NULL, 0, strv1, -1, FALSE);
_STRV_CMP (NULL, 0, strv1, 1, FALSE);
_STRV_CMP (strv0, 0, strv1, -1, FALSE);
_STRV_CMP (strv0, 0, strv1, 1, FALSE);
_STRV_CMP (strv0, -1, strv1, -1, FALSE);
_STRV_CMP (strv0, -1, strv1, 1, FALSE);
_STRV_CMP (strv1, -1, strv1, 1, TRUE);
_STRV_CMP (strv1, 1, strv1, 1, TRUE);
}
/*****************************************************************************/
NMTST_DEFINE ();
int main (int argc, char **argv)
@ -261,6 +405,7 @@ int main (int argc, char **argv)
g_test_add_func ("/general/test_nm_strndup_a", test_nm_strndup_a);
g_test_add_func ("/general/test_nm_ip4_addr_is_localhost", test_nm_ip4_addr_is_localhost);
g_test_add_func ("/general/test_unaligned", test_unaligned);
g_test_add_func ("/general/test_strv_cmp", test_strv_cmp);
return g_test_run ();
}