summary refs log tree commit diff
path: root/src/platform/tests/test-nmp-object.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-11-07 00:14:39 +0100
committerMichael Biebl <biebl@debian.org>2017-11-07 00:14:39 +0100
commit90e8691111889a7b5f3c812f5a41f15a8a058913 (patch)
treef101a879eca27c34a9bfa5f3da52266b22539a36 /src/platform/tests/test-nmp-object.c
parentbdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (diff)
New upstream version 1.9.90 upstream/1.9.90
Diffstat (limited to 'src/platform/tests/test-nmp-object.c')
-rw-r--r--src/platform/tests/test-nmp-object.c494
1 files changed, 282 insertions, 212 deletions
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index 42dfc572..a02388d2 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -33,6 +33,60 @@ struct {
 
 /*****************************************************************************/
 
+static void
+test_obj_base (void)
+{
+	static const union {
+		GObject g;
+		NMPObject k;
+	} x = { };
+	static const union {
+		GTypeClass k;
+		NMPClass c;
+	} l = { };
+	static const GObject *g = &x.g;
+	static const GTypeClass *k = &l.k;
+	static const NMPObject *o = &x.k;
+	static const NMPClass *c = &l.c;
+
+	NMObjBaseInst *obj;
+	gs_unref_object GCancellable *obj_cancellable = g_cancellable_new ();
+	nm_auto_nmpobj NMPObject *obj_link = nmp_object_new_link (10);
+
+#define STATIC_ASSERT(cond) \
+	G_STMT_START { \
+		G_STATIC_ASSERT (cond); \
+		G_STATIC_ASSERT_EXPR (cond); \
+		g_assert (cond); \
+	} G_STMT_END
+
+	STATIC_ASSERT (&g->g_type_instance              == (void *) &o->_class);
+	STATIC_ASSERT (&g->g_type_instance.g_class      == (void *) &o->_class);
+
+	STATIC_ASSERT (sizeof (o->parent.parent)        == sizeof (GTypeInstance));
+
+	STATIC_ASSERT (&c->parent                       == (void *) c);
+	STATIC_ASSERT (&c->parent.parent.g_type_class   == (void *) c);
+	STATIC_ASSERT (&c->parent.parent.g_type         == (void *) c);
+	STATIC_ASSERT (&c->parent.parent.g_type         == &k->g_type);
+
+	STATIC_ASSERT (sizeof (c->parent.parent)        == sizeof (GTypeClass));
+
+	STATIC_ASSERT (&o->parent                       == (void *) o);
+	STATIC_ASSERT (&o->parent.klass                 == (void *) &o->_class);
+
+	obj = (NMObjBaseInst *) obj_cancellable;
+	g_assert (!NMP_CLASS_IS_VALID ((NMPClass *) obj->klass));
+	g_assert (G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE));
+
+	obj = (NMObjBaseInst *) obj_link;
+	g_assert (NMP_CLASS_IS_VALID ((NMPClass *) obj->klass));
+	g_assert (!G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE));
+
+}
+
+/*****************************************************************************/
+
 static gboolean
 _nmp_object_id_equal (const NMPObject *a, const NMPObject *b)
 {
@@ -56,150 +110,144 @@ _nmp_object_equal (const NMPObject *a, const NMPObject *b)
 /*****************************************************************************/
 
 static void
-_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMPCacheId *cache_id, const NMPObject *obj, gboolean contains)
+_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMDedupMultiHeadEntry *head_entry, const NMPObject *obj, gboolean visible_only, gboolean contains)
 {
-	const NMPlatformObject *const *objects;
-	guint i, len;
+	NMDedupMultiIter iter;
 	gboolean found;
+	guint i, len;
+	const NMPObject *o;
 
-	g_assert (cache_id);
 	g_assert (NMP_OBJECT_IS_VALID (obj));
 
 	g_assert (nmp_cache_lookup_obj (cache, obj) == obj);
+	g_assert (!head_entry || (head_entry->len > 0 && c_list_length (&head_entry->lst_entries_head) == head_entry->len));
 
-	objects = nmp_cache_lookup_multi (cache, cache_id, &len);
-
-	g_assert ((len == 0 && !objects) || (len > 0 && objects && !objects[len]));
+	len = head_entry ? head_entry->len : 0;
 
 	found = FALSE;
-	for (i = 0; i < len; i++) {
-		NMPObject *o;
-
-		g_assert (objects[i]);
-		o = NMP_OBJECT_UP_CAST (objects[i]);
+	i = 0;
+	nmp_cache_iter_for_each (&iter,
+	                         head_entry,
+	                         &o) {
 		g_assert (NMP_OBJECT_IS_VALID (o));
-
 		if (obj == o) {
-			g_assert (!found);
-			found = TRUE;
+			if (   !visible_only
+			    || nmp_object_is_visible (o)) {
+				g_assert (!found);
+				found = TRUE;
+			}
 		}
+		i++;
 	}
 
+	g_assert (len == i);
 	g_assert (!!contains == found);
 }
 
-/*****************************************************************************/
+static void
+_assert_cache_multi_lookup_contains_link (const NMPCache *cache,
+                                          gboolean visible_only,
+                                          const NMPObject *obj,
+                                          gboolean contains)
+{
+	const NMDedupMultiHeadEntry *head_entry;
+	NMPLookup lookup;
 
-typedef struct {
-	NMPCache *cache;
-	NMPCacheOpsType expected_ops_type;
-	const NMPObject *obj_clone;
-	NMPObject *new_clone;
-	gboolean was_visible;
-	gboolean called;
-} _NMPCacheUpdateData;
+	g_assert (cache);
+
+	nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK);
+	head_entry = nmp_cache_lookup (cache, &lookup);
+	_assert_cache_multi_lookup_contains (cache, head_entry, obj, visible_only, contains);
+}
+
+/*****************************************************************************/
 
 static void
