test: add tests for destroy callback of asynchronous netlink call
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 3 Oct 2018 18:42:29 +0000 (03:42 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 10 Oct 2018 05:43:05 +0000 (14:43 +0900)
src/libsystemd/sd-netlink/test-netlink.c

index b068687..710ed0d 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "sd-netlink.h"
 
+#include "alloc-util.h"
 #include "ether-addr-util.h"
 #include "macro.h"
 #include "missing.h"
@@ -177,8 +178,9 @@ static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata)
 
         assert_se(rtnl);
         assert_se(m);
+        assert_se(userdata);
 
-        log_info("got link info about %s", ifname);
+        log_info("%s: got link info about %s", __func__, ifname);
         free(ifname);
 
         assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0);
@@ -212,40 +214,135 @@ static void test_event_loop(int ifindex) {
         assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
 }
 
-static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
-        int *counter = userdata;
-        int r;
+static void test_async(int ifindex) {
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
+        uint32_t serial;
+        char *ifname;
 
-        (*counter)--;
+        ifname = strdup("lo");
+        assert_se(ifname);
 
-        r = sd_netlink_message_get_errno(m);
+        assert_se(sd_netlink_open(&rtnl) >= 0);
 
-        log_info_errno(r, "%d left in pipe. got reply: %m", *counter);
+        assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
 
-        assert_se(r >= 0);
+        assert_se(sd_netlink_call_async(rtnl, m, link_handler, NULL, ifname, 0, &serial) >= 0);
+
+        assert_se(sd_netlink_wait(rtnl, 0) >= 0);
+        assert_se(sd_netlink_process(rtnl, &r) >= 0);
+
+        assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
+}
+
+struct test_async_object {
+        unsigned n_ref;
+        char *ifname;
+};
+
+static struct test_async_object *test_async_object_free(struct test_async_object *t) {
+        assert(t);
+
+        free(t->ifname);
+        return mfree(t);
+}
+
+DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(struct test_async_object, test_async_object, test_async_object_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct test_async_object *, test_async_object_unref);
+
+static int link_handler2(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+        struct test_async_object *t = userdata;
+        const char *data;
+
+        assert_se(rtnl);
+        assert_se(m);
+        assert_se(userdata);
+
+        log_info("%s: got link info about %s", __func__, t->ifname);
+
+        assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0);
+        assert_se(streq(data, "lo"));
 
         return 1;
 }
 
-static void test_async(int ifindex) {
+static void test_async_object_destroy(void *userdata) {
+        struct test_async_object *t = userdata;
+
+        assert(userdata);
+
+        log_info("%s: n_ref=%u", __func__, t->n_ref);
+        test_async_object_unref(t);
+}
+
+static void test_async_destroy_callback(int ifindex) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *r = NULL;
+        _cleanup_(test_async_object_unrefp) struct test_async_object *t = NULL;
         uint32_t serial;
         char *ifname;
 
-        ifname = strdup("lo");
-        assert_se(ifname);
+        assert_se(t = new(struct test_async_object, 1));
+        assert_se(ifname = strdup("lo"));
+        *t = (struct test_async_object) {
+                .n_ref = 1,
+                .ifname = ifname,
+        };
 
         assert_se(sd_netlink_open(&rtnl) >= 0);
 
+        /* destroy callback is called after processing message */
         assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
+        assert_se(sd_netlink_call_async(rtnl, m, link_handler2, test_async_object_destroy, t, 0, &serial) >= 0);
 
-        assert_se(sd_netlink_call_async(rtnl, m, link_handler, NULL, ifname, 0, &serial) >= 0);
+        assert_se(t->n_ref == 1);
+        assert_se(test_async_object_ref(t));
+        assert_se(t->n_ref == 2);
 
         assert_se(sd_netlink_wait(rtnl, 0) >= 0);
-        assert_se(sd_netlink_process(rtnl, &r) >= 0);
+        assert_se(sd_netlink_process(rtnl, &r) == 1);
+        assert_se(t->n_ref == 1);
+
+        assert_se(!sd_netlink_message_unref(m));
+
+        /* destroy callback is called when asynchronous call is cancelled */
+        assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
+        assert_se(sd_netlink_call_async(rtnl, m, link_handler2, test_async_object_destroy, t, 0, &serial) >= 0);
+
+        assert_se(t->n_ref == 1);
+        assert_se(test_async_object_ref(t));
+        assert_se(t->n_ref == 2);
+
+        assert_se(sd_netlink_call_async_cancel(rtnl, serial) >= 0);
+        assert_se(t->n_ref == 1);
+
+        assert_se(!sd_netlink_message_unref(m));
+
+        /* destroy callback is also called by sd_netlink_unref() */
+        assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
+        assert_se(sd_netlink_call_async(rtnl, m, link_handler2, test_async_object_destroy, t, 0, &serial) >= 0);
+
+        assert_se(t->n_ref == 1);
+        assert_se(test_async_object_ref(t));
+        assert_se(t->n_ref == 2);
 
         assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
+        assert_se(t->n_ref == 1);
+}
+
+static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+        int *counter = userdata;
+        int r;
+
+        (*counter)--;
+
+        r = sd_netlink_message_get_errno(m);
+
+        log_info_errno(r, "%d left in pipe. got reply: %m", *counter);
+
+        assert_se(r >= 0);
+
+        return 1;
 }
 
 static void test_pipe(int ifindex) {
@@ -366,31 +463,25 @@ int main(void) {
         uint16_t type;
 
         test_match();
-
         test_multiple();
 
         assert_se(sd_netlink_open(&rtnl) >= 0);
         assert_se(rtnl);
 
         test_route(rtnl);
-
         test_message(rtnl);
-
         test_container(rtnl);
 
         if_loopback = (int) if_nametoindex("lo");
         assert_se(if_loopback > 0);
 
         test_async(if_loopback);
-
+        test_async_destroy_callback(if_loopback);
         test_pipe(if_loopback);
-
         test_event_loop(if_loopback);
-
         test_link_configure(rtnl, if_loopback);
 
         test_get_addresses(rtnl);
-
         test_message_link_bridge(rtnl);
 
         assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, if_loopback) >= 0);