-_nmp_cache_update_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data)
+ops_post_check (NMPCache *cache,
+                NMPCacheOpsType ops_type,
+                const NMPObject *obj_old,
+                const NMPObject *obj_new,
+                const NMPObject *obj_new_expected,
+                NMPCacheOpsType expected_ops_type)
 {
-	_NMPCacheUpdateData *data = user_data;
-
-	g_assert (data);
-	g_assert (!data->called);
-	g_assert (data->cache == cache);
+	g_assert (cache);
 
-	g_assert_cmpint (data->expected_ops_type, ==, ops_type);
+	g_assert_cmpint (expected_ops_type, ==, ops_type);
 
 	switch (ops_type) {
 	case NMP_CACHE_OPS_ADDED:
-		g_assert (!old);
-		g_assert (NMP_OBJECT_IS_VALID (new));
-		g_assert (nmp_object_is_alive (new));
-		g_assert (nmp_object_id_equal (data->obj_clone, new));
-		g_assert (nmp_object_equal (data->obj_clone, new));
+		g_assert (!obj_old);
+		g_assert (NMP_OBJECT_IS_VALID (obj_new));
+		g_assert (nmp_object_is_alive (obj_new));
+		g_assert (nmp_object_id_equal (obj_new_expected, obj_new));
+		g_assert (nmp_object_equal (obj_new_expected, obj_new));
 		break;
 	case NMP_CACHE_OPS_UPDATED:
-		g_assert (NMP_OBJECT_IS_VALID (old));
-		g_assert (NMP_OBJECT_IS_VALID (new));
-		g_assert (nmp_object_is_alive (old));
-		g_assert (nmp_object_is_alive (new));
-		g_assert (nmp_object_id_equal (data->obj_clone, new));
-		g_assert (nmp_object_id_equal (data->obj_clone, old));
-		g_assert (nmp_object_id_equal (old, new));
-		g_assert (nmp_object_equal (data->obj_clone, new));
-		g_assert (!nmp_object_equal (data->obj_clone, old));
-		g_assert (!nmp_object_equal (old, new));
+		g_assert (obj_old != obj_new);
+		g_assert (NMP_OBJECT_IS_VALID (obj_old));
+		g_assert (NMP_OBJECT_IS_VALID (obj_new));
+		g_assert (nmp_object_is_alive (obj_old));
+		g_assert (nmp_object_is_alive (obj_new));
+		g_assert (nmp_object_id_equal (obj_new_expected, obj_new));
+		g_assert (nmp_object_id_equal (obj_new_expected, obj_old));
+		g_assert (nmp_object_id_equal (obj_old, obj_new));
+		g_assert (nmp_object_equal (obj_new_expected, obj_new));
+		g_assert (!nmp_object_equal (obj_new_expected, obj_old));
+		g_assert (!nmp_object_equal (obj_old, obj_new));
 		break;
 	case NMP_CACHE_OPS_REMOVED:
-		g_assert (!new);
-		g_assert (NMP_OBJECT_IS_VALID (old));
-		g_assert (nmp_object_is_alive (old));
-		g_assert (nmp_object_id_equal (data->obj_clone, old));
+		g_assert (!obj_new);
+		g_assert (NMP_OBJECT_IS_VALID (obj_old));
+		g_assert (nmp_object_is_alive (obj_old));
+		if (obj_new_expected)
+			g_assert (nmp_object_id_equal (obj_new_expected, obj_old));
+		break;
+	case NMP_CACHE_OPS_UNCHANGED:
+		g_assert (obj_old == obj_new);
+		if (obj_old) {
+			g_assert (NMP_OBJECT_IS_VALID (obj_old));
+			g_assert (nmp_object_is_alive (obj_old));
+			g_assert (nmp_object_equal (obj_old, obj_new));
+			g_assert (nmp_object_id_equal (obj_new_expected, obj_new));
+		} else
+			g_assert (!obj_new_expected);
 		break;
 	default:
 		g_assert_not_reached ();
 	}
-
-	data->was_visible = old ? nmp_object_is_visible (old) : FALSE;
-	data->new_clone = new ? nmp_object_clone (new, FALSE) : NULL;
-	data->called = TRUE;
 }
 
 static void
-_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCacheOpsType expected_ops_type)
+_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, const NMPObject **out_obj_old, const NMPObject **out_obj_new, NMPCacheOpsType expected_ops_type)
 {
 	NMPCacheOpsType ops_type;
-	NMPObject *obj2;
-	gboolean was_visible;
-	nm_auto_nmpobj NMPObject *obj_clone = nmp_object_clone (obj, FALSE);
-	nm_auto_nmpobj NMPObject *new_clone = NULL;
+	const NMPObject *obj_prev;
 	const NMPObject *obj_old;
-	_NMPCacheUpdateData data = {
-		.cache = cache,
-		.expected_ops_type = expected_ops_type,
-		.obj_clone = obj_clone,
-	};
-
-	obj_old = nmp_cache_lookup_link (cache, obj->object.ifindex);
-	if (obj_old && obj_old->_link.udev.device)
-		obj_clone->_link.udev.device = udev_device_ref (obj_old->_link.udev.device);
-	_nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache));
+	const NMPObject *obj_new;
+	nm_auto_nmpobj NMPObject *obj_new_expected = NULL;
 
 	g_assert (cache);
 	g_assert (NMP_OBJECT_IS_VALID (obj));
 
-	ops_type = nmp_cache_update_netlink (cache, obj, &obj2, &was_visible, _nmp_cache_update_hook, &data);
-
-	new_clone = data.new_clone;
-
-	g_assert_cmpint (ops_type, ==, expected_ops_type);
+	obj_prev = nmp_cache_lookup_link (cache, obj->object.ifindex);
+	obj_new_expected = nmp_object_clone (obj, FALSE);
+	if (obj_prev && obj_prev->_link.udev.device)
+		obj_new_expected->_link.udev.device = udev_device_ref (obj_prev->_link.udev.device);
+	_nmp_object_fixup_link_udev_fields (&obj_new_expected, NULL, nmp_cache_use_udev_get (cache));
 
-	if (ops_type != NMP_CACHE_OPS_UNCHANGED) {
-		g_assert (NMP_OBJECT_IS_VALID (obj2));
-		g_assert (data.called);
-		g_assert_cmpint (data.was_visible, ==, was_visible);
+	ops_type = nmp_cache_update_netlink (cache, obj, FALSE, &obj_old, &obj_new);
+	ops_post_check (cache, ops_type, obj_old, obj_new,
+	                nmp_object_is_alive (obj_new_expected) ? obj_new_expected : NULL,
+	                expected_ops_type);
 
-		if (ops_type == NMP_CACHE_OPS_REMOVED)
-			g_assert (!data.new_clone);
-		else {
-			g_assert (data.new_clone);
-			g_assert (nmp_object_equal (obj2, data.new_clone));
-		}
-	} else {
-		g_assert (!data.called);
-		g_assert (!obj2 || was_visible == nmp_object_is_visible (obj2));
-	}
-
-	g_assert (!obj2 || nmp_object_id_equal (obj, obj2));
-	if (ops_type != NMP_CACHE_OPS_REMOVED && obj2)
-		g_assert (nmp_object_equal (obj, obj2));
-
-	if (out_obj)
-		*out_obj = obj2;
+	if (out_obj_new)
+		*out_obj_new = obj_new;
 	else
-		nmp_object_unref (obj2);
-	if (out_was_visible)
-		*out_was_visible = was_visible;
+		nmp_object_unref (obj_new);
+	if (out_obj_old)
+		*out_obj_old = obj_old;
+	else
+		nmp_object_unref (obj_old);
 }
 
 static const NMPlatformLink pl_link_2 = {
@@ -218,168 +266,189 @@ static void
 test_cache_link (void)
 {
 	NMPCache *cache;
-	NMPObject *obj1, *obj2;
+	NMPObject *objm1;
+	const NMPObject *obj_old, *obj_new;
 	NMPObject objs1;
-	gboolean was_visible;
-	NMPCacheId cache_id_storage;
 	struct udev_device *udev_device_2 = g_list_nth_data (global.udev_devices, 0);
 	struct udev_device *udev_device_3 = g_list_nth_data (global.udev_devices, 0);
 	NMPCacheOpsType ops_type;
+	nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL;
+
+	multi_idx = nm_dedup_multi_index_new ();
 
-	cache = nmp_cache_new (nmtst_get_rand_int () % 2);
+	cache = nmp_cache_new (multi_idx, nmtst_get_rand_int () % 2);
 
 	/* if we have a link, and don't set is_in_netlink, adding it has no effect. */
-	obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
-	g_assert (NMP_OBJECT_UP_CAST (&obj1->object) == obj1);
-	g_assert (!nmp_object_is_alive (obj1));
-	_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED);
+	objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
+	g_assert (NMP_OBJECT_UP_CAST (&objm1->object) == objm1);
+	g_assert (!nmp_object_is_alive (objm1));
+	_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED);
 	ASSERT_nmp_cache_is_consistent (cache);
-	g_assert (!obj2);
-	g_assert (!was_visible);
-	g_assert (!nmp_cache_lookup_obj (cache, obj1));
+	g_assert (!obj_old);
+	g_assert (!obj_new);
+	g_assert (!nmp_cache_lookup_obj (cache, objm1));
 	g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)));
-	nmp_object_unref (obj1);
+	nmp_object_unref (objm1);
 
 	/* Only when setting @is_in_netlink the link is added. */
-	obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
-	obj1->_link.netlink.is_in_netlink = TRUE;
-	g_assert (nmp_object_is_alive (obj1));
-	_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_ADDED);
+	objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
+	objm1->_link.netlink.is_in_netlink = TRUE;
+	g_assert (nmp_object_is_alive (objm1));
+	_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_ADDED);
 	ASSERT_nmp_cache_is_consistent (cache);
-	g_assert (nmp_object_equal (obj1, obj2));
-	g_assert (!was_visible);
-	g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
-	g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
-	g_assert (nmp_object_is_visible (obj2));
-	_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE);
-	_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
-	nmp_object_unref (obj1);
-	nmp_object_unref (obj2);
+	g_assert (!obj_old);
+	g_assert (obj_new);
+	g_assert (objm1 == obj_new);
+	g_assert (nmp_object_equal (objm1, obj_new));
+	g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
+	g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
+	g_assert (nmp_object_is_visible (obj_new));
+	_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
+	_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE);
+	nmp_object_unref (objm1);
+	nmp_object_unref (obj_new);
 
 	/* updating the same link with identical value, has no effect. */
-	obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
-	obj1->_link.netlink.is_in_netlink = TRUE;
-	g_assert (nmp_object_is_alive (obj1));
-	_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED);
+	objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
+	objm1->_link.netlink.is_in_netlink = TRUE;
+	g_assert (nmp_object_is_alive (objm1));
+	_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED);
 	ASSERT_nmp_cache_is_consistent (cache);
-	g_assert (obj2 != obj1);
-	g_assert (nmp_object_equal (obj1, obj2));
-	g_assert (was_visible);
-	g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
-	g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
-	nmp_object_unref (obj1);
-	nmp_object_unref (obj2);
+	g_assert (obj_old);
+	g_assert (obj_new);
+	g_assert (obj_new != objm1);
+	g_assert (nmp_object_equal (objm1, obj_new));
+	g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
+	g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
+	nmp_object_unref (objm1);
+	nmp_object_unref (obj_new);
+	nmp_object_unref (obj_new);
 
 	/* remove the link from netlink */
-	obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
-	g_assert (!nmp_object_is_alive (obj1));
-	_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_REMOVED);
+	objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
+	g_assert (!nmp_object_is_alive (objm1));
+	_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_REMOVED);
 	ASSERT_nmp_cache_is_consistent (cache);
-	g_assert (obj2 != obj1);
-	g_assert (was_visible);
-	g_assert (!nmp_cache_lookup_obj (cache, obj1));
+	g_assert (obj_old);
+	g_assert (!obj_new);
+	g_assert (!nmp_cache_lookup_obj (cache, objm1));
 	g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)));
-	nmp_object_unref (obj1);
-	nmp_object_unref (obj2);
+	nmp_object_unref (objm1);
+	nmp_object_unref (obj_old);
+	nmp_object_unref (obj_new);
 
 	if (udev_device_2) {
 		/* now add the link only with aspect UDEV. */
-		ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj2, &was_visible, NULL, NULL);
+		ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj_old, &obj_new);
 		ASSERT_nmp_cache_is_consistent (cache);
 		g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED);
-		g_assert (!was_visible);
-		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
-		g_assert (!nmp_object_is_visible (obj2));
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE);
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
-		nmp_object_unref (obj2);
+		g_assert (!obj_old);
+		g_assert (obj_new);
+		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
+		g_assert (!nmp_object_is_visible (obj_new));
+		_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE);
+		_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
+		nmp_object_unref (obj_new);
 	}
 
 	/* add it in netlink too. */
-	obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
-	obj1->_link.netlink.is_in_netlink = TRUE;
-	g_assert (nmp_object_is_alive (obj1));
-	_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED);
+	objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
+	objm1->_link.netlink.is_in_netlink = TRUE;
+	g_assert (nmp_object_is_alive (objm1));
+	_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED);
 	ASSERT_nmp_cache_is_consistent (cache);
-	g_assert (nmp_object_equal (obj1, obj2));
-	g_assert (!was_visible);
-	g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
-	g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
-	g_assert (nmp_object_is_visible (obj2));
-	_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE);
-	_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
-	nmp_object_unref (obj1);
-	nmp_object_unref (obj2);
+	if (udev_device_2) {
+		g_assert (obj_old);
+		g_assert (!nmp_object_is_visible (obj_old));
+	} else
+		g_assert (!obj_old);
+	g_assert (nmp_object_equal (objm1, obj_new));
+	g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
+	g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
+	g_assert (nmp_object_is_visible (obj_new));
+	_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE);
+	_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
+	nmp_object_unref (objm1);
+	nmp_object_unref (obj_old);
+	nmp_object_unref (obj_new);
 
 	/* remove again from netlink. */
-	obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
-	obj1->_link.netlink.is_in_netlink = FALSE;
-	g_assert (!nmp_object_is_alive (obj1));
-	_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED);
+	objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2);
+	objm1->_link.netlink.is_in_netlink = FALSE;
+	g_assert (!nmp_object_is_alive (objm1));
+	_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED);
 	ASSERT_nmp_cache_is_consistent (cache);
-	g_assert (obj2 != obj1);
-	g_assert (was_visible);
+	if (udev_device_2)
+		g_assert (obj_new == objm1);
+	else
+		g_assert (!obj_new);
+	g_assert (obj_old);
+	g_assert (nmp_object_is_alive (obj_old));
 	if (udev_device_2) {
-		g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
-		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2);
-		g_assert (!nmp_object_is_visible (obj2));
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE);
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
+		g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
+		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj_new);
+		g_assert (!nmp_object_is_visible (obj_new));
+		_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE);
+		_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
 	} else {
-		g_assert (nmp_cache_lookup_obj (cache, obj1) == NULL);
+		g_assert (nmp_cache_lookup_obj (cache, objm1) == NULL);
 		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == NULL);
-		g_assert (nmp_object_is_visible (obj2));
+		g_assert (nmp_object_is_visible (obj_new));
 	}
-	nmp_object_unref (obj1);
-	nmp_object_unref (obj2);
+	nmp_object_unref (objm1);
+	nmp_object_unref (obj_old);
+	nmp_object_unref (obj_new);
 
 	/* now another link only with aspect UDEV. */
 	if (udev_device_3) {
 		/* now add the link only with aspect UDEV. */
-		ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj2, &was_visible, NULL, NULL);
+		ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj_old, &obj_new);
 		g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED);
 		ASSERT_nmp_cache_is_consistent (cache);
-		g_assert (NMP_OBJECT_IS_VALID (obj2));
-		g_assert (!was_visible);
-		g_assert (!nmp_object_is_visible (obj2));
-		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE);
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
-		g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE);
-		g_assert_cmpint (obj2->link.initialized, ==, FALSE);
-		nmp_object_unref (obj2);
+		g_assert (NMP_OBJECT_IS_VALID (obj_new));
+		g_assert (!obj_old);
+		g_assert (!nmp_object_is_visible (obj_new));
+		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new);
+		_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, FALSE);
+		_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
+		g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, FALSE);
+		g_assert_cmpint (obj_new->link.initialized, ==, FALSE);
+		nmp_object_unref (obj_new);
 
 		/* add it in netlink too. */
-		obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3);
-		obj1->_link.netlink.is_in_netlink = TRUE;
-		g_assert (nmp_object_is_alive (obj1));
-		_nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UPDATED);
+		objm1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3);
+		objm1->_link.netlink.is_in_netlink = TRUE;
+		g_assert (nmp_object_is_alive (objm1));
+		_nmp_cache_update_netlink (cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UPDATED);
 		ASSERT_nmp_cache_is_consistent (cache);
-		g_assert (obj2 != obj1);
-		g_assert (nmp_object_equal (obj1, obj2));
-		g_assert (!was_visible);
-		g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2);
-		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
-		g_assert (nmp_object_is_visible (obj2));
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE);
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
-		g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE);
-		g_assert_cmpint (obj2->link.initialized, ==, TRUE);
-		nmp_object_unref (obj1);
-		nmp_object_unref (obj2);
+		g_assert (obj_old);
+		g_assert (obj_new == objm1);
+		g_assert (nmp_object_equal (objm1, obj_new));
+		g_assert (!obj_old || !nmp_object_is_visible (obj_old));
+		g_assert (nmp_cache_lookup_obj (cache, objm1) == obj_new);
+		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new);
+		g_assert (nmp_object_is_visible (obj_new));
+		_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE);
+		_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
+		g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE);
+		g_assert_cmpint (obj_new->link.initialized, ==, TRUE);
+		nmp_object_unref (objm1);
+		nmp_object_unref (obj_old);
+		nmp_object_unref (obj_new);
 
 		/* remove UDEV. */
-		ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj2, &was_visible, NULL, NULL);
+		ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj_old, &obj_new);
 		g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_UPDATED);
 		ASSERT_nmp_cache_is_consistent (cache);
-		g_assert (was_visible);
-		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2);
-		g_assert (nmp_object_is_visible (obj2));
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE);
-		_assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE);
-		g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE);
-		g_assert_cmpint (obj2->link.initialized, ==, !nmp_cache_use_udev_get (cache));
-		nmp_object_unref (obj2);
+		g_assert (obj_old && nmp_object_is_visible (obj_old));
+		g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj_new);
+		g_assert (nmp_object_is_visible (obj_new));
+		_assert_cache_multi_lookup_contains_link (cache, TRUE, obj_new, TRUE);
+		_assert_cache_multi_lookup_contains_link (cache, FALSE, obj_new, TRUE);
+		g_assert_cmpint (obj_new->_link.netlink.is_in_netlink, ==, TRUE);
+		g_assert_cmpint (obj_new->link.initialized, ==, !nmp_cache_use_udev_get (cache));
+		nmp_object_unref (obj_new);
+		nmp_object_unref (obj_old);
 	}
 
 	nmp_cache_free (cache);
@@ -429,6 +498,7 @@ main (int argc, char **argv)
 		udev_enumerate_unref (enumerator);
 	}
 
+	g_test_add_func ("/nmp-object/obj-base", test_obj_base);
 	g_test_add_func ("/nmp-object/cache_link", test_cache_link);
 
 	result = g_test_run ();